import math
import pygame
from typing import Tuple, Union, Optional, List, cast, Callable
import miniworlds.appearances.appearance as appearance
import miniworlds.appearances.background as background_mod
import miniworlds.appearances.backgrounds_manager as backgrounds_manager
import miniworlds.base.app as app
import miniworlds.worlds.world_base as world_base
import miniworlds.worlds.manager.collision_manager as coll_manager
import miniworlds.worlds.manager.event_manager as event_manager
import miniworlds.worlds.manager.mouse_manager as mouse_manager
import miniworlds.worlds.manager.music_manager as world_music_manager
import miniworlds.worlds.manager.sound_manager as world_sound_manager
import miniworlds.worlds.manager.position_manager as position_manager
import miniworlds.worlds.manager.camera_manager as world_camera_manager
import miniworlds.worlds.manager.world_connector as world_connector
import miniworlds.worlds.data.export_factory as export_factory
import miniworlds.worlds.data.import_factory as import_factory
import miniworlds.base.dialogs as ask
import miniworlds.positions.rect as world_rect
import miniworlds.actors.actor as actor_mod
import miniworlds.tools.world_inspection as world_inspection
import miniworlds.tools.color as color
import miniworlds.tools.timer as timer
import miniworlds.base.app as app_mod
from miniworlds.base.exceptions import (
WorldArgumentsError,
)
[docs]
class World(world_base.WorldBase):
"""A world is a playing field on which actors can move.
A world has a `background` and provides basic functions for the positioning of
actors and for the collision detection of actors, which can be queried via the sensors of the actors.
You can create your own world by creating a class that inherits from World or you can directly create a world
object of type `World` or one of its child classes (`TiledWorld`, `PhysicsWorld`, ...).
*World*
A world for pixel accurate games.
* The position of a actor on a World is the pixel at topleft of actor.
* New actors are created with top-left corner of actor rect at position.
* Two actors collide when their sprites overlap.
.. image:: ../_images/asteroids.jpg
:alt: Asteroids
**Other worlds:**
* TiledWorld: For worlds using Tiles, like rogue-like rpgs, see
:doc:`TiledWorld <../api/world.tiledworld>`)
* PhysicsWorld: For worlds using the PhysicsEngine, see
:doc:`PhysicsWorld <../api/world_physicsworld>`)
Examples:
Creating a TiledWorld Object:
.. code-block:: python
from miniworlds import *
my_world = TiledWorld()
my_world.columns = 30
my_world.rows = 20
my_world.tile_size = 20
Creating a TiledWorld-Subclass.
.. code-block:: python
import miniworlds
class MyWorld(miniworlds.TiledWorld):
def on_setup(self):
self.columns = 30
self.rows = 20
self.tile_size = 20
Creating a World Object:
.. code-block:: python
from miniworlds import *
my_world = World()
my_world.columns = 300
my_world.rows = 200
Creating a World Subclass
.. code-block:: python
import miniworlds
class MyWorld(miniworlds.World):
def on_setup(self):
self.columns = 300
self.rows = 200
See also:
* See: :doc:`World <../api/world>`
* See: :doc:`TiledWorld <../api/world.tiledworld>`
Args:
view_x: columns of new world (default: 40)
view_y: rows of new world (default:40)
tile_size: Size of tiles (1 for normal worlds, can differ for Tiledworlds)
"""
subclasses = None
[docs]
def validate_parameters(self, x, y):
if not isinstance(x, Union[float, int]) or not isinstance(y, Union[float, int]):
raise TypeError(
f"World(x, y) x and y must be int or float; Got ({type(x)}, {type(y)})"
)
def __init__(
self,
x: Union[int, Tuple[int, int]] = 400,
y: int = 400,
):
self.validate_parameters(x, y)
self._was_setup = False
self.is_tiled = False
self._is_acting = True # Is act() Method called
self.camera = self._get_camera_manager_class()(x, y, self)
self.actors: "pygame.sprite.LayeredDirty" = pygame.sprite.LayeredDirty()
self.event_manager: event_manager.EventManager = self._create_event_manager()
super().__init__()
self.backgrounds_manager: "backgrounds_manager.BackgroundsManager" = (
backgrounds_manager.BackgroundsManager(self)
)
self.mouse_manager: "mouse_manager.MouseManager" = mouse_manager.MouseManager(
self
)
self.ask: "ask.Ask" = ask.Ask(self)
self.is_display_initialized: bool = False
self._fps: int = 60
self._key_pressed: bool = False
self._animated: bool = False
self._is_filled: bool = False
self._orientation: int = 0
self._static: bool = False
self._step: int = 1 # All actors are acting on n:th frame with n = self.step
self._default_is_filled = False
self._default_fill_color = None
self._default_border_color = None
self._default_border = None
self.is_running: bool = True
self.is_listening: bool = True
self.frame: int = 0
self.clock: pygame.time.Clock = pygame.time.Clock()
if not app.App.init:
app.App.init = True
self.app: "app.App" = app.App("miniworlds")
app.App.running_app = self.app
app.App.running_world = self
app.App.running_worlds.append(self)
else:
self.app = app.App.running_app
self.music: "world_music_manager.MusicManager" = (
world_music_manager.MusicManager(self.app)
)
self.sound: "world_sound_manager.SoundManager" = (
world_sound_manager.SoundManager(self.app)
)
self.background = background_mod.Background(self)
self.background.update()
self.collision_manager: "coll_manager.CollisionManager" = (
coll_manager.CollisionManager(self)
)
self.timed_objects: list = []
self.app.event_manager.to_event_queue("setup", None)
self.dynamic_actors: "pygame.sprite.Group" = pygame.sprite.Group()
self._registered_methods: List[Callable] = []
self.actors_fixed_size = False
self.app.worlds_manager.add_topleft(self)
self.reload_costumes_queue = []
[docs]
def add_right(self, world, size: int = 100):
new_world = world
new_world.camera.screen_topleft = (self.window.width, 0)
new_world.camera.height = self.window.height
new_world.camera.width = size
_container = self.app.worlds_manager.add_world(new_world, "right", size)
app_mod.App.running_worlds.append(new_world)
new_world.on_change()
new_world.on_setup()
return new_world
[docs]
def add_bottom(self, world: "World", size: int = 100):
new_world = world
new_world.camera.screen_topleft = (0, self.window.height,)
new_world.camera.width = self.window.width
new_world.camera.height = size
_container = self.app.worlds_manager.add_world(new_world, "bottom", size)
app_mod.App.running_worlds.append(new_world)
new_world.on_change()
new_world.on_setup()
return new_world
[docs]
def remove_world(self, container: "world_base.WorldBase"):
return self.app.worlds_manager.remove_world(container)
@staticmethod
def _get_camera_manager_class():
return world_camera_manager.CameraManager
@staticmethod
def _get_world_connector_class():
"""needed by get_world_connector in parent class"""
return world_connector.WorldConnector
[docs]
def get_world_connector(self, actor) -> world_connector.WorldConnector:
return self._get_world_connector_class()(self, actor)
def _create_event_manager(self):
return event_manager.EventManager(self)
[docs]
def detect_position(self, pos):
"""Checks if position is in the world.
Returns:
True, if Position is in the world.
"""
if 0 <= pos[0] < self.world_size_x and 0 <= pos[1] < self.world_size_y:
return True
else:
return False
[docs]
def contains_rect(self, rect: Union[tuple, pygame.Rect]):
"""Detects if rect is completely on the world.
Args:
rect: A rectangle as tuple (top, left, width, height)
"""
rectangle = world_rect.Rect.create(rect)
topleft_on_the_world = self.detect_position(rectangle.topleft)
bottom_right_on_the_world = self.detect_position(rectangle.bottomright)
return topleft_on_the_world or bottom_right_on_the_world
@property
def surface(self):
return self.background.surface
@property
def class_name(self) -> str:
return self.__class__.__name__
@property
def step(self) -> int:
"""Step defines how often the method ``act()`` will be called.
If e.g. ``step = 30``, the game logic will be called every 30th-frame.
.. note::
You can adjust the frame-rate with ``world.fps``
Examples:
Set speed and fps.
.. code-block:: python
from miniworlds import *
world = World()
world.size = (120,210)
@world.register
def on_setup(self):
world.fps = 1
world.speed = 3
@world.register
def act(self):
world.run()
Output:
```
3
6
9
12
15
```
"""
return self._step
@step.setter
def step(self, value: int):
self._step = value
@property
def fps(self) -> int:
"""
Frames per second shown on the screen.
This controls how often the screen is redrawn. However, the game logic
can be called more often or less often independently of this with ``world.speed.``
Examples:
.. code-block:: python
world.speed = 10
world.fps = 24
def act(self):
nonlocal i
i = i + 1
if world.frame == 120:
test_instance.assertEqual(i, 13)
test_instance.assertEqual(world.frame, 120)
"""
return self._fps
@fps.setter
def fps(self, value: int):
self._fps = value
@property
def world_size_x(self) -> int:
"""The x-world_size (defaults to view_size)"""
return self.camera.world_size_x
@world_size_x.setter
def world_size_x(self, value: int):
self.camera.world_size_x = value
@property
def world_size_y(self) -> int:
"""The y-world_size (defaults to view_size)"""
return self.camera.world_size_y
@world_size_y.setter
def world_size_y(self, value: int):
self.camera.world_size_y = value
@property
def columns(self) -> int:
return self.camera.width
@columns.setter
def columns(self, value: int):
self.set_columns(value)
[docs]
def set_columns(self, value: int):
self.camera.width = value
self.world_size_x = value
@property
def rows(self) -> int:
return self.camera.height
@rows.setter
def rows(self, value: int):
self.set_rows(value)
[docs]
def set_rows(self, value: int):
self.camera.height = value
self.world_size_y = value
[docs]
def borders(self, value: Union[tuple, pygame.Rect]) -> list:
"""Gets all borders from a source (`Position` or `Rect`).
Args:
value: Position or rect
Returns:
A list of borders, e.g. ["left", "top"], if rect is touching the left a top border.
"""
return []
@property
def size(self) -> tuple:
"""Set the size of world
Examples:
Create a world with 800 columns and 600 rows:
.. code-block:: python
world = miniworlds.PixelWorld()
world.size = (800, 600)
"""
return self.world_size_x, self.world_size_y
@size.setter
def size(self, value: tuple):
self.world_size_x = value[0]
self.world_size_y = value[1]
self.camera.width = value[0]
self.camera.height = value[1]
@property
def default_fill_color(self):
"""Set default fill color for borders and lines"""
return self._default_fill_color
@default_fill_color.setter
def default_fill_color(self, value):
self._default_fill_color = color.Color(value).get()
[docs]
def default_fill(self, value):
"""Set default fill color for borders and lines"""
self._is_filled = value
if self.default_is_filled is not None and self.default_is_filled:
self._default_fill_color = color.Color(value).get()
@property
def default_is_filled(self):
return self._default_is_filled
@default_is_filled.setter
def default_is_filled(self, value):
self.default_fill(value)
@property
def default_stroke_color(self):
"""Set default stroke color for borders and lines. (equivalent to border-color)"""
return self.default_border_color
@default_stroke_color.setter
def default_stroke_color(self, value):
"""Set default stroke color for borders and lines. (equivalent to border-color)"""
self.default_border_color = value
@property
def default_border_color(self):
"""Set default border color for borders and lines.
.. note::
``world.default_border_color`` does not have an effect, if no border is set.
You must also set ``world.border`` > 0.
Examples:
Create actors with and without with border
.. code-block:: python
from miniworlds import *
world = World(210,80)
world.default_border_color = (0,0, 255)
world.default_border = 1
t = Actor((10,10))
t2 = Actor ((60, 10))
t2.border_color = (0,255, 0)
t2.border = 5 # overwrites default border
t3 = Actor ((110, 10))
t3.border = None # removes border
t4 = Actor ((160, 10))
t4.add_costume("images/player.png") # border for sprite
world.run()
Output:
.. image:: ../_images/border_color.png
:width: 200px
:alt: borders
"""
return self._default_border_color
@default_border_color.setter
def default_border_color(self, value):
self._default_border_color = value
@property
def default_border(self):
"""Sets default border color for actors
.. note::
You must also set a border for actor.
Examples:
Set default border for actors:
.. code-block:: python
from miniworlds import *
world = World(210,80)
world.default_border_color = (0,0, 255)
world.default_border = 1
t = Actor((10,10))
world.run()
"""
return self._default_border
@default_border.setter
def default_border(self, value):
self._default_border = value
@property
def backgrounds(self) -> list:
"""Returns all backgrounds of the world as list."""
return self.backgrounds_manager.backgrounds
@property
def background(self) -> "background_mod.Background":
"""Returns the current background"""
return self.get_background()
@background.setter
def background(self, source):
if isinstance(source, appearance.Appearance):
self.backgrounds_manager.background = source
else:
self.backgrounds_manager.add_background(source)
[docs]
def get_background(self) -> "background_mod.Background":
"""Returns the current background"""
return self.backgrounds_manager.background
[docs]
def switch_background(
self, background: Union[int, "appearance.Appearance"]
) -> "background_mod.Background":
"""Switches the background
Args:
background: The index of the new background or an Appearance.
If index = -1, the next background will be selected
Examples:
Switch between different backgrounds:
.. code-block:: python
from miniworlds import *
world = World()
actor = Actor()
world.add_background("images/1.png")
world.add_background((255, 0, 0, 255))
world.add_background("images/2.png")
@timer(frames = 40)
def switch():
world.switch_background(0)
@timer(frames = 80)
def switch():
world.switch_background(1)
@timer(frames = 160)
def switch():
world.switch_background(2)
world.run()
Output:
.. image:: ../_images/switch_background.png
:width: 100%
:alt: Switch background
Returns:
The new background
"""
return cast(
background_mod.Background,
self.backgrounds_manager.switch_appearance(background),
)
[docs]
def remove_background(self, background=None):
"""Removes a background from world
Args:
background: The index of the new background. Defaults to -1 (last background) or an Appearance
"""
return self.backgrounds_manager.remove_appearance(background)
[docs]
def set_background(self, source: Union[str, tuple]) -> "background_mod.Background":
"""Adds a new background to the world
If multiple backgrounds are added, the last adds background will be set as active background.
Args:
source: The path to the first image of the background or a color (e.g. (255,0,0) for red or
"images/my_background.png" as path to a background.
Examples:
Add multiple Backgrounds:
.. code-block:: pythonlist
from miniworlds import *
world = World()
world.add_background((255, 0 ,0)) # red
world.add_background((0, 0 ,255)) # blue
world.run() # Shows a blue world.
Returns:
The new created background.
"""
return self.backgrounds_manager.set_background(source)
[docs]
def add_background(self, source: Union[str, tuple]) -> "background_mod.Background":
"""Adds a new background to the world
If multiple backgrounds are added, the last adds background will be set as active background.
Args:
source: The path to the first image of the background or a color (e.g. (255,0,0) for red or
"images/my_background.png" as path to a background.
Examples:
Add multiple Backgrounds:
.. code-block:: pythonlist
from miniworlds import *
world = World()
world.add_background((255, 0 ,0)) # red
world.add_background((0, 0 ,255)) # blue
world.run() # Shows a blue world.
Returns:
The new created background.
"""
return self.backgrounds_manager.add_background(source)
[docs]
def start(self):
"""Starts the world, if world is not running."""
self.is_running = True
[docs]
def stop(self, frames=0):
"""Stops the world.
Args:
frames (int, optional): If ``frames`` is set, world will be stopped in n frames. . Defaults to 0.
"""
if frames == 0:
self.is_running = False
else:
timer.ActionTimer(frames, self.stop, 0)
[docs]
def start_listening(self):
self.is_listening = True
[docs]
def stop_listening(self):
self.is_listening = False
[docs]
def run(
self,
fullscreen: bool = False,
fit_desktop: bool = False,
replit: bool = False,
event=None,
data=None,
):
"""
The method show() should always be called at the end of your program.
It starts the mainloop.
Examples:
A minimal miniworlds-program:
.. code-block:: python
from miniworlds import *
world = TiledWorld()
actor = Actor()
world.run()
Output:
.. image:: ../_images/min.png
:width: 200px
:alt: Minimal program
"""
self.app.prepare_mainloop()
if hasattr(self, "on_setup") and not self._was_setup:
self.on_setup()
self._was_setup = True
self.init_display()
self.is_running = True
if event:
self.app.event_manager.to_event_queue(event, data)
self.app.run(
self.image, fullscreen=fullscreen, fit_desktop=fit_desktop, replit=replit
)
[docs]
def init_display(self):
if not self.is_display_initialized:
self.is_display_initialized = True
self.background.set_dirty("all", self.background.LOAD_NEW_IMAGE)
[docs]
def play_sound(self, path: str):
"""plays sound from path"""
self.app.sound_manager.play_sound(path)
[docs]
def stop_sounds(self):
self.app.sound_manager.stop()
[docs]
def play_music(self, path: str):
"""plays a music from path
Args:
path: The path to the music
Returns:
"""
self.music.play(path)
[docs]
def stop_music(self):
"""stops a music
Returns:
"""
self.music.stop()
[docs]
def get_mouse_position(self) -> Optional[tuple]:
"""
Gets the current mouse_position
Returns:
Returns the mouse position if mouse is on the world. Returns None otherwise
Examples:
Create circles at current mouse position:
.. code-block:: python
from miniworlds import *
world = PixelWorld()
@world.register
def act(self):
c = Circle(world.get_mouse_position(), 40)
c.color = (255,255,255, 100)
c.border = None
world.run()
Output:
.. image:: ../_images/mousepos.png
:width: 200px
:alt: Circles at mouse-position
"""
return self.mouse_manager.mouse_position
[docs]
def get_mouse_x(self) -> int:
"""Gets x-coordinate of mouse-position"""
if self.mouse_manager.mouse_position:
return self.mouse_manager.mouse_position[0]
else:
return 0
[docs]
def get_mouse_y(self) -> int:
"""Gets y-coordinate of mouse-position"""
if self.mouse_manager.mouse_position:
return self.mouse_manager.mouse_position[1]
else:
return 0
[docs]
def get_prev_mouse_position(self):
"""gets mouse-position of last frame"""
return self.mouse_manager.prev_mouse_position
[docs]
def is_mouse_pressed(self) -> bool:
"""Returns True, if mouse is pressed"""
return (
self.mouse_manager.mouse_left_is_clicked()
or self.mouse_manager.mouse_left_is_clicked()
)
[docs]
def is_mouse_left_pressed(self) -> bool:
"""Returns True, if mouse left button is pressed"""
return self.mouse_manager.mouse_left_is_clicked()
[docs]
def is_mouse_right_pressed(self) -> bool:
"""Returns True, if mouse right button is pressed"""
return self.mouse_manager.mouse_right_is_clicked()
[docs]
def is_in_world(self, position: Tuple[float, float]) -> bool:
if (
position[0] > 0
and position[1] > 0
and position[0] < self.camera.world_size_x
and position[1] < self.camera.world_size_y
):
return True
return False
[docs]
def send_message(self, message, data=None):
"""Sends broadcast message
A message can be received by the world or any actor on world
"""
self.app.event_manager.to_event_queue("message", message)
[docs]
def quit(self, exit_code=0):
"""quits app and closes the window"""
self.app.quit(exit_code)
[docs]
def reset(self):
"""Resets the world
Creates a new world with init-function - recreates all actors and actors on the world.
Examples:
Restarts flappy the bird game after collision with pipe:
.. code-block:: python
def on_sensing_collision_with_pipe(self, other, info):
self.world.is_running = False
self.world.reset()
"""
self.clear()
# Re-Setup the world
if hasattr(self, "on_setup"):
self._was_setup = False
self.on_setup()
self._was_setup = True
[docs]
def clear(self):
self.app.event_manager.event_queue.clear()
for background in self.backgrounds:
self.backgrounds_manager.remove_appearance(background)
# Remove all actors
for actor in self.actors:
actor.remove()
[docs]
def switch_world(self, new_world: "World", reset: bool = False):
"""Switches to another world
Args:
new_world (World): _description_
"""
self.app.worlds_manager.switch_world(new_world, reset)
[docs]
def get_color_from_pixel(self, position: Tuple[float, float]) -> tuple:
"""
Returns the color at a specific position
Examples:
.. code-block:: python
from miniworlds import *
world = World((100,60))
@world.register
def on_setup(self):
self.add_background((255,0,0))
print(self.get_color_from_pixel((5,5)))
world.run()
Output: (255, 0, 0, 255)
.. image:: ../_images/get_color.png
:width: 100px
:alt: get color of red screen
Args:
position: The position to search for
Returns:
The color
"""
return self.app.window.surface.get_at((int(position[0]), int(position[1])))
[docs]
def get_from_pixel(self, position: Tuple) -> Optional[tuple]:
"""Gets Position from pixel
PixelWorld: the pixel position is returned
TiledWorld: the tile-position is returned
:param position: Position as pixel coordinates
:return: The pixel position, if position is on the world, None if position is not on World.
"""
column = position[0]
row = position[1]
position = (column, row)
if column < self.camera.width and row < self.camera.height:
return position
else:
return None
[docs]
def to_pixel(self, position):
x = position[0]
y = position[1]
return x, y
[docs]
def on_setup(self):
"""Overwrite or register this method to call `on_setup`-Actions"""
pass
#def __str__(self):
# return f"{self.__class__.__name__} with {self.columns} columns and {self.rows} rows"
@property
def has_background(self) -> bool:
return self.backgrounds_manager.has_appearance()
@property
def registered_events(self) -> set:
return self.event_manager.registered_events
@registered_events.setter
def registered_events(self, value):
return # setter is defined so that world_event_manager is not overwritten by world parent class container
def add_to_world(self, actor, position: tuple):
"""Adds a Actor to the world.
Is called in __init__-Method if position is set.
Args:
actor: The actor, which should be added to the world.
position: The position on the world where the actor should be added.
"""
self.get_world_connector(actor).add_to_world(position)
[docs]
def detect_actors(self, position: Tuple[float, float]) -> List["actor_mod.Actor"]:
"""Gets all actors which are found at a specific position.
Args:
position: Position, where actors should be searched.
Returns:
A list of actors
Examples:
Get all actors at mouse position:
.. code-block:: python
position = world.get_mouse_position()
actors = world.get_actors_by_pixel(position)
"""
# overwritten in tiled_sensor_manager
return cast(
List["actor_mod.Actor"],
[
actor
for actor in self.actors
if actor.sensor_manager.detect_point(position)
],
)
[docs]
def get_actors_from_pixel(self, pixel: Tuple[float, float]):
return cast(
List["actor_mod.Actor"],
[actor for actor in self.actors if actor.sensor_manager.detect_pixel(pixel)],
)
@property
def image(self) -> pygame.Surface:
"""The current displayed image"""
return self.backgrounds_manager.image
[docs]
def repaint(self):
self.background.repaint() # called 1/frame in container.repaint()
[docs]
def update(self):
"""The mainloop, called once per frame.
Called in app.update() when update() from all containers is called.
"""
if self.is_running or self.frame == 0:
# Acting for all actors@static
if self.frame > 0 and self.frame % self.step == 0:
self._act_all()
self.collision_manager.handle_all_collisions()
self.mouse_manager.update_positions()
if self.frame == 0:
self.init_display()
# run animations
self.background.update()
# update all costumes on current background
self._update_all_costumes()
# @TODO: Update costumes for animated costumes, performance
self._tick_timed_objects()
self.frame = self.frame + 1
self.clock.tick(self.fps)
self.event_manager.executed_events.clear()
def _update_all_costumes(self):
"""updates costumes for all actors on the world"""
[
actor.costume.update()
for actor in self.reload_costumes_queue
if actor.costume
]
self.reload_costumes_queue = []
# Dynamic actors are updated every frame
# All other actors are updated when they are created.
if hasattr(self, "dynamic_actors"):
[actor.costume.update() for actor in self.dynamic_actors if actor.costume]
def _act_all(self):
"""Overwritten in subclasses, e.g. physics_world"""
self.event_manager.act_all()
def _tick_timed_objects(self):
[obj.tick() for obj in self.timed_objects]
def handle_event(self, event, data=None):
"""
Event handling
Args:
event (str): The event which was thrown, e.g. "key_up", "act", "reset", ...
data: The data of the event (e.g. ["S","s"], (155,3), ...
"""
self.event_manager.handle_event(event, data)
[docs]
def register(self, method: Callable) -> Callable:
"""
Used as decorator
e.g.
@register
def method...
"""
self._registered_methods.append(method)
bound_method = world_inspection.WorldInspection(self).bind_method(method)
self.event_manager.register_event(method.__name__, self)
return bound_method
[docs]
def unregister(self, method: Callable):
self._registered_methods.remove(method)
world_inspection.WorldInspection(self).unbind_method(method)
@property
def fill_color(self):
return self.background.fill_color
@fill_color.setter
def fill_color(self, value):
self.background.fill(value)
# Alias
color = fill_color
[docs]
def direction(self, point1, point2):
pass
[docs]
@staticmethod
def distance_to(pos1: Tuple[float, float], pos2: Tuple[float, float]):
return math.sqrt((pos1[0] - pos2[0]) ** 2 + (pos1[1] - pos2[1]) ** 2)
[docs]
def direction_to(
self, pos1: Tuple[float, float], pos2: Tuple[float, float]
) -> float:
return position_manager.Positionmanager.direction_from_two_points(pos1, pos2)
@property
def window(self) -> "app_mod.App":
"""
Gets the parent window
Returns:
The window
"""
return self._window
[docs]
def load_world_from_db(self, file: str):
"""
Loads a sqlite db file.
"""
return import_factory.ImportWorldFromDB(file, self.__class__).load()
[docs]
def load_actors_from_db(
self, file: str, actor_classes: list
) -> List["actor_mod.Actor"]:
"""Loads all actors from db. Usually you load the actors in __init__() or in on_setup()
Args:
file (str): reference to db file
actor_classes (list): a list of all Actor Classes which should be imported.
Returns:
[type]: All Actors
"""
return import_factory.ImportActorsFromDB(file, actor_classes).load()
[docs]
def save_to_db(self, file):
"""
Saves the current world an all actors to database.
The file is stored as db file and can be opened with sqlite.
Args:
file: The file as relative location
Returns:
"""
export = export_factory.ExportWorldToDBFactory(file, self)
export.remove_file()
export.save()
export_factory.ExportActorsToDBFactory(file, self.actors).save()
[docs]
def screenshot(self, filename: str = "screenshot.jpg"):
"""Creates a screenshot in given file.
Args:
filename: The location of the file. The folder must exist. Defaults to "screenshot.jpg".
"""
pygame.image.save(self.app.window.surface, filename)
[docs]
def get_columns_by_width(self, width):
return width
[docs]
def get_rows_by_height(self, height):
return height
[docs]
def get_events(self):
"""Gets a set of all events you can register"""
print(self.event_manager.class_events_set)