Dynamic Island это та самая пилюля сверху экрана, как на айфоне. Клиент показывает в ней короткие статусы: играет музыка, пришло уведомление, что-то происходит. Твой скрипт может добавить туда свой статус, он встанет в общую очередь и бесплатно получит все анимации: перетекание, раскрытие, появление.
Остров на экране один, а статусов внутри может быть много. Клиент сам решает, какой показать прямо сейчас, и плавно перетекает между ними. Твоё дело описать один статус: какого он размера, что в нём нарисовано и когда он вообще уместен.
| Слово | Что значит |
|---|---|
статус | Один экранчик острова: свой размер, свой рисунок, своё условие показа |
compact | Обычное, свёрнутое состояние, маленькая пилюля |
extended | Раскрытое: остров вырастает и показывает подробности |
expanding | Насколько раскрытие уже произошло: 0 свёрнут, 1 раскрыт полностью. Всё между это анимация |
Статус создают один раз в теле скрипта глобальным конструктором IslandStatus(...), ровно как Module(...) у модулей. Сам island остаётся менеджером: через него ищут и убирают уже созданные статусы (island.find, island.all, island.remove). При выгрузке скрипта статус снимается сам. Но есть важная деталь: новый статус создаётся выключенным. Он появляется в списке статусов острова, но не показывается, пока его не включат: из кода через st.selected(True) или руками в настройках HUD.
st = IslandStatus("Ping", width=48, height=15, radius=7)
st.selected(True) # без этой строки статус не появится на экране
@st.render
def draw(d):
d.centered_text("42 ms", d.w / 2, 4, size=7, color=Color(255, 255, 255))Новый статус встаёт в начало списка, над встроенными. Это важно: остров показывает один статус за раз, самый верхний из подходящих, поэтому снизу статус скрипта почти всегда был бы перекрыт. Порядок игрок меняет перетаскиванием в настройках HUD, и его выбор запоминается: после перезагрузки скрипта статус вернётся туда, куда его положили.
| Параметр IslandStatus | По умолчанию | Что это |
|---|---|---|
name | обязателен | Имя статуса, по нему его видно в настройках острова |
width / height | 48 / 15 | Размер в свёрнутом виде |
radius | 7 | Скругление пилюли |
expandable | False | Можно ли раскрыть статус кликом |
render | None | Функция рисования, то же, что декоратор @st.render |
measure | None | Функция, которая задаёт размер перед кадром |
click | None | Что делать по клику |
color | None | Цвет пилюли: сам цвет или функция, которая его вернёт |
visible_when | None | Условие показа статуса |
Дальше статусом управляют методами. Все возвращают сам статус, так что их можно писать цепочкой:
| Метод / свойство | Что делает |
|---|---|
st.selected(True) | Включить статус. Выключенный статус не показывается вообще |
st.size(w, h[, radius]) | Задать размер и, если надо, скругление |
st.radius(r) | Только скругление пилюли |
st.expandable(True) | Разрешить раскрывать статус кликом |
st.color(fn или Color) | Цвет пилюли. Дашь функцию, клиент дёрнет её каждый кадр |
st.visible_when(fn) | Показывать, только когда fn вернула True |
st.remove() | Убрать статус совсем |
st.x / st.y / st.w / st.h | Где статус сейчас и какого он размера |
st.alpha | Текущая прозрачность (клиент плавно показывает и прячет статусы) |
st.extended / st.expanding | Раскрыт ли (True/False) и насколько раскрыт (0..1) |
Найти и убрать: island.find(name), island.all(), island.mine(), island.remove(name).
Кисточка у статуса та же, что у HUD-элемента: координаты относительные (0, 0 это левый верхний угол пилюли), есть d.w, d.h, d.alpha и методы d.text, d.centered_text, d.icon, d.rounded_rect и остальные. Фон пилюли клиент рисует сам, твоя забота только содержимое.
Размер часто зависит от того, что внутри: длинный ник, пилюля шире. Для этого есть measure, функция, где ты пересчитываешь размер до отрисовки. А клик ловится в click(fn): туда приходят координаты мыши и номер кнопки (0 левая, 1 правая).
ACCENT = Color(86, 124, 252)
TEXT = Color(255, 255, 255)
st = IslandStatus("Target", width=48, height=15, radius=7)
st.selected(True)
def _target_name():
t = world.target() # кого бьёт Aura
return None if t is None else str(t.getNameForScoreboard())
# показываем статус, только когда цель реально есть
st.visible_when(lambda: _target_name() is not None)
# ширина пилюли равна ширине ника плюс место под иконку и поля
def measure():
name = _target_name() or ""
st.size(client.font_width("semibold", 7, name) + 34, 15)
st.measure(measure)
# цвет отдаём функцией, клиент спросит её на каждом кадре
st.color(lambda: ACCENT)
@st.render
def draw(d):
name = _target_name() or ""
d.icon("hud/player", 8, 3.5, size=8, color=TEXT)
d.text(name, 20, 4, size=7, weight="semibold", color=TEXT)
st.click(lambda mx, my, btn: client.msg("Цель: " + (_target_name() or "нет")))Если статус создан с expandable=True, первый клик по острову не уйдёт в click, а раскроет статус: st.extended станет True, а st.expanding плавно поедет от 0 к 1. Когда статус уже раскрыт, клики внутри пилюли приходят в click, а клик мимо сворачивает остров.
Схема тут всегда одна. В measure выбираем размер по st.extended. А в рисовании домножаем прозрачность содержимого на st.expanding, тогда подробности проявляются точно в такт анимации и ничего не прыгает.
TEXT = Color(255, 255, 255)
DIM = Color(255, 255, 255, 160)
st = IslandStatus("Stats", width=52, height=15, radius=7, expandable=True)
st.selected(True)
st.visible_when(lambda: world.ingame())
def measure():
if st.extended:
st.size(150, 56, 16) # раскрыто: широкая карточка
else:
st.size(52, 15, 7) # свёрнуто: маленькая пилюля
st.measure(measure)
@st.render
def draw(d):
fps = int(mc.getCurrentFps())
k = st.expanding # 0 свёрнут, 1 раскрыт
# компактная надпись гаснет по мере раскрытия: альфа падает с 1 до 0
d.centered_text("%d fps" % fps, d.w / 2, 4, size=7,
color=TEXT.mulAlpha(1.0 - k))
if k <= 0.01:
return # ещё свёрнут, подробности не рисуем
# а подробности, наоборот, проявляются: альфа растёт с 0 до 1
p = world.self()
coords = "-" if p is None else "%d, %d, %d" % (int(p.getX()), int(p.getY()), int(p.getZ()))
players = len(world.players()) + 1
d.text("Stats", 12, 10, size=8, weight="semibold", color=TEXT.mulAlpha(k))
d.text("%d fps" % fps, 12, 24, size=7, color=DIM.mulAlpha(k))
d.text(coords, 12, 34, size=7, color=DIM.mulAlpha(k))
d.right_text("%d игроков" % players, d.w - 12, 24, size=7, color=DIM.mulAlpha(k))
st.click(lambda mx, my, btn: client.msg("клик внутри раскрытого острова"))