-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add 13.6.1 files, almost carbon copy
- Loading branch information
Showing
112 changed files
with
10,687 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from .capture import LayerCapture | ||
from .health import LayerHealthBase as LayerHealth | ||
from .markers import LayerMarkersBase as LayerMarkers | ||
from .ribbon import LayerRibbon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
from typing import Optional | ||
from PIL import Image, ImageDraw | ||
from renderer.base import LayerBase | ||
from renderer.render import Renderer | ||
from renderer.const import COLORS_NORMAL, RELATION_NORMAL_STR | ||
from renderer.utils import replace_color | ||
from renderer.data import ReplayData | ||
from functools import lru_cache | ||
|
||
|
||
class LayerCapture(LayerBase): | ||
"""A class for handling/drawing capture points. | ||
Args: | ||
LayerBase (_type_): _description_ | ||
""" | ||
|
||
def __init__( | ||
self, renderer: Renderer, replay_data: Optional[ReplayData] = None | ||
): | ||
"""Initiates this class. | ||
Args: | ||
renderer (Renderer): The renderer. | ||
""" | ||
self._renderer = renderer | ||
self._replay_data = ( | ||
replay_data if replay_data else self._renderer.replay_data | ||
) | ||
self._owner = self._replay_data.player_info[self._replay_data.owner_id] | ||
self._generated_caps: dict[ | ||
int, tuple[Image.Image, tuple[int, int], int] | ||
] = {} | ||
|
||
def draw(self, game_time: int, image: Image.Image): | ||
"""Draws the capture area on the minimap image. | ||
Args: | ||
game_time (int): Game time. Used to sync. events. | ||
image (Image.Image): Image where the capture are will be pasted on. | ||
""" | ||
events = self._replay_data.events | ||
cps = events[game_time].evt_control.values() | ||
|
||
for count, cp in enumerate(cps): | ||
if not cp.is_visible: | ||
continue | ||
|
||
if count in self._generated_caps: | ||
cap_image, cap_pos, cap_hash = self._generated_caps[count] | ||
if hash(cp) == cap_hash: | ||
image.alpha_composite(cap_image, cap_pos) | ||
continue | ||
|
||
x, y = self._renderer.get_scaled(cp.position) | ||
radius = self._renderer.get_scaled_r(cp.radius) | ||
w = h = round(radius * 2) | ||
cp_area = self._get_capture_area(cp.relation, (w, h)) | ||
|
||
if cp.control_point_type == 5: | ||
icon_name = "flag.png" | ||
else: | ||
icon_name = f"lettered_{count}.png" | ||
|
||
relation_str = RELATION_NORMAL_STR[cp.relation] | ||
icon = self._renderer.resman.load_image( | ||
icon_name, path=f"cap_icons.{relation_str}" | ||
) | ||
|
||
if cp.has_invaders and cp.invader_team != -1: | ||
if cp.invader_team == self._owner.team_id: | ||
from_color = COLORS_NORMAL[cp.relation] | ||
to_color = COLORS_NORMAL[0] | ||
else: | ||
from_color = COLORS_NORMAL[cp.relation] | ||
to_color = COLORS_NORMAL[1] | ||
progress = self._get_progress( | ||
from_color, to_color, cp.progress | ||
) | ||
else: | ||
normal = self._renderer.resman.load_image("cap_normal.png") | ||
from_color = "#000000" | ||
to_color = COLORS_NORMAL[cp.relation] | ||
progress = replace_color(normal, from_color, to_color) | ||
|
||
px = round(cp_area.width / 2 - progress.width / 2) + 1 | ||
py = round(cp_area.height / 2 - progress.height / 2) + 1 | ||
|
||
cp_area.alpha_composite(progress, (px, py)) | ||
|
||
ix = round(cp_area.width / 2 - icon.width / 2) + 1 | ||
iy = round(cp_area.height / 2 - icon.height / 2) + 1 | ||
|
||
cp_area.alpha_composite(icon, (ix, iy)) | ||
|
||
cx = round(x - cp_area.width / 2) | ||
cy = round(y - cp_area.height / 2) | ||
|
||
self._generated_caps[count] = ( | ||
cp_area.copy(), | ||
(cx, cy), | ||
hash(cp) | ||
) | ||
|
||
image.alpha_composite(cp_area, (cx, cy)) | ||
|
||
def _get_capture_area(self, relation: int, size: tuple) -> Image.Image: | ||
"""Loads the proper capture area image from the resources. | ||
Args: | ||
relation (int): relation | ||
size (tuple): size of the image. | ||
Returns: | ||
Image.Image: Image of the capture area, resized. | ||
""" | ||
relation_to_str = {-1: "neutral", 0: "ally", 1: "enemy"} | ||
filename = f"cap_{relation_to_str[relation]}.png" | ||
return self._renderer.resman.load_image(filename, size=size) | ||
|
||
@lru_cache | ||
def _get_progress(self, from_color: str, to_color: str, percent: float): | ||
"""Gets the diamond progress `bar` from the resources and properly | ||
color it depending from the colors and percentage provided. | ||
Args: | ||
from_color (str): From color. | ||
to_color (str): To color. | ||
percent (float): Percentage of the progress. 0.0 to 1.0 | ||
Returns: | ||
Image.Image: Diamond progress `bar` image. | ||
""" | ||
pd = self._renderer.resman.load_image("cap_invaded.png") | ||
|
||
bg_diamond = replace_color(pd, "#000000", from_color) | ||
fg_diamond = replace_color(pd, "#000000", to_color) | ||
mask = Image.new("RGBA", pd.size) | ||
mask_draw = ImageDraw.Draw(mask, "RGBA") | ||
mask_draw.pieslice( | ||
( | ||
(0, 0), | ||
(pd.width - 1, pd.height - 1), | ||
), | ||
start=-90, | ||
end=(-90 + 360 * percent), | ||
fill="black", | ||
) | ||
bg_diamond.paste(fg_diamond, mask=mask) | ||
return bg_diamond |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
import numpy as np | ||
|
||
from typing import Optional | ||
from renderer.data import ReplayData | ||
from renderer.render import Renderer | ||
from renderer.base import LayerBase | ||
from PIL import Image, ImageDraw, ImageColor | ||
from math import floor | ||
|
||
from renderer.layers.counter import X_POS as COUNTERS_POS | ||
|
||
|
||
CENTER = (800 + COUNTERS_POS) // 2 | ||
SKILLS_ORDER = [ | ||
"AirCarrier", | ||
"Battleship", | ||
"Cruiser", | ||
"Destroyer", | ||
"Auxiliary", | ||
"Submarine", | ||
] | ||
FIRE_PREVENTION_ID = 14 | ||
BURN_NODE_BITS = 4 | ||
BURN_NODE_POSITIONS = [ | ||
[None, None, None, None], | ||
[(0.5, 0.52), None, None, None], # subs | ||
[(0.125, 0.52), None, None, (0.875, 0.52)], # only auxiliary ships? | ||
[(0.125, 0.52), (0.5, 0.52), None, (0.875, 0.52)], # battleships w/ FP | ||
[ | ||
(0.125, 0.52), | ||
(0.375, 0.52), | ||
(0.625, 0.52), | ||
(0.875, 0.52), | ||
], # everything else | ||
] | ||
FLOOD_NODE_BITS = 2 | ||
FLOOD_NODE_POSITIONS = [ | ||
[None, None], | ||
[(0.5, 0.86), None], # subs | ||
[(0.252, 0.86), (0.748, 0.86)], # everything else | ||
] | ||
|
||
|
||
class LayerHealthBase(LayerBase): | ||
"""The class for handling player's ship health bar. | ||
Args: | ||
LayerBase (_type_): _description_ | ||
""" | ||
|
||
def __init__( | ||
self, renderer: Renderer, replay_data: Optional[ReplayData] = None | ||
): | ||
self._renderer = renderer | ||
self._replay_data = ( | ||
replay_data if replay_data else self._renderer.replay_data | ||
) | ||
self._ships = renderer.resman.load_json("ships.json") | ||
self._player = self._replay_data.player_info[ | ||
self._replay_data.owner_id | ||
] | ||
self._font = self._renderer.resman.load_font( | ||
filename="warhelios_bold.ttf", size=16 | ||
) | ||
self._green = ImageColor.getrgb("#4ce8aaff") | ||
self._yellow = ImageColor.getrgb("#ffc400ff") | ||
self._red = ImageColor.getrgb("#fe4d2aff") | ||
self._color_gray = ImageColor.getrgb("#ffffffc3") | ||
self._abilities = renderer.resman.load_json("abilities.json") | ||
|
||
self._burn_icon = self._renderer.resman.load_image( | ||
"fire.png", path="status_icons" | ||
) | ||
self._flood_icon = self._renderer.resman.load_image( | ||
"flooding.png", path="status_icons" | ||
) | ||
|
||
def _add_padding(self, bar: Image.Image): | ||
padded = Image.new("RGBA", (bar.width, bar.height + 4), (0, 0, 0, 0)) | ||
padded.alpha_composite(bar, (0, 0)) | ||
return padded | ||
|
||
def draw(self, game_time: int, image: Image.Image): | ||
"""Draws the health bar into the image. | ||
Args: | ||
game_time (int): The game time. | ||
image (Image.Image): The minimap image. | ||
""" | ||
ships = self._replay_data.events[game_time].evt_vehicle | ||
ship = ships[self._player.ship_id] | ||
ability = self._abilities[self._player.ship_params_id] | ||
per = ship.health / self._player.max_health | ||
info = self._ships[self._player.ship_params_id] | ||
|
||
suffix_fg = "_h" | ||
suffix_bg = "_h_bg" if ship.is_alive else "_h_bgdead" | ||
|
||
bg_bar = self._renderer.resman.load_image( | ||
f"{info['index']}{suffix_bg}.png", nearest=False, path="ship_bars" | ||
) | ||
bg_bar = self._add_padding(bg_bar) | ||
|
||
fg_bar = self._renderer.resman.load_image( | ||
f"{info['index']}{suffix_fg}.png", nearest=False, path="ship_bars" | ||
) | ||
fg_bar = self._add_padding(fg_bar) | ||
fg_bar = fg_bar.resize(bg_bar.size, Image.Resampling.LANCZOS) | ||
|
||
if per > 0.8: | ||
bar_color = self._green | ||
elif 0.8 >= per > 0.3: | ||
bar_color = self._yellow | ||
else: | ||
bar_color = self._red | ||
|
||
if ship.is_alive: | ||
alpha = 75 | ||
hp_bar_arr = np.array(fg_bar) | ||
hp_bar_arr[hp_bar_arr[:, :, 3] > alpha] = bar_color | ||
hp_bar_img = Image.fromarray(hp_bar_arr) | ||
mask_hp_img = Image.new(fg_bar.mode, fg_bar.size) | ||
mask_hp_img_w = mask_hp_img.width * per | ||
mask_hp_draw = ImageDraw.Draw(mask_hp_img) | ||
mask_hp_draw.rectangle( | ||
((0, 0), (mask_hp_img_w, mask_hp_img.width)), fill="black" | ||
) | ||
|
||
if regen := ship.consumables_state.get(8, None): | ||
_, count, _, _ = regen | ||
if count: | ||
subtype = ability["id_to_subtype"][8] | ||
index = ability["id_to_index"][8] | ||
name = f"{index}.{subtype}" | ||
wt = ability[name]["workTime"] | ||
rhs = ability[name]["regenerationHPSpeed"] | ||
maxHeal = floor(wt) * rhs * self._player.max_health | ||
canHeal = ( | ||
ship.regeneration_health | ||
if ship.regeneration_health < maxHeal | ||
else maxHeal | ||
) | ||
|
||
per_limit = ( | ||
canHeal + ship.health | ||
) / self._player.max_health | ||
|
||
regen_bar_arr = np.array(fg_bar) | ||
regen_bar_arr[ | ||
regen_bar_arr[:, :, 3] > alpha | ||
] = self._color_gray | ||
regen_bar_img = Image.fromarray(regen_bar_arr) | ||
mask_regen_img = Image.new(fg_bar.mode, fg_bar.size) | ||
mask_regen_img_w = mask_regen_img.width * per_limit | ||
mask_regen_draw = ImageDraw.Draw(mask_regen_img) | ||
mask_regen_draw.rectangle( | ||
((0, 0), (mask_regen_img_w, mask_regen_img.width)), | ||
fill="black", | ||
) | ||
|
||
bg_bar.paste(regen_bar_img, mask=mask_regen_img) | ||
bg_bar.paste(hp_bar_img, mask=mask_hp_img) | ||
|
||
hp_current = "{:,}".format(round(ship.health)).replace(",", " ") | ||
hp_max = "{:,}".format(round(self._player.max_health)).replace( | ||
",", " " | ||
) | ||
hp_max_text = f"/{hp_max}" | ||
|
||
hp_c_w, hp_c_h = self._font.getbbox(hp_current)[2:] | ||
hp_w, hp_h = self._font.getbbox(hp_max_text)[2:] | ||
n_w, n_h = self._font.getbbox(info["name"])[2:] | ||
|
||
bg_bar = bg_bar.resize((235, 62), resample=Image.Resampling.LANCZOS) | ||
|
||
px = CENTER - round(bg_bar.width / 2) | ||
|
||
th = Image.new("RGBA", (bg_bar.width, max(hp_h, n_h, hp_c_h))) | ||
th_draw = ImageDraw.Draw(th) | ||
|
||
th_draw.text((0, 0), info["name"], fill="white", font=self._font) | ||
th_draw.text( | ||
(th.width - (hp_w + hp_c_w), 0), | ||
hp_current, | ||
fill=bar_color, | ||
font=self._font, | ||
) | ||
th_draw.text( | ||
(th.width - hp_w, 0), | ||
hp_max_text, | ||
fill="#cdcdcd", | ||
font=self._font, | ||
) | ||
|
||
if (flags := bin(ship.burn_flags)[2:][::-1]) != "0": | ||
burn_nodes, flood_nodes = info["hulls"][self._player.hull] | ||
active_skills = self._player.skills.by_species(info["species"]) | ||
if FIRE_PREVENTION_ID in active_skills: | ||
burn_nodes -= 1 | ||
|
||
self._draw_nodes( | ||
bg_bar, | ||
self._burn_icon, | ||
flags[0:BURN_NODE_BITS], | ||
BURN_NODE_POSITIONS[burn_nodes], | ||
) | ||
total_bits = BURN_NODE_BITS + FLOOD_NODE_BITS | ||
self._draw_nodes( | ||
bg_bar, | ||
self._flood_icon, | ||
flags[BURN_NODE_BITS:total_bits], | ||
FLOOD_NODE_POSITIONS[flood_nodes], | ||
) | ||
|
||
image.paste(th, (px, 205), th) | ||
image.paste(bg_bar, (px, 145), bg_bar) | ||
|
||
def _draw_nodes( | ||
self, | ||
image: Image.Image, | ||
icon: Image.Image, | ||
flags: str, | ||
positions: list, | ||
): | ||
"""Draws the status nodes into the health bar. | ||
Args: | ||
image (Image.Image): Base image. | ||
icon (Image.Image): Status icon. | ||
flags (str): ? | ||
positions (list): The position of the node. | ||
""" | ||
for index, bit in enumerate(flags): | ||
if bit == "1": | ||
x_per, y_per = positions[index] | ||
image.alpha_composite( | ||
icon, | ||
( | ||
round(image.width * x_per - icon.width / 2), | ||
round(image.height * y_per - icon.height / 2), | ||
), | ||
) |
Oops, something went wrong.