feat: implemented die images
This commit is contained in:
parent
bdca66b4fd
commit
f9234dc9a2
|
|
@ -6,15 +6,8 @@
|
|||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
enum Font {
|
||||
FONT_DEFAULT = 0,
|
||||
FONT_BOLD = 1,
|
||||
FONT_MAX
|
||||
};
|
||||
|
||||
extern SDL_Window *window;
|
||||
extern SDL_Renderer *renderer;
|
||||
extern TTF_Font *fonts[FONT_MAX];
|
||||
extern TTF_TextEngine *textEngine;
|
||||
|
||||
#endif // !DEFS_H
|
||||
|
|
|
|||
43
src/dice.c
43
src/dice.c
|
|
@ -1,58 +1,73 @@
|
|||
#include "dice.h"
|
||||
#include <memory.h>
|
||||
|
||||
int roll_die(enum die_type die) {
|
||||
int Die_Roll(enum Die_Dice die) {
|
||||
int const max = die;
|
||||
return (rand() % max) + 1;
|
||||
}
|
||||
|
||||
static int current_active_count = 0;
|
||||
static enum die_type active_dice_set[MAX_ACTIVE_DICE];
|
||||
static enum Die_Dice active_dice_set[MAX_ACTIVE_DICE];
|
||||
|
||||
static struct roll_result_type roll_results[MAX_ACTIVE_DICE];
|
||||
static struct roll_result_type roll_total = {
|
||||
static struct Die_ResultType roll_results[MAX_ACTIVE_DICE];
|
||||
static struct Die_ResultType roll_total = {
|
||||
.roll = 0, .string_len = 0
|
||||
};
|
||||
|
||||
enum die_type const *get_active_dice_set(size_t *out_length) {
|
||||
static
|
||||
struct Die_ResultType Die_RollToResultType(int roll, enum Die_Dice die) {
|
||||
struct Die_ResultType result = { };
|
||||
result.roll = roll;
|
||||
if (die == COIN) {
|
||||
result.string_len = SDL_snprintf(result.string, MAX_ROLL_STR_LEN, roll == 1 ? "H" : "T");
|
||||
} else {
|
||||
result.string_len = SDL_snprintf(result.string, MAX_ROLL_STR_LEN, "%d", roll);
|
||||
}
|
||||
result.clay_string = (Clay_String) {
|
||||
.chars = result.string,
|
||||
.length = result.string_len,
|
||||
.isStaticallyAllocated = false
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
enum Die_Dice const *Die_GetActiveSet(size_t *out_length) {
|
||||
if (out_length != nullptr) {
|
||||
*out_length = current_active_count;
|
||||
}
|
||||
return active_dice_set;
|
||||
}
|
||||
|
||||
size_t add_die_to_active(enum die_type die) {
|
||||
size_t Die_AddToActiveSet(enum Die_Dice die) {
|
||||
if (current_active_count >= MAX_ACTIVE_DICE) {
|
||||
return MAX_ACTIVE_DICE;
|
||||
}
|
||||
active_dice_set[current_active_count] = die;
|
||||
roll_results[current_active_count].roll = die;
|
||||
roll_results[current_active_count].string_len = SDL_snprintf(roll_results[current_active_count].string, MAX_ROLL_STR_LEN, "%d", die);
|
||||
roll_results[current_active_count] = Die_RollToResultType(die, die);
|
||||
return current_active_count++;
|
||||
}
|
||||
|
||||
void remove_die_from_active(size_t index) {
|
||||
void Die_RemoveFromActiveSet(size_t index) {
|
||||
memcpy(active_dice_set + index, active_dice_set + index + 1, MAX_ACTIVE_DICE - index - 1);
|
||||
--current_active_count;
|
||||
}
|
||||
|
||||
void roll_active_dice_set() {
|
||||
void Die_RollActiveSet() {
|
||||
for (size_t i = 0; i < current_active_count; ++i) {
|
||||
roll_results[i].roll = roll_die(active_dice_set[i]);
|
||||
roll_results[i].string_len = SDL_snprintf(roll_results[i].string, MAX_ROLL_STR_LEN, "%d", roll_results[i].roll);
|
||||
roll_results[i] = Die_RollToResultType(Die_Roll(active_dice_set[i]), active_dice_set[i]);
|
||||
roll_total.roll += roll_results[i].roll;
|
||||
}
|
||||
roll_total.string_len = SDL_snprintf(roll_total.string, MAX_ROLL_STR_LEN, "%d", roll_total.roll);
|
||||
}
|
||||
|
||||
struct roll_result_type *get_current_results(size_t *out_length) {
|
||||
struct Die_ResultType *Die_GetLastResult(size_t *out_length) {
|
||||
if (out_length != nullptr) {
|
||||
*out_length = current_active_count;
|
||||
}
|
||||
return roll_results;
|
||||
}
|
||||
|
||||
Clay_String die_to_str(enum die_type die) {
|
||||
Clay_String Die_ToString(enum Die_Dice die) {
|
||||
switch (die) {
|
||||
case COIN: return CLAY_STRING("C");
|
||||
case D4: return CLAY_STRING("4");
|
||||
|
|
|
|||
21
src/dice.h
21
src/dice.h
|
|
@ -9,10 +9,10 @@
|
|||
#endif
|
||||
|
||||
#ifndef MAX_ROLL_STR_LEN
|
||||
#define MAX_ROLL_STR_LEN 5
|
||||
#define MAX_ROLL_STR_LEN 10
|
||||
#endif
|
||||
|
||||
enum die_type {
|
||||
enum Die_Dice {
|
||||
COIN = 2,
|
||||
D4 = 4,
|
||||
D6 = 6,
|
||||
|
|
@ -23,22 +23,23 @@ enum die_type {
|
|||
D100 = 100
|
||||
};
|
||||
|
||||
struct roll_result_type {
|
||||
struct Die_ResultType {
|
||||
int roll;
|
||||
size_t string_len;
|
||||
char string[MAX_ROLL_STR_LEN];
|
||||
Clay_String clay_string;
|
||||
};
|
||||
|
||||
extern int roll_die(enum die_type die);
|
||||
extern int Die_Roll(enum Die_Dice die);
|
||||
|
||||
extern enum die_type const *get_active_dice_set(size_t *out_length);
|
||||
extern size_t add_die_to_active(enum die_type die);
|
||||
extern void remove_die_from_active(size_t index);
|
||||
extern enum Die_Dice const *Die_GetActiveSet(size_t *out_length);
|
||||
extern size_t Die_AddToActiveSet(enum Die_Dice die);
|
||||
extern void Die_RemoveFromActiveSet(size_t index);
|
||||
|
||||
extern struct roll_result_type *get_current_results(size_t *out_length);
|
||||
extern struct Die_ResultType *Die_GetLastResult(size_t *out_length);
|
||||
|
||||
extern void roll_active_dice_set();
|
||||
extern void Die_RollActiveSet();
|
||||
|
||||
extern Clay_String die_to_str(enum die_type die);
|
||||
extern Clay_String Die_ToString(enum Die_Dice die);
|
||||
|
||||
#endif // !DICE_H
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "dice_container.h"
|
||||
#include <SDL3/SDL_mouse.h>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
#include <clay/clay.h>
|
||||
#include <stdint.h>
|
||||
#include "elements.h"
|
||||
|
|
@ -9,7 +10,7 @@
|
|||
static
|
||||
void HandleRollSetButtonInteraction(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) {
|
||||
if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
|
||||
roll_active_dice_set();
|
||||
Die_RollActiveSet();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -27,21 +28,21 @@ void RollSetButton() {
|
|||
static
|
||||
void HandleAddDieButtonInteraction(Clay_ElementId element, Clay_PointerData pointer, intptr_t die) {
|
||||
if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
|
||||
add_die_to_active((enum die_type)die);
|
||||
Die_AddToActiveSet((enum Die_Dice)die);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
void AddDieButton(enum die_type die) {
|
||||
void AddDieButton(enum Die_Dice die) {
|
||||
CLAY(CLAY_IDI("AddDieButton", die), {
|
||||
.layout = {
|
||||
.sizing = { CLAY_SIZING_FIXED(100), CLAY_SIZING_FIXED(100) },
|
||||
.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER },
|
||||
},
|
||||
.backgroundColor = DieButtonColor(die, Clay_Hovered()),
|
||||
.image = { GetDiceImage(die) }
|
||||
}) {
|
||||
Clay_OnHover(&HandleAddDieButtonInteraction, die);
|
||||
CLAY_TEXT(die_to_str(die), CLAY_TEXT_CONFIG({
|
||||
CLAY_TEXT(Die_ToString(die), CLAY_TEXT_CONFIG({
|
||||
.textColor = TextColors(0),
|
||||
.H(2),
|
||||
}));
|
||||
|
|
@ -84,21 +85,21 @@ void DiceSelectorContainer() {
|
|||
static
|
||||
void HandleRemoveDieButtonInteraction(Clay_ElementId element, Clay_PointerData pointer, intptr_t index) {
|
||||
if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
|
||||
remove_die_from_active(index);
|
||||
Die_RemoveFromActiveSet(index);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
void RemoveDieButton(enum die_type die, int index) {
|
||||
void RemoveDieButton(enum Die_Dice die, int index) {
|
||||
CLAY(CLAY_IDI("RemoveDieButton", index), {
|
||||
.layout = {
|
||||
.sizing = { CLAY_SIZING_FIXED(200), CLAY_SIZING_FIXED(200) },
|
||||
.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER },
|
||||
},
|
||||
.backgroundColor = DieButtonColor(die, Clay_Hovered()),
|
||||
.image = { GetDiceImage(die) },
|
||||
}) {
|
||||
size_t result_length;
|
||||
struct roll_result_type const *result = get_current_results(&result_length);
|
||||
struct Die_ResultType const *result = Die_GetLastResult(&result_length);
|
||||
Clay_String string = {
|
||||
.chars = result[index].string,
|
||||
.length = result[index].string_len,
|
||||
|
|
@ -136,7 +137,7 @@ void ActiveDiceContainer() {
|
|||
},
|
||||
}) {
|
||||
size_t dice_count = 0;
|
||||
enum die_type const *dice = get_active_dice_set(&dice_count);
|
||||
enum Die_Dice const *dice = Die_GetActiveSet(&dice_count);
|
||||
for (size_t i = 0; i < dice_count; ++i) {
|
||||
RemoveDieButton(dice[i], i);
|
||||
}
|
||||
|
|
|
|||
21
src/main.c
21
src/main.c
|
|
@ -7,6 +7,7 @@
|
|||
#include "application.h"
|
||||
#include "defs.h"
|
||||
#include "style.h"
|
||||
#include "resources.h"
|
||||
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL3/SDL.h>
|
||||
|
|
@ -33,8 +34,6 @@ bool running = true;
|
|||
uint64_t clayMemorySize = 0;
|
||||
|
||||
Clay_Arena clayPrimaryArena;
|
||||
TTF_Font *fonts[FONT_MAX];
|
||||
TTF_TextEngine *textEngine = nullptr;
|
||||
|
||||
Clay_SDL3RendererData backendData = {
|
||||
.renderer = nullptr,
|
||||
|
|
@ -66,22 +65,6 @@ void HandleClayErrors(Clay_ErrorData data) {
|
|||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s", data.errorText.chars);
|
||||
}
|
||||
|
||||
static inline void InitFonts() {
|
||||
fonts[FONT_DEFAULT] = TTF_OpenFont("assets/AdwaitaSans-Regular.ttf", baseFontSize * 5);
|
||||
if (fonts[FONT_DEFAULT] == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF_OpenFont failed: Failed to load adwaita sans: %s", SDL_GetError());
|
||||
exit(6);
|
||||
}
|
||||
TTF_SetFontHinting(fonts[FONT_DEFAULT], TTF_HINTING_LIGHT_SUBPIXEL);
|
||||
fonts[FONT_BOLD] = TTF_OpenFont("assets/AdwaitaSans-Regular.ttf", baseFontSize * 5);
|
||||
if (fonts[FONT_BOLD] == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF_OpenFont failed: Failed to load adwaita sans bold: %s", SDL_GetError());
|
||||
exit(7);
|
||||
}
|
||||
TTF_SetFontHinting(fonts[FONT_BOLD], TTF_HINTING_LIGHT_SUBPIXEL);
|
||||
TTF_SetFontStyle(fonts[FONT_BOLD], TTF_STYLE_BOLD);
|
||||
}
|
||||
|
||||
static inline
|
||||
void InitSDL() {
|
||||
SDL_SetHint(SDL_HINT_RENDER_LINE_METHOD, "3");
|
||||
|
|
@ -124,7 +107,7 @@ extern void HandleEvent(SDL_Event event);
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
InitSDL();
|
||||
InitFonts();
|
||||
LoadResources();
|
||||
LogOutputResolution();
|
||||
InitClay();
|
||||
backendData = (Clay_SDL3RendererData) {
|
||||
|
|
|
|||
89
src/resources.c
Normal file
89
src/resources.c
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#include "resources.h"
|
||||
#include "defs.h"
|
||||
#include "style.h"
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <SDL3/SDL_render.h>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
|
||||
TTF_Font *fonts[FONT_MAX];
|
||||
SDL_Texture *diceImages[DICE_IMAGE_MAX];
|
||||
TTF_TextEngine *textEngine = nullptr;
|
||||
|
||||
static inline
|
||||
void LoadFonts() {
|
||||
fonts[FONT_DEFAULT] = TTF_OpenFont("assets/AdwaitaSans-Regular.ttf", baseFontSize * 5);
|
||||
if (fonts[FONT_DEFAULT] == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF_OpenFont failed: Failed to load adwaita sans: %s", SDL_GetError());
|
||||
exit(6);
|
||||
}
|
||||
TTF_SetFontHinting(fonts[FONT_DEFAULT], TTF_HINTING_LIGHT_SUBPIXEL);
|
||||
fonts[FONT_BOLD] = TTF_OpenFont("assets/AdwaitaSans-Regular.ttf", baseFontSize * 5);
|
||||
if (fonts[FONT_BOLD] == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF_OpenFont failed: Failed to load adwaita sans bold: %s", SDL_GetError());
|
||||
exit(6);
|
||||
}
|
||||
TTF_SetFontHinting(fonts[FONT_BOLD], TTF_HINTING_LIGHT_SUBPIXEL);
|
||||
TTF_SetFontStyle(fonts[FONT_BOLD], TTF_STYLE_BOLD);
|
||||
}
|
||||
|
||||
static inline
|
||||
void LoadDiceImages() {
|
||||
if(!(diceImages[COIN_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d2.svg"))) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError());
|
||||
exit(7);
|
||||
}
|
||||
if(!(diceImages[D4_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d4.svg"))) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError());
|
||||
exit(7);
|
||||
}
|
||||
if(!(diceImages[D6_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d6.svg"))) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError());
|
||||
exit(7);
|
||||
}
|
||||
if(!(diceImages[D8_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d8.svg"))) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError());
|
||||
exit(7);
|
||||
}
|
||||
if(!(diceImages[D10_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d10.svg"))) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError());
|
||||
exit(7);
|
||||
}
|
||||
if(!(diceImages[D12_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d12.svg"))) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError());
|
||||
exit(7);
|
||||
}
|
||||
if(!(diceImages[D20_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d20.svg"))) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError());
|
||||
exit(7);
|
||||
}
|
||||
for (size_t i = 0; i < DICE_IMAGE_MAX; ++i) {
|
||||
Clay_Color color = dieColors[i];
|
||||
SDL_SetTextureColorMod(diceImages[i], color.r, color.g, color.b);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadResources() {
|
||||
LoadFonts();
|
||||
LoadDiceImages();
|
||||
}
|
||||
|
||||
SDL_Texture *GetDiceImage(enum Die_Dice die) {
|
||||
switch (die) {
|
||||
case COIN:
|
||||
return diceImages[COIN_IMAGE];
|
||||
case D4:
|
||||
return diceImages[D4_IMAGE];
|
||||
case D6:
|
||||
return diceImages[D6_IMAGE];
|
||||
case D8:
|
||||
return diceImages[D8_IMAGE];
|
||||
case D10:
|
||||
return diceImages[D10_IMAGE];
|
||||
case D12:
|
||||
return diceImages[D12_IMAGE];
|
||||
case D20:
|
||||
return diceImages[D20_IMAGE];
|
||||
case D100:
|
||||
return diceImages[D10_IMAGE];
|
||||
}
|
||||
}
|
||||
34
src/resources.h
Normal file
34
src/resources.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef RESOURCES_H
|
||||
#define RESOURCES_H
|
||||
|
||||
#include "dice.h"
|
||||
#include <SDL3/SDL_render.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
enum Font {
|
||||
FONT_DEFAULT = 0,
|
||||
FONT_BOLD = 1,
|
||||
FONT_MAX
|
||||
};
|
||||
|
||||
enum DiceImages {
|
||||
COIN_IMAGE = 0,
|
||||
D4_IMAGE = 1,
|
||||
D6_IMAGE,
|
||||
D8_IMAGE,
|
||||
D10_IMAGE,
|
||||
D12_IMAGE,
|
||||
D20_IMAGE,
|
||||
|
||||
DICE_IMAGE_MAX
|
||||
};
|
||||
|
||||
extern TTF_TextEngine *textEngine;
|
||||
extern TTF_Font *fonts[FONT_MAX];
|
||||
extern SDL_Texture *diceImages[DICE_IMAGE_MAX];
|
||||
|
||||
extern void LoadResources();
|
||||
|
||||
extern SDL_Texture *GetDiceImage(enum Die_Dice die);
|
||||
|
||||
#endif // !RESOURCES_H
|
||||
20
src/style.c
20
src/style.c
|
|
@ -45,21 +45,21 @@ Clay_ElementDeclaration WindowStyle() {
|
|||
};
|
||||
}
|
||||
|
||||
Clay_Color DieColor(enum die_type die) {
|
||||
Clay_Color DieColor(enum Die_Dice die) {
|
||||
switch(die) {
|
||||
case COIN: return (Clay_Color) { 230, 184, 48, 255 };
|
||||
case D4: return (Clay_Color) { 177, 56, 52, 255 };
|
||||
case D6: return (Clay_Color) { 115, 177, 52, 255 };
|
||||
case D8: return (Clay_Color) { 52, 177, 125, 255 };
|
||||
case D10: return (Clay_Color) { 52, 177, 176, 255 };
|
||||
case D12: return (Clay_Color) { 52, 93, 177, 255 };
|
||||
case D20: return (Clay_Color) { 177, 52, 140, 255 };
|
||||
case D100: return (Clay_Color) { 95, 52, 177, 255 };
|
||||
case COIN: return dieColors[0];
|
||||
case D4: return dieColors[1];
|
||||
case D6: return dieColors[2];
|
||||
case D8: return dieColors[3];
|
||||
case D10: return dieColors[4];
|
||||
case D12: return dieColors[5];
|
||||
case D20: return dieColors[6];
|
||||
case D100: return dieColors[7];
|
||||
default: return (Clay_Color) { 0, 0, 0, 255 };
|
||||
}
|
||||
}
|
||||
|
||||
Clay_Color DieButtonColor(enum die_type die, bool selected) {
|
||||
Clay_Color DieButtonColor(enum Die_Dice die, bool selected) {
|
||||
return selected ? ToHoveredColor(DieColor(die)) : DieColor(die);
|
||||
}
|
||||
|
||||
|
|
|
|||
17
src/style.h
17
src/style.h
|
|
@ -2,7 +2,7 @@
|
|||
#define STYLE_H
|
||||
|
||||
#include "defs.h"
|
||||
#include "dice.h"
|
||||
#include "resources.h"
|
||||
#include <clay/clay.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
|
@ -81,6 +81,17 @@ constexpr Clay_CornerRadius buttonRadii = {
|
|||
3, 3, 3, 3
|
||||
};
|
||||
|
||||
constexpr Clay_Color dieColors[] = {
|
||||
{ 230, 184, 48, 255 },
|
||||
{ 177, 56, 52, 255 },
|
||||
{ 115, 177, 52, 255 },
|
||||
{ 52, 177, 125, 255 },
|
||||
{ 52, 177, 176, 255 },
|
||||
{ 52, 93, 177, 255 },
|
||||
{ 177, 52, 140, 255 },
|
||||
{ 95, 52, 177, 255 },
|
||||
};
|
||||
|
||||
////////////////////////////////////
|
||||
// COMPILATIONS
|
||||
// | Functions and expressions that combine styling data from the settings above.
|
||||
|
|
@ -101,8 +112,8 @@ extern Clay_Color PanelBackground(size_t idx);
|
|||
extern Clay_Color TextColors(size_t idx);
|
||||
extern Clay_Color WindowBackground();
|
||||
extern Clay_ElementDeclaration WindowStyle();
|
||||
extern Clay_Color DieColor(enum die_type die);
|
||||
extern Clay_Color DieButtonColor(enum die_type die, bool selected);
|
||||
extern Clay_Color DieColor(enum Die_Dice die);
|
||||
extern Clay_Color DieButtonColor(enum Die_Dice die, bool selected);
|
||||
extern Clay_Color ToHoveredColor(Clay_Color color);
|
||||
|
||||
#endif // !STYLE_H
|
||||
|
|
|
|||
Loading…
Reference in a new issue