mirror of
https://github.com/pret/pmd-red.git
synced 2026-08-24 17:54:43 -05:00
decomp another function and move movement funcs
This commit is contained in:
@@ -3,83 +3,4 @@
|
||||
|
||||
.syntax unified
|
||||
|
||||
.text
|
||||
|
||||
thumb_func_start sub_8070564
|
||||
sub_8070564:
|
||||
push {r4-r6,lr}
|
||||
adds r4, r1, 0
|
||||
ldr r6, [r0, 0x70]
|
||||
movs r1, 0
|
||||
ldrsh r0, [r4, r1]
|
||||
movs r2, 0x2
|
||||
ldrsh r1, [r4, r2]
|
||||
bl GetMapTile_1
|
||||
adds r5, r0, 0
|
||||
movs r1, 0
|
||||
ldrsh r0, [r4, r1]
|
||||
cmp r0, 0
|
||||
blt _080705E6
|
||||
movs r2, 0x2
|
||||
ldrsh r1, [r4, r2]
|
||||
cmp r1, 0
|
||||
blt _080705E6
|
||||
cmp r0, 0x37
|
||||
bgt _080705E6
|
||||
cmp r1, 0x1F
|
||||
bgt _080705E6
|
||||
ldrh r1, [r5]
|
||||
movs r0, 0x10
|
||||
ands r0, r1
|
||||
cmp r0, 0
|
||||
bne _080705E6
|
||||
ldr r0, [r5, 0x10]
|
||||
cmp r0, 0
|
||||
beq _080705A8
|
||||
bl GetEntityType
|
||||
cmp r0, 0x1
|
||||
bne _080705E6
|
||||
_080705A8:
|
||||
movs r1, 0x2
|
||||
ldrsh r0, [r6, r1]
|
||||
bl GetCrossableTerrain
|
||||
lsls r0, 24
|
||||
lsrs r2, r0, 24
|
||||
adds r3, r2, 0
|
||||
ldrh r0, [r5]
|
||||
movs r1, 0x3
|
||||
ands r1, r0
|
||||
adds r0, r1, 0
|
||||
cmp r2, 0x1
|
||||
beq _080705DE
|
||||
cmp r2, 0x1
|
||||
bgt _080705CC
|
||||
cmp r2, 0
|
||||
beq _080705D8
|
||||
b _080705D4
|
||||
_080705CC:
|
||||
cmp r3, 0x3
|
||||
bgt _080705D4
|
||||
cmp r1, 0
|
||||
beq _080705E6
|
||||
_080705D4:
|
||||
movs r0, 0
|
||||
b _080705E8
|
||||
_080705D8:
|
||||
cmp r1, 0x1
|
||||
bne _080705E6
|
||||
b _080705D4
|
||||
_080705DE:
|
||||
cmp r1, 0x2
|
||||
beq _080705D4
|
||||
cmp r0, 0x1
|
||||
beq _080705D4
|
||||
_080705E6:
|
||||
movs r0, 0x1
|
||||
_080705E8:
|
||||
pop {r4-r6}
|
||||
pop {r1}
|
||||
bx r1
|
||||
thumb_func_end sub_8070564
|
||||
|
||||
.align 2, 0
|
||||
|
||||
@@ -234,7 +234,6 @@ SECTIONS {
|
||||
src/dungeon_movement.o(.text);
|
||||
asm/code_807034C.o(.text);
|
||||
src/dungeon_movement_1.o(.text);
|
||||
asm/code_807049C.o(.text);
|
||||
src/status_checks_1.o(.text);
|
||||
src/dungeon_capabilities.o(.text);
|
||||
asm/code_8070BC0.o(.text);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "constants/item.h"
|
||||
#include "constants/iq_skill.h"
|
||||
#include "constants/status.h"
|
||||
#include "constants/type.h"
|
||||
#include "constants/weather.h"
|
||||
#include "dungeon_engine.h"
|
||||
#include "dungeon_items.h"
|
||||
#include "dungeon_global_data.h"
|
||||
@@ -12,6 +14,8 @@
|
||||
#include "dungeon_pokemon_attributes.h"
|
||||
#include "dungeon_util.h"
|
||||
#include "map.h"
|
||||
#include "pokemon.h"
|
||||
#include "weather.h"
|
||||
|
||||
bool8 CanCrossWalls(struct DungeonEntity *pokemon)
|
||||
{
|
||||
@@ -71,3 +75,164 @@ bool8 sub_807049C(struct DungeonEntity *pokemon, struct Position *pos)
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool8 sub_8070564(struct DungeonEntity *pokemon, struct Position *pos)
|
||||
{
|
||||
u8 crossableTerrain;
|
||||
register s32 crossableTerrain2 asm("r3");
|
||||
struct MapTile *tile;
|
||||
u16 tileFlags;
|
||||
register s32 tileFlags_0 asm("r0");
|
||||
struct DungeonEntityData *entityData;
|
||||
|
||||
entityData = pokemon->entityData;
|
||||
tile = GetMapTile_1(pos->x, pos->y);
|
||||
if ((pos->x >= 0) && (pos->y >= 0) && (DUNGEON_MAX_SIZE_X > pos->x) &&
|
||||
(DUNGEON_MAX_SIZE_Y > pos->y && ((tile->tileType & TILE_TYPE_MAP_EDGE) == 0)) &&
|
||||
(tile->pokemon == NULL || (GetEntityType(tile->pokemon) == ENTITY_POKEMON))) {
|
||||
crossableTerrain2 = crossableTerrain = GetCrossableTerrain(entityData->entityID);
|
||||
tileFlags_0 = tileFlags = tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID);
|
||||
switch(crossableTerrain)
|
||||
{
|
||||
case CROSSABLE_TERRAIN_WALL: // 3
|
||||
case CROSSABLE_TERRAIN_CREVICE: // 2
|
||||
case 4 ... INT_MAX:
|
||||
if (crossableTerrain2 > CROSSABLE_TERRAIN_WALL) return FALSE;
|
||||
if(tileFlags != 0)
|
||||
return FALSE;
|
||||
break;
|
||||
case CROSSABLE_TERRAIN_REGULAR: // 0
|
||||
if(tileFlags == TILE_TYPE_FLOOR)
|
||||
default:
|
||||
return FALSE;
|
||||
break;
|
||||
case CROSSABLE_TERRAIN_LIQUID: // 1
|
||||
if(tileFlags == TILE_TYPE_LIQUID) return FALSE;
|
||||
if (tileFlags_0 == TILE_TYPE_FLOOR) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool8 sub_80705F0(struct DungeonEntity *pokemon, struct Position *pos)
|
||||
{
|
||||
s32 crossableTerrain;
|
||||
struct MapTile *tile;
|
||||
u16 tileFlags;
|
||||
struct DungeonEntityData *entityData;
|
||||
|
||||
entityData = pokemon->entityData;
|
||||
tile = GetMapTile_1(pos->x, pos->y);
|
||||
if ((pos->x >= 0) && (pos->y >= 0) && (DUNGEON_MAX_SIZE_X > pos->x) &&
|
||||
(DUNGEON_MAX_SIZE_Y > pos->y && ((tile->tileType & TILE_TYPE_MAP_EDGE) == 0)) &&
|
||||
((tile->pokemon == NULL) || ((GetEntityType(tile->pokemon) == ENTITY_POKEMON)))) {
|
||||
if (IsFixedDungeon() || (entityData->transformStatus != TRANSFORM_STATUS_MOBILE && !HasItem(pokemon, ITEM_ID_MOBILE_SCARF))) {
|
||||
crossableTerrain = GetCrossableTerrain(entityData->entityID);
|
||||
tileFlags = tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID);
|
||||
if (HasIQSkill(pokemon, IQ_SKILL_ALL_TERRAIN_HIKER)) {
|
||||
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
||||
}
|
||||
if (HasIQSkill(pokemon, IQ_SKILL_SUPER_MOBILE)) {
|
||||
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
||||
}
|
||||
|
||||
switch(crossableTerrain)
|
||||
{
|
||||
case CROSSABLE_TERRAIN_WALL:
|
||||
default:
|
||||
return FALSE;
|
||||
case CROSSABLE_TERRAIN_REGULAR:
|
||||
case CROSSABLE_TERRAIN_LIQUID:
|
||||
case CROSSABLE_TERRAIN_CREVICE:
|
||||
if(tileFlags != 0) return FALSE;
|
||||
}
|
||||
}
|
||||
else return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool8 sub_80706A4(struct DungeonEntity *pokemon, struct Position *pos)
|
||||
{
|
||||
s32 crossableTerrain;
|
||||
struct MapTile *tile;
|
||||
u16 tileFlags;
|
||||
struct DungeonEntityData *entityData;
|
||||
|
||||
entityData = pokemon->entityData;
|
||||
tile = GetMapTile_1(pos->x, pos->y);
|
||||
if ((pos->x >= 0) && (pos->y >= 0) && (DUNGEON_MAX_SIZE_X > pos->x) &&
|
||||
(DUNGEON_MAX_SIZE_Y > pos->y && ((tile->tileType & TILE_TYPE_MAP_EDGE) == 0)) &&
|
||||
((tile->pokemon == NULL) || ((GetEntityType(tile->pokemon) == ENTITY_POKEMON) && (tile->pokemon->entityData == entityData)))) {
|
||||
if (IsFixedDungeon() || (entityData->transformStatus != TRANSFORM_STATUS_MOBILE && !HasItem(pokemon, ITEM_ID_MOBILE_SCARF))) {
|
||||
crossableTerrain = GetCrossableTerrain(entityData->entityID);
|
||||
tileFlags = tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID);
|
||||
if (HasIQSkill(pokemon, IQ_SKILL_ALL_TERRAIN_HIKER)) {
|
||||
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
||||
}
|
||||
if (HasIQSkill(pokemon, IQ_SKILL_SUPER_MOBILE)) {
|
||||
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
||||
}
|
||||
switch(crossableTerrain)
|
||||
{
|
||||
case CROSSABLE_TERRAIN_LIQUID:
|
||||
if(tileFlags == TILE_TYPE_LIQUID) return FALSE;
|
||||
case CROSSABLE_TERRAIN_REGULAR:
|
||||
if(tileFlags == TILE_TYPE_FLOOR) return FALSE;
|
||||
break;
|
||||
case CROSSABLE_TERRAIN_WALL:
|
||||
default:
|
||||
return FALSE;
|
||||
case CROSSABLE_TERRAIN_CREVICE:
|
||||
if(tileFlags != 0) return FALSE;
|
||||
}
|
||||
}
|
||||
else return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
s32 GetMovementSpeed(struct DungeonEntity *pokemon)
|
||||
{
|
||||
s32 index;
|
||||
s32 speed;
|
||||
struct DungeonEntityData * entityData;
|
||||
|
||||
entityData = pokemon->entityData;
|
||||
speed = 0;
|
||||
|
||||
for(index = 0; index < NUM_SPEED_TURN_COUNTERS; index++)
|
||||
{
|
||||
if (entityData->speedUpTurnsLeft[index + NUM_SPEED_TURN_COUNTERS] != 0) {
|
||||
speed--;
|
||||
}
|
||||
if (entityData->speedUpTurnsLeft[index] != 0) {
|
||||
speed++;
|
||||
}
|
||||
}
|
||||
|
||||
if (entityData->nonVolatileStatus == NON_VOLATILE_STATUS_PARALYZED) {
|
||||
speed--;
|
||||
}
|
||||
|
||||
speed += GetMoveSpeed(entityData->entityID);
|
||||
if ((HasType(pokemon, TYPE_ICE)) && (GetWeather(pokemon) == WEATHER_SNOW)) {
|
||||
speed++;
|
||||
}
|
||||
if (entityData->transformSpecies == SPECIES_DEOXYS_SPEED) {
|
||||
speed++;
|
||||
}
|
||||
if ((entityData->entityID == SPECIES_KECLEON) && entityData->isEnemy &&
|
||||
gDungeonGlobalData->unk66E) {
|
||||
speed++;
|
||||
}
|
||||
if (speed < 0) {
|
||||
speed = 0;
|
||||
}
|
||||
if (MAX_MOVEMENT_SPEED < speed) {
|
||||
speed = MAX_MOVEMENT_SPEED;
|
||||
}
|
||||
entityData->movementSpeed = speed;
|
||||
return speed;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ extern void GroundMap_GetStationScript(struct GroundScript_ExecutePP_3 *, s16, u
|
||||
bool8 GroundScript_ExecutePP(struct GroundScript_ExecutePP_1 *param_1, s32 *param_2, struct GroundScript_ExecutePP_3 *param_3, struct DebugLocation *unused);
|
||||
extern u8 sub_809D678(void *);
|
||||
extern void sub_809D710(u8 *, struct GroundScript_ExecutePP_3 *, s32);
|
||||
extern u8 sub_809D968(void *, s32);
|
||||
extern bool8 sub_809D968(void *, s32);
|
||||
|
||||
void sub_80A4BE8(struct GroundScript_ExecutePP_3 *script, s16 r1)
|
||||
{
|
||||
@@ -92,7 +92,7 @@ u8 sub_80A4D14(void)
|
||||
return sub_809D678(gUnknown_3001B6C);
|
||||
}
|
||||
|
||||
u8 sub_80A4D2C(s16 param_1)
|
||||
bool8 sub_80A4D2C(s16 param_1)
|
||||
{
|
||||
s32 iVar1 = param_1;
|
||||
return sub_809D968(gUnknown_3001B6C,iVar1);
|
||||
|
||||
@@ -37,11 +37,10 @@ struct FunctionScript
|
||||
extern struct FunctionScript gFunctionScriptTable[];
|
||||
|
||||
|
||||
// NOTE: shift of index and load of gFunctionScriptTable is flipped
|
||||
#ifdef NONMATCHING
|
||||
void sub_809D710(struct GroundScript_ExecutePP_1 *param_1, struct GroundScript_ExecutePP_3 *script, s16 index)
|
||||
{
|
||||
script->scriptPointer = gFunctionScriptTable[index].script;
|
||||
s32 index_s32 = index;
|
||||
script->scriptPointer = gFunctionScriptTable[index_s32].script;
|
||||
script->scriptType = 2;
|
||||
if (param_1 != NULL) {
|
||||
script->unk6 = param_1->unk10;
|
||||
@@ -52,46 +51,6 @@ void sub_809D710(struct GroundScript_ExecutePP_1 *param_1, struct GroundScript_E
|
||||
script->unk8 = -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
NAKED void sub_809D710(struct GroundScript_ExecutePP_1 *param_1, struct GroundScript_ExecutePP_3 *script, s16 index)
|
||||
{
|
||||
asm_unified(
|
||||
"\tpush {r4,lr}\n"
|
||||
"\tadds r4, r0, 0\n"
|
||||
"\tadds r3, r1, 0\n"
|
||||
"\tlsls r2, 16\n"
|
||||
"\tasrs r2, 16\n"
|
||||
"\tldr r1, _0809D73C\n"
|
||||
"\tlsls r0, r2, 1\n"
|
||||
"\tadds r0, r2\n"
|
||||
"\tlsls r0, 2\n"
|
||||
"\tadds r1, 0x8\n"
|
||||
"\tadds r0, r1\n"
|
||||
"\tldr r0, [r0]\n"
|
||||
"\tstr r0, [r3]\n"
|
||||
"\tmovs r0, 0x2\n"
|
||||
"\tstrh r0, [r3, 0x4]\n"
|
||||
"\tcmp r4, 0\n"
|
||||
"\tbeq _0809D740\n"
|
||||
"\tldrh r0, [r4, 0x10]\n"
|
||||
"\tstrh r0, [r3, 0x6]\n"
|
||||
"\tldrb r0, [r4, 0x12]\n"
|
||||
"\tb _0809D746\n"
|
||||
"\t.align 2, 0\n"
|
||||
"_0809D73C: .4byte gFunctionScriptTable\n"
|
||||
"_0809D740:\n"
|
||||
"\tldr r0, _0809D750\n"
|
||||
"\tstrh r0, [r3, 0x6]\n"
|
||||
"\tmovs r0, 0xFF\n"
|
||||
"_0809D746:\n"
|
||||
"\tstrb r0, [r3, 0x8]\n"
|
||||
"\tpop {r4}\n"
|
||||
"\tpop {r0}\n"
|
||||
"\tbx r0\n"
|
||||
"\t.align 2, 0\n"
|
||||
"_0809D750: .4byte 0x0000ffff");
|
||||
}
|
||||
#endif
|
||||
|
||||
bool8 sub_809D754(struct GroundScript_ExecutePP_1 *param_1, struct DebugLocation *unused)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "constants/ability.h"
|
||||
#include "constants/iq_skill.h"
|
||||
#include "constants/status.h"
|
||||
#include "constants/type.h"
|
||||
#include "constants/weather.h"
|
||||
#include "dungeon_engine.h"
|
||||
#include "dungeon_global_data.h"
|
||||
@@ -27,128 +26,6 @@ extern u8 *gUnknown_80F8968[];
|
||||
extern u8 *gUnknown_80F8988[];
|
||||
extern u8 gUnknown_8106FA4[];
|
||||
|
||||
bool8 sub_80705F0(struct DungeonEntity *pokemon, struct Position *pos)
|
||||
{
|
||||
s32 crossableTerrain;
|
||||
struct MapTile *tile;
|
||||
u16 tileFlags;
|
||||
struct DungeonEntityData *entityData;
|
||||
|
||||
entityData = pokemon->entityData;
|
||||
tile = GetMapTile_1(pos->x, pos->y);
|
||||
if ((pos->x >= 0) && (pos->y >= 0) && (DUNGEON_MAX_SIZE_X > pos->x) &&
|
||||
(DUNGEON_MAX_SIZE_Y > pos->y && ((tile->tileType & TILE_TYPE_MAP_EDGE) == 0)) &&
|
||||
((tile->pokemon == NULL) || ((GetEntityType(tile->pokemon) == ENTITY_POKEMON)))) {
|
||||
if (IsFixedDungeon() || (entityData->transformStatus != TRANSFORM_STATUS_MOBILE && !HasItem(pokemon, ITEM_ID_MOBILE_SCARF))) {
|
||||
crossableTerrain = GetCrossableTerrain(entityData->entityID);
|
||||
tileFlags = tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID);
|
||||
if (HasIQSkill(pokemon, IQ_SKILL_ALL_TERRAIN_HIKER)) {
|
||||
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
||||
}
|
||||
if (HasIQSkill(pokemon, IQ_SKILL_SUPER_MOBILE)) {
|
||||
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
||||
}
|
||||
|
||||
switch(crossableTerrain)
|
||||
{
|
||||
case CROSSABLE_TERRAIN_WALL:
|
||||
default:
|
||||
return FALSE;
|
||||
case CROSSABLE_TERRAIN_REGULAR:
|
||||
case CROSSABLE_TERRAIN_LIQUID:
|
||||
case CROSSABLE_TERRAIN_CREVICE:
|
||||
if(tileFlags != 0) return FALSE;
|
||||
}
|
||||
}
|
||||
else return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool8 sub_80706A4(struct DungeonEntity *pokemon, struct Position *pos)
|
||||
{
|
||||
s32 crossableTerrain;
|
||||
struct MapTile *tile;
|
||||
u16 tileFlags;
|
||||
struct DungeonEntityData *entityData;
|
||||
|
||||
entityData = pokemon->entityData;
|
||||
tile = GetMapTile_1(pos->x, pos->y);
|
||||
if ((pos->x >= 0) && (pos->y >= 0) && (DUNGEON_MAX_SIZE_X > pos->x) &&
|
||||
(DUNGEON_MAX_SIZE_Y > pos->y && ((tile->tileType & TILE_TYPE_MAP_EDGE) == 0)) &&
|
||||
((tile->pokemon == NULL) || ((GetEntityType(tile->pokemon) == ENTITY_POKEMON) && (tile->pokemon->entityData == entityData)))) {
|
||||
if (IsFixedDungeon() || (entityData->transformStatus != TRANSFORM_STATUS_MOBILE && !HasItem(pokemon, ITEM_ID_MOBILE_SCARF))) {
|
||||
crossableTerrain = GetCrossableTerrain(entityData->entityID);
|
||||
tileFlags = tile->tileType & (TILE_TYPE_FLOOR | TILE_TYPE_LIQUID);
|
||||
if (HasIQSkill(pokemon, IQ_SKILL_ALL_TERRAIN_HIKER)) {
|
||||
crossableTerrain = CROSSABLE_TERRAIN_CREVICE;
|
||||
}
|
||||
if (HasIQSkill(pokemon, IQ_SKILL_SUPER_MOBILE)) {
|
||||
crossableTerrain = CROSSABLE_TERRAIN_WALL;
|
||||
}
|
||||
switch(crossableTerrain)
|
||||
{
|
||||
case CROSSABLE_TERRAIN_LIQUID:
|
||||
if(tileFlags == TILE_TYPE_LIQUID) return FALSE;
|
||||
case CROSSABLE_TERRAIN_REGULAR:
|
||||
if(tileFlags == TILE_TYPE_FLOOR) return FALSE;
|
||||
break;
|
||||
case CROSSABLE_TERRAIN_WALL:
|
||||
default:
|
||||
return FALSE;
|
||||
case CROSSABLE_TERRAIN_CREVICE:
|
||||
if(tileFlags != 0) return FALSE;
|
||||
}
|
||||
}
|
||||
else return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
s32 GetMovementSpeed(struct DungeonEntity *pokemon)
|
||||
{
|
||||
s32 index;
|
||||
s32 speed;
|
||||
struct DungeonEntityData * entityData;
|
||||
|
||||
entityData = pokemon->entityData;
|
||||
speed = 0;
|
||||
|
||||
for(index = 0; index < NUM_SPEED_TURN_COUNTERS; index++)
|
||||
{
|
||||
if (entityData->speedUpTurnsLeft[index + NUM_SPEED_TURN_COUNTERS] != 0) {
|
||||
speed--;
|
||||
}
|
||||
if (entityData->speedUpTurnsLeft[index] != 0) {
|
||||
speed++;
|
||||
}
|
||||
}
|
||||
|
||||
if (entityData->nonVolatileStatus == NON_VOLATILE_STATUS_PARALYZED) {
|
||||
speed--;
|
||||
}
|
||||
|
||||
speed += GetMoveSpeed(entityData->entityID);
|
||||
if ((HasType(pokemon, TYPE_ICE)) && (GetWeather(pokemon) == WEATHER_SNOW)) {
|
||||
speed++;
|
||||
}
|
||||
if (entityData->transformSpecies == SPECIES_DEOXYS_SPEED) {
|
||||
speed++;
|
||||
}
|
||||
if ((entityData->entityID == SPECIES_KECLEON) && entityData->isEnemy &&
|
||||
gDungeonGlobalData->unk66E) {
|
||||
speed++;
|
||||
}
|
||||
if (speed < 0) {
|
||||
speed = 0;
|
||||
}
|
||||
if (MAX_MOVEMENT_SPEED < speed) {
|
||||
speed = MAX_MOVEMENT_SPEED;
|
||||
}
|
||||
entityData->movementSpeed = speed;
|
||||
return speed;
|
||||
}
|
||||
|
||||
u8 sub_8070828(struct DungeonEntity *pokemon, bool8 displayMessage)
|
||||
{
|
||||
bool8 flag;
|
||||
|
||||
Reference in New Issue
Block a user