from __future__ import annotations
import logging
import math
from typing import TYPE_CHECKING, TypeVar
import pygame
from nqp.core.constants import IMG_FORMATS, SceneType
if TYPE_CHECKING:
from typing import Dict, List, Optional, Tuple
_V = TypeVar("_V", int, float) # to represent where we don't know which type is being used
__all__ = [
"swap_colour",
"clip",
"offset",
"lerp",
"clamp",
"itr",
"scene_to_scene_type",
"next_number_in_loop",
"grid_up",
"grid_right",
"grid_down",
"grid_left",
"distance_to",
"angle_to",
"get_direction",
"percent_to_float",
]
[docs]def swap_colour(img, old_c, new_c):
img.set_colorkey(old_c)
surface = img.copy()
surface.fill(new_c)
surface.blit(img, (0, 0))
surface.set_colorkey((0, 0, 0))
return surface
[docs]def clip(surface, x, y, x_size, y_size) -> pygame.Surface:
"""
Clip a subsurface from a surface.
"""
handle_surf = surface.copy()
clip_r = pygame.Rect(x, y, x_size, y_size)
handle_surf.set_clip(clip_r)
image = surface.subsurface(handle_surf.get_clip())
return image.copy()
[docs]def offset(list_: List, offset_, offset_mult=1):
for i, val in enumerate(list_):
list_[i] += offset_[i] * offset_mult
return list_
[docs]def lerp(initial_value: float, target_value: float, lerp_fraction: float) -> float:
"""
Linear interpolation between initial and target by amount. Fraction clamped between 0 and 1. >=0.99 is treated
as 1 to handle float imprecision.
"""
clamped_lerp_fraction = clamp(lerp_fraction, 0, 1)
if clamped_lerp_fraction >= 0.99:
return target_value
else:
return initial_value * (1 - clamped_lerp_fraction) + target_value * clamped_lerp_fraction
[docs]def clamp(value: _V, min_value: _V, max_value: _V) -> _V:
"""
Return the value, clamped between min and max.
"""
return max(min_value, min(value, max_value))
[docs]def itr(iterable):
"""
An iteration tool for easy removal.
"""
return sorted(enumerate(iterable), reverse=True)
[docs]def scene_to_scene_type(scene) -> SceneType:
"""
Take a Scene and return the relevant SceneType
"""
from nqp.scenes.gallery.scene import GalleryScene
from nqp.scenes.main_menu.scene import MainMenuScene
from nqp.scenes.run_setup.scene import RunSetupScene
from nqp.scenes.unit_data.scene import UnitDataScene
from nqp.scenes.view_troupe.scene import ViewTroupeScene
if type(scene) is ViewTroupeScene:
scene = SceneType.VIEW_TROUPE
elif type(scene) is RunSetupScene:
scene = SceneType.RUN_SETUP
elif type(scene) is UnitDataScene:
scene = SceneType.DEV_DATA_EDITOR
elif type(scene) is MainMenuScene:
scene = SceneType.MAIN_MENU
elif type(scene) is GalleryScene:
scene = SceneType.DEV_GALLERY
else:
logging.error(f"scene_to_scene_type: Scene ({scene}) not found.")
return scene
[docs]def next_number_in_loop(start: int, loop_size: int) -> int:
if start + 1 >= loop_size:
result = 0
else:
result = start + 1
return result
[docs]def previous_number_in_loop(start: int, loop_size: int) -> int:
"""
If at max loop size, returns loop size -1.
"""
if start - 1 < 0:
result = loop_size - 1
else:
result = start - 1
return result
[docs]def grid_up(selected: int, width: int, height: int):
return selected - width
[docs]def grid_down(selected: int, width: int, height: int):
index = selected + width
length = width * height
if index >= length:
index -= length
return index
[docs]def grid_left(selected: int, width: int, height: int):
if selected % width == 0:
index = selected + width - 1
else:
index = selected - 1
return index
[docs]def grid_right(selected: int, width: int, height: int):
index = selected + 1
length = width * height
if index % width == 0 or index >= length:
index -= width
return index
[docs]def distance_to(start_pos: pygame.Vector2, end_pos: pygame.Vector2) -> float:
"""
Find the distance to another position.
"""
return math.sqrt((start_pos[0] - end_pos[0]) ** 2 + (start_pos[1] - end_pos[1]) ** 2)
[docs]def angle_to(start_pos: pygame.Vector2, end_pos: pygame.Vector2) -> float:
"""
Find the angle to another position.
"""
return math.atan2(end_pos[1] - start_pos[1], end_pos[0] - start_pos[0])
[docs]def get_direction(angle: float, move_distance: float) -> pygame.Vector2:
"""
Find the direction based on the angle and distance
"""
return pygame.Vector2(math.cos(angle) * move_distance, math.sin(angle) * move_distance)
[docs]def percent_to_float(percent_string: str):
"""
Convert string representation of percent into a float
"""
return float(percent_string.strip("%")) / 100.0