Skip to content

Instantly share code, notes, and snippets.

@Cheaterman
Last active April 13, 2022 00:18
Show Gist options
  • Save Cheaterman/4b54af8eb62ff94b79af3b8ecf8fc7f1 to your computer and use it in GitHub Desktop.
Save Cheaterman/4b54af8eb62ff94b79af3b8ecf8fc7f1 to your computer and use it in GitHub Desktop.
eselection.py
from dataclasses import dataclass, field
from typing import Callable, List
from samp import (
INVALID_TEXT_DRAW,
CancelSelectTextDraw,
CreatePlayerTextDraw,
GetTickCount,
IsPlayerConnected,
PlayerTextDrawAlignment,
PlayerTextDrawBackgroundColor,
PlayerTextDrawBoxColor,
PlayerTextDrawColor,
PlayerTextDrawDestroy,
PlayerTextDrawFont,
PlayerTextDrawHide,
PlayerTextDrawLetterSize,
PlayerTextDrawSetOutline,
PlayerTextDrawSetPreviewModel,
PlayerTextDrawSetPreviewRot,
PlayerTextDrawSetProportional,
PlayerTextDrawSetSelectable,
PlayerTextDrawSetShadow,
PlayerTextDrawSetString,
PlayerTextDrawShow,
PlayerTextDrawTextSize,
PlayerTextDrawUseBox,
SelectTextDraw,
TextDrawAlignment,
TextDrawBackgroundColor,
TextDrawBoxColor,
TextDrawColor,
TextDrawCreate,
TextDrawFont,
TextDrawHideForPlayer,
TextDrawLetterSize,
TextDrawSetOutline,
TextDrawSetProportional,
TextDrawSetSelectable,
TextDrawSetShadow,
TextDrawShowForPlayer,
TextDrawTextSize,
TextDrawUseBox,
)
ITEMS_PER_PAGE = 18
_static_graphics = None
_player_menus = {}
class StaticGraphics:
"""Contains static graphics elements (background, etc) textdraws."""
def __init__(self):
self.background = background = TextDrawCreate(
531.333374,
140.877777,
'_'
)
TextDrawBackgroundColor(background, 0)
TextDrawAlignment(background, 1)
TextDrawFont(background, 0)
TextDrawLetterSize(background, 0.000000, 22.912965)
TextDrawColor(background, 0)
TextDrawSetOutline(background, 0)
TextDrawSetProportional(background, 1)
TextDrawSetShadow(background, 0)
TextDrawUseBox(background, 1)
TextDrawBoxColor(background, 0x000000DD)
TextDrawTextSize(background, 121.333328, 0.000000)
TextDrawSetSelectable(background, 0)
self.right_arrow = right_arrow = TextDrawCreate(
521.333374,
339.318542,
'LD_BEAT:right'
)
TextDrawLetterSize(right_arrow, 0.000000, 0.000000)
TextDrawTextSize(right_arrow, 5.999938, 7.051818)
TextDrawAlignment(right_arrow, 1)
TextDrawColor(right_arrow, 0xC0C0C0FF)
TextDrawSetShadow(right_arrow, 0)
TextDrawSetOutline(right_arrow, 0)
TextDrawFont(right_arrow, 4)
TextDrawSetSelectable(right_arrow, True)
self.left_arrow = left_arrow = TextDrawCreate(
507.000305,
339.074066,
'LD_BEAT:left'
)
TextDrawLetterSize(left_arrow, 0.000000, 0.000000)
TextDrawTextSize(left_arrow, 5.999938, 7.051818)
TextDrawAlignment(left_arrow, 1)
TextDrawColor(left_arrow, 0xC0C0C0FF)
TextDrawSetShadow(left_arrow, 0)
TextDrawSetOutline(left_arrow, 0)
TextDrawFont(left_arrow, 4)
TextDrawSetSelectable(left_arrow, True)
self.top_banner = top_banner = TextDrawCreate(
531.000244,
155.811111,
'TopBanner'
)
TextDrawLetterSize(top_banner, 0.000000, -0.447120)
TextDrawTextSize(top_banner, 121.333328, 0.000000)
TextDrawAlignment(top_banner, 1)
TextDrawColor(top_banner, 0)
TextDrawUseBox(top_banner, True)
TextDrawBoxColor(top_banner, 0x808080FF)
TextDrawSetShadow(top_banner, 0)
TextDrawSetOutline(top_banner, 0)
TextDrawFont(top_banner, 0)
self.bottom_banner = bottom_banner = TextDrawCreate(
531.333618,
338.500305,
'BottomBanner'
)
TextDrawLetterSize(bottom_banner, 0.000000, -0.447120)
TextDrawTextSize(bottom_banner, 120.666656, 0.000000)
TextDrawAlignment(bottom_banner, 1)
TextDrawColor(bottom_banner, 0)
TextDrawUseBox(bottom_banner, True)
TextDrawBoxColor(bottom_banner, 0x808080FF)
TextDrawSetShadow(bottom_banner, 0)
TextDrawSetOutline(bottom_banner, 0)
TextDrawFont(bottom_banner, 0)
self.close_button = close_button = TextDrawCreate(
490.666809,
337.829711,
'CLOSE'
)
TextDrawLetterSize(close_button, 0.128333, 0.957036)
TextDrawTextSize(close_button, 10.5021, 10.0187)
TextDrawAlignment(close_button, 2)
TextDrawColor(close_button, 0xC0C0C0FF)
TextDrawSetShadow(close_button, 0)
TextDrawSetOutline(close_button, 0)
TextDrawBackgroundColor(close_button, 0x00000033)
TextDrawFont(close_button, 2)
TextDrawSetProportional(close_button, 1)
TextDrawSetSelectable(close_button, True)
def show(self, playerid):
TextDrawShowForPlayer(playerid, self.right_arrow)
TextDrawShowForPlayer(playerid, self.left_arrow)
TextDrawShowForPlayer(playerid, self.background)
TextDrawShowForPlayer(playerid, self.top_banner)
TextDrawShowForPlayer(playerid, self.bottom_banner)
TextDrawShowForPlayer(playerid, self.close_button)
def hide(self, playerid):
TextDrawHideForPlayer(playerid, self.right_arrow)
TextDrawHideForPlayer(playerid, self.left_arrow)
TextDrawHideForPlayer(playerid, self.background)
TextDrawHideForPlayer(playerid, self.top_banner)
TextDrawHideForPlayer(playerid, self.bottom_banner)
TextDrawHideForPlayer(playerid, self.close_button)
@dataclass
class MenuItem:
"""A menu item containing a 3D model and a text label."""
model_id: int
text: str = ''
rot_x: float = 0.
rot_y: float = 0.
rot_z: float = 0.
zoom: float = 1.
@dataclass
class Menu:
"""A menu that has a title and a list of menu items."""
title: str
items: List[MenuItem] = field(default_factory=list)
on_select: Callable[[int, MenuItem], None] = lambda playerid, item: None
on_cancel: Callable[[int], None] = lambda playerid: None
def add(self, item):
if item in self.items:
raise ValueError(
f'Adding the same menu item to the same menu twice: {item:r}'
)
self.items.append(item)
def remove(self, item):
if item not in self.items:
raise ValueError(
f'Removing menu item not present in menu: {item:r}'
)
self.items.remove(item)
def show(self, playerid):
global _player_menus
if not IsPlayerConnected(playerid):
return
if _player_menus.get(playerid):
_player_menus[playerid].hide()
_player_menus[playerid] = player_menu = PlayerMenu(self, playerid)
player_menu.show()
@dataclass
class PlayerMenu:
"""A menu that is being shown to a player. Handles paging, etc."""
menu: Menu
playerid: int
_show_time: int = 0
_current_page: int = 0
_total_pages: int = 0
_paging_text: int = INVALID_TEXT_DRAW
_header_text: int = INVALID_TEXT_DRAW
_item_models: List[int] = field(default_factory=list)
_item_texts: List[int] = field(default_factory=list)
def __post_init__(self):
# eg. 18 items in list, 18 items per page, should be one.
# Same for 17, but not for 19.
# This will also be zero if there's no items.
self._total_pages = (len(self.menu.items) - 1) // ITEMS_PER_PAGE + 1
def show(self):
if not _static_graphics:
raise RuntimeError(
f"{__name__} module isn't initialized. "
f"Did you forget to pyload('{__name__.partition('.')[2]}')?"
)
self._show_time = GetTickCount()
self._destroy_player_textdraws()
self._create_player_textdraws()
for index, item in enumerate(self.menu.items[:ITEMS_PER_PAGE]):
self._show_item_at_index(item, index)
playerid = self.playerid
PlayerTextDrawSetString(
playerid,
self._paging_text,
f'1/{self._total_pages}'
)
PlayerTextDrawSetString(playerid, self._header_text, self.menu.title)
PlayerTextDrawShow(playerid, self._paging_text)
PlayerTextDrawShow(playerid, self._header_text)
_static_graphics.show(playerid)
SelectTextDraw(playerid, 0xFFFFFFFF)
def hide(self):
global _player_menus
playerid = self.playerid
self._destroy_player_textdraws()
_static_graphics.hide(playerid)
CancelSelectTextDraw(playerid)
del _player_menus[playerid]
def set_page(self, page):
if not (0 <= page < self._total_pages):
return
playerid = self.playerid
for item_model, item_text in zip(self._item_models, self._item_texts):
PlayerTextDrawHide(playerid, item_model)
PlayerTextDrawHide(playerid, item_text)
start_index = ITEMS_PER_PAGE * page
for index, item in enumerate(
self.menu.items[start_index:start_index + ITEMS_PER_PAGE]
):
self._show_item_at_index(item, index)
self._current_page = page
PlayerTextDrawSetString(
playerid,
self._paging_text,
f'{page + 1}/{self._total_pages}'
)
def next_page(self):
if self._current_page >= self._total_pages - 1:
return
self.set_page(self._current_page + 1)
def previous_page(self):
if self._current_page <= 0:
return
self.set_page(self._current_page - 1)
def showed_at_least(self, ticks):
return GetTickCount() - self._show_time >= ticks
def get_clicked_item(self, playertextid):
item_models = self._item_models
if playertextid not in item_models:
return None
return self.menu.items[
self._current_page * ITEMS_PER_PAGE
+ item_models.index(playertextid)
]
def on_select(self, item):
self.menu.on_select(self.playerid, item)
def on_cancel(self):
self.menu.on_cancel(self.playerid)
def _create_player_textdraws(self):
playerid = self.playerid
self._paging_text = paging_text = CreatePlayerTextDraw(
playerid,
523.333251,
139.792648,
'0/1'
)
PlayerTextDrawLetterSize(playerid, paging_text, 0.190666, 1.110518)
PlayerTextDrawAlignment(playerid, paging_text, 3)
PlayerTextDrawColor(playerid, paging_text, 0xC0C0C0FF)
PlayerTextDrawSetShadow(playerid, paging_text, 0)
PlayerTextDrawSetOutline(playerid, paging_text, 1)
PlayerTextDrawBackgroundColor(playerid, paging_text, 0x00000033)
PlayerTextDrawFont(playerid, paging_text, 2)
PlayerTextDrawSetProportional(playerid, paging_text, 1)
self._header_text = header_text = CreatePlayerTextDraw(
playerid,
128.333312,
139.377761,
'header'
)
PlayerTextDrawLetterSize(playerid, header_text, 0.315000, 1.247407)
PlayerTextDrawAlignment(playerid, header_text, 1)
PlayerTextDrawColor(playerid, header_text, 0xC0C0C0FF)
PlayerTextDrawSetShadow(playerid, header_text, 0)
PlayerTextDrawSetOutline(playerid, header_text, 1)
PlayerTextDrawBackgroundColor(playerid, header_text, 0x00000033)
PlayerTextDrawFont(playerid, header_text, 2)
PlayerTextDrawSetProportional(playerid, header_text, 1)
item_models, item_texts = self._item_models, self._item_texts
origin = (140., 162.)
for index, item in enumerate(self.menu.items):
if index >= ITEMS_PER_PAGE:
break
# Hard-coded because that's how it was
current_col = index % 6
current_line = index // 6
current_coords = (
origin[0] + 62 * current_col,
origin[1] + 55 * current_line,
)
item_model = CreatePlayerTextDraw(
playerid,
current_coords[0],
current_coords[1],
'_'
)
item_models.append(item_model)
PlayerTextDrawBackgroundColor(playerid, item_model, 0xD3D3D344)
PlayerTextDrawFont(playerid, item_model, 5)
PlayerTextDrawLetterSize(playerid, item_model, 1.430000, 5.700000)
PlayerTextDrawColor(playerid, item_model, 0xC0C0C0FF)
PlayerTextDrawSetOutline(playerid, item_model, 1)
PlayerTextDrawSetProportional(playerid, item_model, 1)
PlayerTextDrawUseBox(playerid, item_model, 1)
PlayerTextDrawBoxColor(playerid, item_model, 0)
PlayerTextDrawTextSize(playerid, item_model, 61.000000, 54.000000)
PlayerTextDrawSetSelectable(playerid, item_model, 1)
item_text = CreatePlayerTextDraw(
playerid,
current_coords[0] + 31,
current_coords[1], '_'
)
item_texts.append(item_text)
PlayerTextDrawFont(playerid, item_text, 2)
PlayerTextDrawLetterSize(playerid, item_text, 0.199999, 0.6)
PlayerTextDrawAlignment(playerid, item_text, 2)
PlayerTextDrawSetOutline(playerid, item_text, 0)
PlayerTextDrawSetProportional(playerid, item_text, 1)
PlayerTextDrawTextSize(playerid, item_text, 0.0, 62.0)
PlayerTextDrawSetShadow(playerid, item_text, 0)
PlayerTextDrawColor(playerid, item_text, 0xD3D3D3AA)
def _destroy_player_textdraws(self):
playerid = self.playerid
PlayerTextDrawDestroy(playerid, self._paging_text)
PlayerTextDrawDestroy(playerid, self._header_text)
self._paging_text = INVALID_TEXT_DRAW
self._header_text = INVALID_TEXT_DRAW
for item_model, item_text in zip(self._item_models, self._item_texts):
PlayerTextDrawDestroy(playerid, item_model)
PlayerTextDrawDestroy(playerid, item_text)
self._item_models = []
self._item_texts = []
def _show_item_at_index(self, item, index):
playerid = self.playerid
item_model = self._item_models[index]
PlayerTextDrawSetPreviewModel(playerid, item_model, item.model_id)
PlayerTextDrawSetPreviewRot(
playerid,
item_model,
item.rot_x,
item.rot_y,
item.rot_z,
item.zoom
)
PlayerTextDrawShow(playerid, item_model)
if item.text:
item_text = self._item_texts[index]
PlayerTextDrawSetString(playerid, item_text, item.text)
PlayerTextDrawShow(playerid, item_text)
def OnGameModeInit():
global _static_graphics
_static_graphics = StaticGraphics()
def OnPlayerClickTextDraw(playerid, clickedid):
global _static_graphics, _player_menus
if not _player_menus.get(playerid):
return
player_menu = _player_menus[playerid]
# Handle on_select showing another Menu
if not player_menu.showed_at_least(600):
return
if clickedid in (INVALID_TEXT_DRAW, _static_graphics.close_button):
player_menu.on_cancel()
player_menu.hide()
elif clickedid == _static_graphics.right_arrow:
player_menu.next_page()
elif clickedid == _static_graphics.left_arrow:
player_menu.previous_page()
def OnPlayerClickPlayerTextDraw(playerid, playertextid):
global _static_graphics, _player_menus
if not _player_menus.get(playerid):
return
player_menu = _player_menus[playerid]
# Handle on_select showing another Menu
if not player_menu.showed_at_least(600):
return
item = player_menu.get_clicked_item(playertextid)
if not item:
return
player_menu.hide()
player_menu.on_select(item)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment