Split monster immunities bitfield into more comprehensible chunks.
- Monsters now properly decide whether they can enter damaging terrains - The possibility of being unusually vulnerable to a damage type is introduce - such monsters would take double damage from that type
This commit is contained in:
@@ -21,14 +21,14 @@
|
||||
<text name='priest' framed='true' top='137' left='188' width='25' height='17'/>
|
||||
<text name='poison' framed='true' top='137' left='280' width='25' height='17'/>
|
||||
<text name='abil1' framed='true' top='165' left='67' width='238' height='16'/>
|
||||
<led name='immune1' state='off' top='227' left='118'/>
|
||||
<led name='immune2' state='off' top='242' left='118'/>
|
||||
<led name='immune3' state='off' top='257' left='118'/>
|
||||
<led name='immune4' state='off' top='272' left='118'/>
|
||||
<led name='immune5' state='off' top='227' left='241'/>
|
||||
<led name='immune6' state='off' top='242' left='241'/>
|
||||
<led name='immune7' state='off' top='257' left='241'/>
|
||||
<led name='immune8' state='off' top='272' left='241'/>
|
||||
<led name='magic-res' state='off' top='227' left='118'/>
|
||||
<led name='magic-imm' state='off' top='242' left='118'/>
|
||||
<led name='fire-res' state='off' top='257' left='118'/>
|
||||
<led name='fire-imm' state='off' top='272' left='118'/>
|
||||
<led name='cold-res' state='off' top='227' left='241'/>
|
||||
<led name='cold-imm' state='off' top='242' left='241'/>
|
||||
<led name='poison-res' state='off' top='257' left='241'/>
|
||||
<led name='poison-imm' state='off' top='272' left='241'/>
|
||||
<button name='left' type='left' def-key='left' top='319' left='11'/>
|
||||
<button name='right' type='right' def-key='right' top='319' left='74'/>
|
||||
<text top='288' left='10' width='305' height='28'>(To make monsters appear in roster menu, cast Scry Monster on them.)</text>
|
||||
|
||||
@@ -410,17 +410,15 @@ static void put_monst_info(cDialog& me, const cCreature& store_m) {
|
||||
me["mage"].setTextToNum(store_m.mu);
|
||||
me["priest"].setTextToNum(store_m.cl);
|
||||
me["poison"].setTextToNum(store_m.poison);
|
||||
// 2140 - lit 2141 - dark
|
||||
// immunities
|
||||
for(i = 0; i < 8; i++) {
|
||||
std::string id = "immune" + boost::lexical_cast<std::string>(i + 1);
|
||||
cLed& led = dynamic_cast<cLed&>(me[id]);
|
||||
// TODO: What's this s_pow nonsense? Isn't this just 1 << i? Also, why not make it a C++ bitfield?
|
||||
if(store_m.immunities & (char)(s_pow(2,i)))
|
||||
led.setState(led_red);
|
||||
else led.setState(led_off);
|
||||
}
|
||||
|
||||
// Immunities
|
||||
dynamic_cast<cLed&>(me["magic-res"]).setState(store_m.magic_res == RESIST_HALF ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["magic-imm"]).setState(store_m.magic_res == RESIST_ALL ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["fire-res"]).setState(store_m.fire_res == RESIST_HALF ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["fire-imm"]).setState(store_m.fire_res == RESIST_ALL ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["cold-res"]).setState(store_m.cold_res == RESIST_HALF ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["cold-imm"]).setState(store_m.cold_res == RESIST_ALL ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["poison-res"]).setState(store_m.poison_res == RESIST_HALF ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["poison-imm"]).setState(store_m.poison_res == RESIST_ALL ? led_red : led_off);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -280,20 +280,50 @@ bool monst_hate_spot(short which_m,location *good_loc) {
|
||||
location prospect,loc;
|
||||
|
||||
loc = univ.town.monst[which_m].cur_loc;
|
||||
if(univ.town.is_fire_barr(loc.x,loc.y) || univ.town.is_force_barr(loc.x,loc.y) || univ.town.is_quickfire(loc.x,loc.y)
|
||||
|| univ.town.is_blade_wall(loc.x,loc.y) // hate regular fields
|
||||
|| (univ.town.is_ice_wall(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 2)
|
||||
&& ((univ.town.monst[which_m].immunities & 32) == 0)) // hate ice wall?
|
||||
|| (univ.town.is_fire_wall(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 1)
|
||||
&& ((univ.town.monst[which_m].immunities & 8) == 0)) // hate fire wall?
|
||||
|| (univ.town.is_scloud(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 6)
|
||||
&& ((univ.town.monst[which_m].immunities & 3) == 0)) // hate stink cloud?
|
||||
|| (univ.town.is_sleep_cloud(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 5)
|
||||
&& ((univ.town.monst[which_m].immunities & 3) == 0)) // hate sleep cloud?
|
||||
|| (univ.town.is_force_wall(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 3)
|
||||
&& ((univ.town.monst[which_m].immunities & 3) == 0)) // hate shock cloud?
|
||||
|| (((univ.town.monst[which_m].mu > 0) || (univ.town.monst[which_m].cl > 0))
|
||||
&& univ.town.is_antimagic(loc.x,loc.y))) { // hate antimagic
|
||||
bool hate_spot = false;
|
||||
// Hate barriers
|
||||
if(univ.town.is_fire_barr(loc.x,loc.y) || univ.town.is_force_barr(loc.x,loc.y)) hate_spot = true;
|
||||
// Hate regular fields
|
||||
else if(univ.town.is_quickfire(loc.x,loc.y) || univ.town.is_blade_wall(loc.x,loc.y)) hate_spot = true;
|
||||
// Hate ice wall?
|
||||
else if(univ.town.is_ice_wall(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == 2) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].cold_res == RESIST_ALL) hate_spot = false;
|
||||
}
|
||||
// Hate fire wall?
|
||||
else if(univ.town.is_fire_wall(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == 1) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].fire_res == RESIST_ALL) hate_spot = false;
|
||||
}
|
||||
// Note: Monsters used to enter shock walls even if they were merely resistant to magic
|
||||
// Hate shock wall?
|
||||
else if(univ.town.is_force_wall(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == 3) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_ALL) hate_spot = false;
|
||||
}
|
||||
// Hate stink cloud?
|
||||
else if(univ.town.is_scloud(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == 6) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_ALL) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_HALF) hate_spot = false;
|
||||
}
|
||||
// Hate sleep cloud?
|
||||
else if(univ.town.is_sleep_cloud(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == 5) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_ALL) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_HALF) hate_spot = false;
|
||||
}
|
||||
// Hate antimagic?
|
||||
else if(univ.town.is_antimagic(loc.x,loc.y)) {
|
||||
if(univ.town.monst[which_m].mu > 0 || univ.town.monst[which_m].cl > 0)
|
||||
hate_spot = true;
|
||||
}
|
||||
if(hate_spot) {
|
||||
prospect = find_clear_spot(loc,1);
|
||||
if(prospect.x > 0) {
|
||||
*good_loc = prospect;
|
||||
@@ -1006,10 +1036,18 @@ bool monst_check_special_terrain(location where_check,short mode,short which_mon
|
||||
break;
|
||||
|
||||
case eTerSpec::DAMAGING: // TODO: Update this to check other cases
|
||||
if(eDamageType(ter_flag) == eDamageType::FIRE && univ.town.monst[which_monst].immunities & 8)
|
||||
return true;
|
||||
else return false;
|
||||
break;
|
||||
switch(eDamageType(ter_flag)) {
|
||||
case eDamageType::FIRE:
|
||||
return univ.town.monst[which_monst].fire_res == RESIST_ALL;
|
||||
case eDamageType::COLD:
|
||||
return univ.town.monst[which_monst].cold_res == RESIST_ALL;
|
||||
case eDamageType::MAGIC:
|
||||
return univ.town.monst[which_monst].magic_res == RESIST_ALL;
|
||||
case eDamageType::POISON:
|
||||
return univ.town.monst[which_monst].poison_res == RESIST_ALL;
|
||||
default:
|
||||
return univ.town.monst[which_monst].spec_skill == MONSTER_INVULNERABILITY;
|
||||
}
|
||||
// TODO: Should it check any other terrain specials?
|
||||
}
|
||||
|
||||
@@ -1051,18 +1089,30 @@ void magic_adjust(cCreature *which_m,short *how_much) {
|
||||
which_m->health = 32767;
|
||||
else which_m->health += 3;
|
||||
}
|
||||
if(which_m->immunities & 1)
|
||||
*how_much = *how_much / 2;
|
||||
if(which_m->immunities & 2)
|
||||
*how_much = 0;
|
||||
switch(which_m->magic_res) {
|
||||
case RESIST_HALF:
|
||||
*how_much /= 2;
|
||||
break;
|
||||
case RESIST_ALL:
|
||||
*how_much = 0;
|
||||
break;
|
||||
case RESIST_DOUBLE:
|
||||
*how_much *= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void poison_monst(cCreature *which_m,short how_much) {
|
||||
if(which_m->immunities & 64)
|
||||
how_much = how_much / 2;
|
||||
if(which_m->immunities & 128) {
|
||||
monst_spell_note(which_m->number,10);
|
||||
return;
|
||||
switch(which_m->poison_res) {
|
||||
case RESIST_HALF:
|
||||
how_much /= 2;
|
||||
break;
|
||||
case RESIST_ALL:
|
||||
monst_spell_note(which_m->number,10);
|
||||
return;
|
||||
case RESIST_DOUBLE:
|
||||
how_much *= 2;
|
||||
break;
|
||||
}
|
||||
which_m->status[eStatus::POISON] = min(8, which_m->status[eStatus::POISON] + how_much);
|
||||
if(how_much >= 0)
|
||||
@@ -1147,10 +1197,17 @@ void charm_monst(cCreature *which_m,short penalty,eStatus which_status,short amo
|
||||
which_m->m_type == eRace::STONE || which_m->m_type == eRace::PLANT))
|
||||
return;
|
||||
r1 = get_ran(1,1,100);
|
||||
if(which_m->immunities & 1)
|
||||
r1 = r1 * 2;
|
||||
if(which_m->immunities & 2)
|
||||
r1 = 200;
|
||||
switch(which_m->magic_res) {
|
||||
case RESIST_HALF:
|
||||
r1 *= 2;
|
||||
break;
|
||||
case RESIST_ALL:
|
||||
r1 = 200;
|
||||
break;
|
||||
case RESIST_DOUBLE:
|
||||
r1 /= 2;
|
||||
break;
|
||||
}
|
||||
r1 += penalty;
|
||||
if(which_status == eStatus::ASLEEP)
|
||||
r1 -= 25;
|
||||
|
||||
@@ -1391,8 +1391,6 @@ bool damage_monst(short which_m, short who_hit, short how_much, short how_much_s
|
||||
short r1,which_spot;
|
||||
location where_put;
|
||||
|
||||
char resist;
|
||||
|
||||
//print_num(which_m,(short)univ.town.monst[which_m].m_loc.x,(short)univ.town.monst[which_m].m_loc.y);
|
||||
|
||||
if(univ.town.monst[which_m].active == 0) return false;
|
||||
@@ -1408,33 +1406,59 @@ bool damage_monst(short which_m, short who_hit, short how_much, short how_much_s
|
||||
sound_type = 11;
|
||||
}
|
||||
|
||||
|
||||
victim = &univ.town.monst[which_m];
|
||||
resist = victim->immunities;
|
||||
|
||||
if(dam_type == eDamageType::MAGIC) {
|
||||
if(resist & 1)
|
||||
how_much = how_much / 2;
|
||||
if(resist & 2)
|
||||
how_much = 0;
|
||||
switch(victim->magic_res) {
|
||||
case RESIST_HALF:
|
||||
how_much /= 2;
|
||||
break;
|
||||
case RESIST_ALL:
|
||||
how_much = 0;
|
||||
break;
|
||||
case RESIST_DOUBLE:
|
||||
how_much *= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(dam_type == eDamageType::FIRE) {
|
||||
if(resist & 4)
|
||||
how_much = how_much / 2;
|
||||
if(resist & 8)
|
||||
how_much = 0;
|
||||
switch(victim->fire_res) {
|
||||
case RESIST_HALF:
|
||||
how_much /= 2;
|
||||
break;
|
||||
case RESIST_ALL:
|
||||
how_much = 0;
|
||||
break;
|
||||
case RESIST_DOUBLE:
|
||||
how_much *= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(dam_type == eDamageType::COLD) {
|
||||
if(resist & 16)
|
||||
how_much = how_much / 2;
|
||||
if(resist & 32)
|
||||
how_much = 0;
|
||||
switch(victim->cold_res) {
|
||||
case RESIST_HALF:
|
||||
how_much /= 2;
|
||||
break;
|
||||
case RESIST_ALL:
|
||||
how_much = 0;
|
||||
break;
|
||||
case RESIST_DOUBLE:
|
||||
how_much *= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(dam_type == eDamageType::POISON) {
|
||||
if(resist & 64)
|
||||
how_much = how_much / 2;
|
||||
if(resist & 128)
|
||||
how_much = 0;
|
||||
switch(victim->poison_res) {
|
||||
case RESIST_HALF:
|
||||
how_much /= 2;
|
||||
break;
|
||||
case RESIST_ALL:
|
||||
how_much = 0;
|
||||
break;
|
||||
case RESIST_DOUBLE:
|
||||
how_much *= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Absorb damage?
|
||||
@@ -1576,7 +1600,7 @@ void petrify_monst(cCreature* m_target, short strength) {
|
||||
r1 += m_target->status[eStatus::BLESS_CURSE];
|
||||
r1 -= strength;
|
||||
|
||||
if(r1 > 14 || m_target->immunities & 2)
|
||||
if(r1 > 14 || m_target->magic_res == RESIST_ALL)
|
||||
monst_spell_note(m_target->number,10);
|
||||
else {
|
||||
monst_spell_note(m_target->number,8);
|
||||
|
||||
@@ -39,7 +39,22 @@ void cMonster::append(legacy::monster_record_type& old){
|
||||
poison = old.poison;
|
||||
corpse_item = old.corpse_item;
|
||||
corpse_item_chance = old.corpse_item_chance;
|
||||
immunities = old.immunities;
|
||||
if(old.immunities & 2)
|
||||
magic_res = RESIST_ALL;
|
||||
else if(old.immunities & 1)
|
||||
magic_res = RESIST_HALF;
|
||||
if(old.immunities & 8)
|
||||
fire_res = RESIST_ALL;
|
||||
else if(old.immunities & 4)
|
||||
fire_res = RESIST_HALF;
|
||||
if(old.immunities & 32)
|
||||
cold_res = RESIST_ALL;
|
||||
else if(old.immunities & 16)
|
||||
cold_res = RESIST_HALF;
|
||||
if(old.immunities & 128)
|
||||
poison_res = RESIST_ALL;
|
||||
else if(old.immunities & 64)
|
||||
poison_res = RESIST_HALF;
|
||||
x_width = old.x_width;
|
||||
y_width = old.y_width;
|
||||
radiate_1 = old.radiate_1;
|
||||
@@ -488,7 +503,7 @@ void cMonster::writeTo(std::ostream& file) const {
|
||||
file << "ABIL 2 " << int(radiate_1) << ' ' << int(radiate_2) << " 0\n";
|
||||
file << "POISON " << int(poison) << '\n';
|
||||
file << "CORPSEITEM " << corpse_item << ' ' << corpse_item_chance << '\n';
|
||||
file << "IMMUNE " << int(immunities) << '\n';
|
||||
file << "IMMUNE " << magic_res << '\t' << fire_res << '\t' << cold_res << '\t' << poison_res << '\n';
|
||||
file << "SIZE " << int(x_width) << ' ' << int(y_width) << '\n';
|
||||
file << "ATTITUDE " << int(default_attitude) << '\n';
|
||||
file << "SUMMON " << int(summon_type) << '\n';
|
||||
@@ -530,10 +545,17 @@ void cMonster::readFrom(std::istream& file) {
|
||||
spec_skill = temp1;
|
||||
else if(which == 2)
|
||||
radiate_1 = temp1, radiate_2 = temp2;
|
||||
} else if(cur == "SIZE") {
|
||||
} else if(cur == "SIZE") {
|
||||
line >> temp1 >> temp2;
|
||||
x_width = temp1;
|
||||
y_width = temp2;
|
||||
} else if(cur == "IMMUNE") {
|
||||
line >> temp1 >> temp2;
|
||||
magic_res = temp1;
|
||||
fire_res = temp2;
|
||||
line >> temp1 >> temp2;
|
||||
cold_res = temp1;
|
||||
poison_res = temp2;
|
||||
} else if(cur == "RACE")
|
||||
line >> m_type;
|
||||
else if(cur == "CORPSEITEM")
|
||||
@@ -564,8 +586,6 @@ void cMonster::readFrom(std::istream& file) {
|
||||
treasure = temp1;
|
||||
else if(cur == "POISON")
|
||||
poison = temp1;
|
||||
else if(cur == "IMMUNITIES")
|
||||
immunities = temp1;
|
||||
else if(cur == "ATTITUDE")
|
||||
default_attitude = temp1;
|
||||
else if(cur == "SUMMON")
|
||||
|
||||
@@ -113,6 +113,8 @@ inline eDirection& operator++ (eDirection& me, int) {
|
||||
else return me = (eDirection)(1 + (int)me);
|
||||
}
|
||||
|
||||
enum eResistType {RESIST_NONE, RESIST_HALF, RESIST_ALL, RESIST_DOUBLE};
|
||||
|
||||
class cMonster {
|
||||
public:
|
||||
struct cAttack{
|
||||
@@ -143,7 +145,10 @@ public:
|
||||
unsigned char poison;
|
||||
item_num_t corpse_item;
|
||||
short corpse_item_chance;
|
||||
unsigned char immunities;
|
||||
int magic_res : 2;
|
||||
int fire_res : 2;
|
||||
int cold_res : 2;
|
||||
int poison_res : 2;
|
||||
unsigned char x_width,y_width;
|
||||
unsigned char radiate_1; // TODO: Delete in favour of cAbility
|
||||
unsigned short radiate_2; // I THINK this is the extra field for the second ability TODO: Delete in favour of cAbility
|
||||
|
||||
@@ -573,11 +573,6 @@ short edit_monst_type(short which_monst) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const std::string resist_field_names[8] = {
|
||||
"magic-res", "fire-res", "cold-res", "poison-res",
|
||||
"magic-imm", "fire-imm", "cold-imm", "poison-imm",
|
||||
};
|
||||
|
||||
static void put_monst_abils_in_dlog(cDialog& me, cMonster& store_monst, short which_monst) {
|
||||
me["num"].setTextToNum(which_monst);
|
||||
|
||||
@@ -609,12 +604,14 @@ static void put_monst_abils_in_dlog(cDialog& me, cMonster& store_monst, short wh
|
||||
|
||||
dynamic_cast<cLedGroup&>(me["summon"]).setSelected("s" + boost::lexical_cast<std::string,short>(store_monst.summon_type));
|
||||
|
||||
for(short i = 0; i < 8; i++) {
|
||||
cLed& resistLed = dynamic_cast<cLed&>(me[resist_field_names[i]]);
|
||||
if(store_monst.immunities & (1 << i))
|
||||
resistLed.setState(led_red);
|
||||
else resistLed.setState(led_off);
|
||||
}
|
||||
dynamic_cast<cLed&>(me["magic-res"]).setState(store_monst.magic_res == RESIST_HALF ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["magic-imm"]).setState(store_monst.magic_res == RESIST_ALL ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["fire-res"]).setState(store_monst.fire_res == RESIST_HALF ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["fire-imm"]).setState(store_monst.fire_res == RESIST_ALL ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["cold-res"]).setState(store_monst.cold_res == RESIST_HALF ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["cold-imm"]).setState(store_monst.cold_res == RESIST_ALL ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["poison-res"]).setState(store_monst.poison_res == RESIST_HALF ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["poison-imm"]).setState(store_monst.poison_res == RESIST_ALL ? led_red : led_off);
|
||||
}
|
||||
|
||||
static bool save_monst_abils(cDialog& me, cMonster& store_monst) {
|
||||
@@ -634,11 +631,14 @@ static bool save_monst_abils(cDialog& me, cMonster& store_monst) {
|
||||
|
||||
store_monst.corpse_item = me["loot-item"].getTextAsNum();
|
||||
store_monst.corpse_item_chance = me["loot-chance"].getTextAsNum();
|
||||
store_monst.immunities = 0;
|
||||
for(short i = 0; i < 8; i++) {
|
||||
if(dynamic_cast<cLed&>(me[resist_field_names[i]]).getState() != led_off)
|
||||
store_monst.immunities |= 1 << i;
|
||||
}
|
||||
if(dynamic_cast<cLed&>(me["magic-res"]).getState() != led_off) store_monst.magic_res = RESIST_HALF;
|
||||
if(dynamic_cast<cLed&>(me["magic-imm"]).getState() != led_off) store_monst.magic_res = RESIST_ALL;
|
||||
if(dynamic_cast<cLed&>(me["fire-res"]).getState() != led_off) store_monst.fire_res = RESIST_HALF;
|
||||
if(dynamic_cast<cLed&>(me["fire-imm"]).getState() != led_off) store_monst.fire_res = RESIST_ALL;
|
||||
if(dynamic_cast<cLed&>(me["cold-res"]).getState() != led_off) store_monst.cold_res = RESIST_HALF;
|
||||
if(dynamic_cast<cLed&>(me["cold-imm"]).getState() != led_off) store_monst.cold_res = RESIST_ALL;
|
||||
if(dynamic_cast<cLed&>(me["poison-res"]).getState() != led_off) store_monst.poison_res = RESIST_HALF;
|
||||
if(dynamic_cast<cLed&>(me["poison-imm"]).getState() != led_off) store_monst.poison_res = RESIST_ALL;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user