678 lines
17 KiB
C
Executable file
678 lines
17 KiB
C
Executable file
#include "game/board/com.h"
|
|
#include "game/gamework.h"
|
|
#include "game/gamework_data.h"
|
|
#include "game/board/com_path.h"
|
|
#include "game/board/main.h"
|
|
#include "game/board/player.h"
|
|
#include "game/board/space.h"
|
|
#include "game/board/tutorial.h"
|
|
#include "game/board/window.h"
|
|
#include "game/board/boo.h"
|
|
|
|
#include "math.h"
|
|
|
|
typedef BOOL (*UseCheckFunc)(s32 player, s32 item);
|
|
|
|
static void ExecComKeyLeft(void);
|
|
static void ExecComKeyRight(void);
|
|
static void ExecComKeyUp(void);
|
|
static void ExecComKeyDown(void);
|
|
static void ExecComKey(s32 player, s32 move, s32 vertical);
|
|
static s32 ChooseUseItem(s32 player);
|
|
static BOOL CheckMiniUse(s32 player, s32 item);
|
|
static BOOL CheckMegaUse(s32 player, s32 item);
|
|
static BOOL CheckSparkUse(s32 player, s32 item);
|
|
static BOOL CheckBoardChangeUse(s32 player, s32 item);
|
|
static BOOL CheckSwapUse(s32 player, s32 item);
|
|
static BOOL CheckBooUse(s32 player, s32 item);
|
|
static BOOL CheckLampUse(s32 player, s32 item);
|
|
|
|
static s8 itemUse = -1;
|
|
|
|
static s8 comItemPreferTbl[8][12] = {
|
|
{ 0x0C, 0x0B, 0x05, 0x03, 0x01, 0x02, 0x06, 0x04, 0x00, 0x07, 0x09, 0x0A },
|
|
{ 0x0C, 0x0B, 0x05, 0x0A, 0x03, 0x01, 0x02, 0x00, 0x06, 0x04, 0x07, 0x09 },
|
|
{ 0x0C, 0x0B, 0x05, 0x06, 0x03, 0x01, 0x02, 0x0A, 0x07, 0x09, 0x00, 0x04 },
|
|
{ 0x0C, 0x05, 0x0B, 0x09, 0x03, 0x01, 0x02, 0x06, 0x00, 0x04, 0x0A, 0x07 },
|
|
{ 0x0C, 0x0B, 0x04, 0x09, 0x0A, 0x03, 0x01, 0x05, 0x02, 0x06, 0x00, 0x07 },
|
|
{ 0x0C, 0x0B, 0x05, 0x03, 0x01, 0x0A, 0x06, 0x02, 0x00, 0x04, 0x09, 0x07 },
|
|
{ 0x0C, 0x05, 0x0B, 0x04, 0x07, 0x09, 0x03, 0x01, 0x0A, 0x06, 0x02, 0x00 },
|
|
{ 0x0C, 0x05, 0x07, 0x0B, 0x0A, 0x09, 0x04, 0x06, 0x03, 0x01, 0x02, 0x00 }
|
|
};
|
|
|
|
static UseCheckFunc comItemUseCheckFuncTbl[] = {
|
|
CheckMiniUse,
|
|
CheckMegaUse,
|
|
CheckMiniUse,
|
|
CheckMegaUse,
|
|
CheckBoardChangeUse,
|
|
CheckBoardChangeUse,
|
|
CheckSwapUse,
|
|
CheckSparkUse,
|
|
NULL,
|
|
CheckBoardChangeUse,
|
|
NULL,
|
|
CheckBooUse,
|
|
CheckLampUse,
|
|
NULL
|
|
};
|
|
|
|
static Vec comJunctionDirTbl[] = {
|
|
{ 0.0f, 0.0f, -72.0f },
|
|
{ 90.0f, 72.0f, 0.0f },
|
|
{ 180.0f, 0.0f, 72.0f },
|
|
{ 270.0f, -72.0f, 0.0f }
|
|
};
|
|
|
|
static void ExecComKeyLeft(void) {
|
|
ExecComKey(GWSystem.player_curr, 0, 1);
|
|
}
|
|
|
|
void BoardComKeySetLeft(void) {
|
|
BoardWinComKeyFuncSet(ExecComKeyLeft);
|
|
}
|
|
|
|
static void ExecComKeyRight(void) {
|
|
ExecComKey(GWSystem.player_curr, 1, 1);
|
|
}
|
|
|
|
void BoardComKeySetRight(void) {
|
|
BoardWinComKeyFuncSet(ExecComKeyRight);
|
|
}
|
|
|
|
static void ExecComKeyUp(void) {
|
|
ExecComKey(GWSystem.player_curr, 0, 0);
|
|
}
|
|
|
|
void BoardComKeySetUp(void) {
|
|
BoardWinComKeyFuncSet(ExecComKeyUp);
|
|
}
|
|
|
|
static void ExecComKeyDown(void) {
|
|
ExecComKey(GWSystem.player_curr, 1, 0);
|
|
}
|
|
|
|
void BoardComKeySetDown(void) {
|
|
BoardWinComKeyFuncSet(ExecComKeyDown);
|
|
}
|
|
|
|
static void ExecComKey(s32 player, s32 move, s32 vertical) {
|
|
s32 comkey[4];
|
|
s32 port;
|
|
s16 delay;
|
|
s32 dpad;
|
|
|
|
comkey[0] = comkey[1] = comkey[2] = comkey[3] = 0;
|
|
port = GWPlayer[player].port;
|
|
delay = GWMessDelayGet();
|
|
if (vertical == 0) {
|
|
dpad = PAD_BUTTON_DOWN;
|
|
} else {
|
|
dpad = PAD_BUTTON_RIGHT;
|
|
}
|
|
if (move == 1) {
|
|
comkey[port] = dpad;
|
|
HuWinComKeyWait(comkey[0], comkey[1], comkey[2], comkey[3], delay);
|
|
}
|
|
comkey[port] = 0x100;
|
|
HuWinComKeyWait(comkey[0], comkey[1], comkey[2], comkey[3], delay);
|
|
}
|
|
|
|
s8 BoardComPreferItemGet(s32 player, s8 *items, s8 num_items) {
|
|
s32 character;
|
|
s32 weight;
|
|
s32 item;
|
|
s32 prefer;
|
|
s32 i;
|
|
s32 j;
|
|
|
|
character = GWPlayer[player].character;
|
|
prefer = -1;
|
|
weight = 100;
|
|
for (i = 0; i < num_items; i++) {
|
|
item = items[i];
|
|
for (j = 0; j < 12; j++) {
|
|
if (item == comItemPreferTbl[character][j]) {
|
|
if (j < weight && (BoardPlayerItemFind(player, item) == -1 || item == 0xC || item == 0xB)) {
|
|
weight = j;
|
|
prefer = i;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (weight == 100) {
|
|
return -1;
|
|
}
|
|
return prefer;
|
|
}
|
|
|
|
s8 BoardComPreferItemCheck(s32 player, s8 item1, s8 item2, s8 item3) {
|
|
s8 items[3];
|
|
s8 prefer;
|
|
|
|
items[0] = item1;
|
|
items[1] = item2;
|
|
items[2] = item3;
|
|
prefer = BoardComPreferItemGet(player, items, 3);
|
|
return prefer;
|
|
}
|
|
|
|
s32 BoardComItemWeightGet(s32 player, s32 item) {
|
|
s32 weight;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 12; i++) {
|
|
if (item == comItemPreferTbl[GWPlayer[player].character][i]) {
|
|
break;
|
|
}
|
|
}
|
|
weight = 12-1-i;
|
|
return weight;
|
|
}
|
|
|
|
s32 BoardComUseItemSlotGet(s32 player) {
|
|
s32 slot;
|
|
s32 item;
|
|
|
|
if (!GWPlayer[player].com) {
|
|
return -1;
|
|
}
|
|
if (itemUse != -1) {
|
|
item = itemUse;
|
|
}
|
|
if (boardTutorialUseItem >= 0 && boardTutorialUseItem <= 13) {
|
|
item = boardTutorialUseItem;
|
|
boardTutorialUseItem = -1;
|
|
}
|
|
if (item != -1) {
|
|
slot = BoardPlayerItemFind(player, item);
|
|
} else {
|
|
slot = -1;
|
|
}
|
|
return slot;
|
|
}
|
|
|
|
s32 BoardComUseItemSet(s32 player, s32 item) {
|
|
if (item == -1) {
|
|
itemUse = -1;
|
|
return 1;
|
|
}
|
|
if (BoardPlayerItemFind(player, item) != -1) {
|
|
itemUse = item;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
BOOL BoardComUseItemCheck(s32 player) {
|
|
s32 count;
|
|
|
|
count = BoardPlayerItemCount(player);
|
|
if (count <= 0) {
|
|
return FALSE;
|
|
}
|
|
if (ChooseUseItem(player) != -1) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static s32 ChooseUseItem(s32 player) {
|
|
s32 i;
|
|
s32 j;
|
|
s32 useItem;
|
|
s32 weight;
|
|
s32 item;
|
|
s32 character;
|
|
UseCheckFunc func;
|
|
s8 result;
|
|
|
|
useItem = -1;
|
|
character = GWPlayer[player].character;
|
|
weight = 0x64;
|
|
for (i = 0; i < 3; i++) {
|
|
item = GWPlayer[player].items[i];
|
|
if (item == -1) {
|
|
continue;
|
|
}
|
|
func = comItemUseCheckFuncTbl[item];
|
|
if (!func) {
|
|
continue;
|
|
}
|
|
result = func(player, item);
|
|
if (result) {
|
|
for (j = 0; j < 0xC; j++) {
|
|
if (item == comItemPreferTbl[character][j]) {
|
|
if (j < weight) {
|
|
weight = j;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (weight == 0x64) {
|
|
return -1;
|
|
}
|
|
useItem = comItemPreferTbl[character][weight];
|
|
if (useItem == -1) {
|
|
itemUse = -1;
|
|
} else if (BoardPlayerItemFind(player, useItem) != -1) {
|
|
itemUse = useItem;
|
|
}
|
|
return useItem;
|
|
}
|
|
|
|
static BOOL CheckMiniUse(s32 player, s32 item) {
|
|
s32 space;
|
|
s32 star_dist_pipe;
|
|
s32 star_dist_no_pipe;
|
|
s32 diff;
|
|
s32 character;
|
|
s32 force_use_mini;
|
|
s32 max_dist;
|
|
s32 chance;
|
|
|
|
force_use_mini = 0;
|
|
space = GWPlayer[player].space_curr;
|
|
diff = GWPlayer[player].diff;
|
|
character = GWPlayer[player].character;
|
|
switch (diff) {
|
|
case 0:
|
|
chance = 50;
|
|
break;
|
|
case 1:
|
|
chance = 25;
|
|
break;
|
|
case 2:
|
|
chance = 5;
|
|
break;
|
|
case 3:
|
|
chance = 0;
|
|
break;
|
|
}
|
|
if ((diff == 0 || diff == 1) && character == 1) {
|
|
chance = 7;
|
|
}
|
|
if (GWBoardGet() != 7 && GWBoardGet() != 8) {
|
|
star_dist_pipe = BoardComPathShortcutLenGet(space, 8, 0);
|
|
star_dist_no_pipe = BoardComPathShortcutLenGet(space, 8, 1);
|
|
} else {
|
|
star_dist_pipe = 0x3E7;
|
|
star_dist_no_pipe = BoardComPathBestGetFlag(space, 0x10000000, 10);
|
|
if ((GWBoardGet() == 8 || GWBoardGet() == 7) && BoardRandMod(0x64) < 0x3C) {
|
|
force_use_mini = 1;
|
|
}
|
|
}
|
|
switch (GWPlayer[player].diff) {
|
|
case 0:
|
|
max_dist = 5;
|
|
break;
|
|
case 1:
|
|
max_dist = 7;
|
|
break;
|
|
case 2:
|
|
max_dist = 9;
|
|
break;
|
|
case 3:
|
|
max_dist = 10;
|
|
break;
|
|
}
|
|
if ((star_dist_no_pipe < max_dist || star_dist_pipe < max_dist || force_use_mini != 0) && BoardRandMod(0x64) >= chance) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL CheckMegaUse(s32 player, s32 item) {
|
|
s32 short_len;
|
|
s32 character;
|
|
s32 space;
|
|
s32 diff;
|
|
s16 path_len;
|
|
s16 max_len;
|
|
s16 space_search;
|
|
s16 space_other;
|
|
s16 i;
|
|
|
|
diff = GWPlayer[player].diff;
|
|
character = GWPlayer[player].character;
|
|
switch (diff) {
|
|
case 0:
|
|
max_len = 5;
|
|
break;
|
|
case 1:
|
|
max_len = 7;
|
|
break;
|
|
case 2:
|
|
max_len = 9;
|
|
break;
|
|
case 3:
|
|
max_len = 10;
|
|
break;
|
|
}
|
|
if (((diff == 0 || diff == 1) && character == 0) || ((diff == 0 || diff == 1) && character == 5)) {
|
|
max_len = 8;
|
|
}
|
|
if (GWBoardGet() != 7 || BoardRandMod(0x64) >= 60) {
|
|
space = GWPlayer[player].space_curr;
|
|
short_len = BoardComPathShortcutLenGet(space, 8, 0);
|
|
space_search = GWPlayer[player].space_curr;
|
|
for (i = 0; i < 4; i++) {
|
|
if (i != player) {
|
|
space_other = GWPlayer[i].space_curr;
|
|
path_len = BoardComPathLenGet(space_search, space_other);
|
|
if (path_len < max_len + 20 && path_len > 0 && GWTeamGet() && i == BoardPlayerSameTeamFind(player)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
if (GWBoardGet() != 7 && GWBoardGet() != 8 && short_len < 10 && short_len > 0) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (BoardPlayerItemFind(player, item) != -1) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL CheckSparkUse(s32 player, s32 item) {
|
|
s32 length;
|
|
s32 diff;
|
|
s32 space_other;
|
|
s32 space;
|
|
s32 character;
|
|
s32 i;
|
|
s32 chance;
|
|
|
|
space = GWPlayer[player].space_curr;
|
|
diff = GWPlayer[player].diff;
|
|
character = GWPlayer[player].character;
|
|
switch (diff) {
|
|
case 0:
|
|
chance = 50;
|
|
break;
|
|
case 1:
|
|
chance = 25;
|
|
break;
|
|
case 2:
|
|
chance = 5;
|
|
break;
|
|
case 3:
|
|
chance = 0;
|
|
break;
|
|
}
|
|
if ((diff == 0 || diff == 1) && character == 7) {
|
|
chance = 7;
|
|
}
|
|
for (i = 0; i < 4; i++) {
|
|
if (i != player) {
|
|
space_other = GWPlayer[i].space_curr;
|
|
length = BoardComPathLenGet(space_other, space);
|
|
if (length > 0 && length <= 15 && BoardPlayerItemFind(player, item) != -1 && BoardRandMod(0x64) >= chance) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL CheckBoardChangeUse(s32 player, s32 item) {
|
|
s32 diff;
|
|
s32 character;
|
|
s32 mini_len;
|
|
s32 non_mini_len;
|
|
s32 search_space;
|
|
s32 space;
|
|
s32 length_min;
|
|
s32 length;
|
|
s32 chance;
|
|
s32 i;
|
|
|
|
diff = GWPlayer[player].diff;
|
|
character = GWPlayer[player].character;
|
|
switch (diff) {
|
|
case 0:
|
|
chance = 0x32;
|
|
break;
|
|
case 1:
|
|
chance = 0x19;
|
|
break;
|
|
case 2:
|
|
chance = 5;
|
|
break;
|
|
case 3:
|
|
chance = 0;
|
|
break;
|
|
}
|
|
if (((item == 4 && character == 4) || (item == 5 && (character == 6 || character == 3))) && (diff == 0 || diff == 1)) {
|
|
chance = 7;
|
|
}
|
|
length_min = 999;
|
|
for (i = 0; i < 4; i++) {
|
|
if (player == i) {
|
|
continue;
|
|
}
|
|
search_space = GWPlayer[i].space_curr;
|
|
if (!GWTeamGet() || i != BoardPlayerSameTeamFind(player)) {
|
|
space = GWPlayer[i].space_curr;
|
|
if (GWBoardGet() == 7) {
|
|
if (BoardComPathBestGetFlag(search_space, 0x10000000, 10) != -1) {
|
|
length = 10;
|
|
} else {
|
|
length = 0;
|
|
}
|
|
} else if (GWBoardGet() == 8) {
|
|
if (BoardComPathBestGetFlag(search_space, 0x200000, 10) != -1 || BoardComPathBestGetFlag(search_space, 0x400000, 10) != -1) {
|
|
length = 10;
|
|
} else {
|
|
length = 0;
|
|
}
|
|
} else {
|
|
length = BoardComPathShortcutLenGet(space, 8, 0);
|
|
}
|
|
if (length != 0 && length < length_min) {
|
|
length_min = length;
|
|
}
|
|
}
|
|
}
|
|
if ((length_min > 0xF && length_min != 999) || (GWTeamGet() && i == BoardPlayerSameTeamFind(player))) {
|
|
return FALSE;
|
|
}
|
|
space = GWPlayer[player].space_curr;
|
|
mini_len = BoardComPathShortcutLenGet(space, 8, 0);
|
|
non_mini_len = BoardComPathShortcutLenGet(space, 8, 1);
|
|
if (BoardPlayerItemFind(player, item) != -1 && BoardRandMod(0x64) >= chance) {
|
|
if ((mini_len == 0 || non_mini_len == 0) && length_min == 999) {
|
|
return TRUE;
|
|
}
|
|
if (mini_len > length_min || non_mini_len > length_min) {
|
|
return TRUE;
|
|
}
|
|
if (mini_len > 10 || non_mini_len > 10) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL CheckSwapUse(s32 player, s32 item) {
|
|
s32 diff;
|
|
s32 team_player;
|
|
s32 character;
|
|
s32 item_count;
|
|
s32 chance;
|
|
s32 i;
|
|
|
|
diff = GWPlayer[player].diff;
|
|
character = GWPlayer[player].character;
|
|
switch (diff) {
|
|
case 0:
|
|
chance = 0x32;
|
|
break;
|
|
case 1:
|
|
chance = 0x19;
|
|
break;
|
|
case 2:
|
|
chance = 5;
|
|
break;
|
|
case 3:
|
|
chance = 0;
|
|
break;
|
|
}
|
|
if ((diff == 0 || diff == 1) && character == 2) {
|
|
chance = 7;
|
|
}
|
|
for (item_count = i = 0; i < 4; i++) {
|
|
if (i != player) {
|
|
item_count += BoardPlayerItemCount(i);
|
|
}
|
|
}
|
|
if (item_count == 0) {
|
|
return FALSE;
|
|
}
|
|
if (GWTeamGet()) {
|
|
team_player = BoardPlayerSameTeamFind(player);
|
|
for (item_count = i = 0; i < 4; i++) {
|
|
if (team_player != i && i != player) {
|
|
item_count += BoardPlayerItemCount(i);
|
|
}
|
|
}
|
|
if (item_count == 0) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (BoardPlayerItemFind(player, item) != -1 && BoardRandMod(0x64) >= chance) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL CheckBooUse(s32 player, s32 item) {
|
|
if (BoardPlayerCoinsGet(player) < 5) {
|
|
return FALSE;
|
|
}
|
|
if (BoardBooComUseCheck(player) == 0) {
|
|
return FALSE;
|
|
}
|
|
if (BoardPlayerItemFind(player, item) == -1) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL CheckLampUse(s32 player, s32 item) {
|
|
if ((BoardPlayerCoinsGet(player) >= 0x14 || GWSystem.last5_effect == 4) && BoardPlayerItemFind(player, item) != -1) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
s32 BoardComJunctionInputGet(s32 item, Vec *input, s32 num_dirs, float *dirs) {
|
|
Vec pos_next;
|
|
Vec pos_junction;
|
|
Vec dir;
|
|
float dist_min;
|
|
float dist;
|
|
float angle;
|
|
s32 chance_random;
|
|
s32 best_dir;
|
|
s32 board;
|
|
s32 path_unknown;
|
|
s32 choose_path;
|
|
s32 dir_random;
|
|
s32 i;
|
|
s16 space_next;
|
|
s16 space;
|
|
s16 roll;
|
|
|
|
board = GWBoardGet();
|
|
path_unknown = 0;
|
|
space = GWPlayer[item].space_curr;
|
|
space_next = -1;
|
|
roll = GWPlayer[item].roll;
|
|
switch (GWPlayer[item].diff) {
|
|
case 3:
|
|
chance_random = 0;
|
|
break;
|
|
case 2:
|
|
chance_random = 0;
|
|
break;
|
|
case 1:
|
|
chance_random = 10;
|
|
break;
|
|
default:
|
|
case 0:
|
|
chance_random = 0x1E;
|
|
break;
|
|
}
|
|
if (BoardRandMod(0x64) >= chance_random) {
|
|
choose_path = 1;
|
|
} else {
|
|
choose_path = 0;
|
|
}
|
|
if (choose_path != 0) {
|
|
if (board >= 0 && board <= 5) {
|
|
space_next = BoardComPathBestGet(space);
|
|
} else if (board == 7) {
|
|
space_next = BoardComPathBestGetFlag(space, 0x10000000, 10);
|
|
}
|
|
if (space_next == -1) {
|
|
path_unknown = 1;
|
|
} else {
|
|
BoardSpacePosGet(0, space, &pos_junction);
|
|
BoardSpacePosGet(0, space_next, &pos_next);
|
|
PSVECSubtract(&pos_next, &pos_junction, &dir);
|
|
angle = BoardDAngleCalc(90.0 - 180.0 * (atan2(dir.z, dir.x) / M_PI));
|
|
if (angle < 0.0f) {
|
|
angle += 360.0f;
|
|
}
|
|
if (angle > 360.0f) {
|
|
angle -= 360.0f;
|
|
}
|
|
}
|
|
}
|
|
if (path_unknown != 0) {
|
|
dir_random = BoardRandMod(num_dirs);
|
|
angle = dirs[dir_random];
|
|
}
|
|
dist_min = 999.0f;
|
|
for (best_dir = i = 0; i < 4; i++) {
|
|
dist = (comJunctionDirTbl[i].x - angle < 0.0f)
|
|
? -(comJunctionDirTbl[i].x - angle)
|
|
: (comJunctionDirTbl[i].x - angle);
|
|
if (dist < dist_min) {
|
|
best_dir = i;
|
|
dist_min = dist;
|
|
}
|
|
}
|
|
input->x = comJunctionDirTbl[best_dir].y;
|
|
input->z = comJunctionDirTbl[best_dir].z;
|
|
input->y = 0.0f;
|
|
return 0;
|
|
}
|
|
|
|
s32 BoardComFarPlayerFind(void) {
|
|
s32 len;
|
|
s32 max_len;
|
|
s32 far_player;
|
|
s32 space;
|
|
s32 i;
|
|
|
|
if (_CheckFlag(0x1000B) != 0) {
|
|
return -1;
|
|
}
|
|
far_player = -1;
|
|
max_len = -1;
|
|
for (i = 0; i < 4; i++) {
|
|
space = GWPlayer[i].space_curr;
|
|
len = BoardComPathShortcutLenGet(space, 8, 0);
|
|
if (len > 0 && len > max_len) {
|
|
max_len = len;
|
|
far_player = i;
|
|
}
|
|
}
|
|
return far_player;
|
|
}
|