First flip management
This commit is contained in:
parent
8cd9d638b9
commit
05a2b02482
@ -18,7 +18,7 @@
|
|||||||
"STATS.physical": "physical",
|
"STATS.physical": "physical",
|
||||||
"STATS.mental": "mental",
|
"STATS.mental": "mental",
|
||||||
"STATS.strength": "Strength",
|
"STATS.strength": "Strength",
|
||||||
"STATS.dexterity": "Dextrity",
|
"STATS.dexterity": "Dexterity",
|
||||||
"STATS.speed": "Speed",
|
"STATS.speed": "Speed",
|
||||||
"STATS.endurance": "Endurance",
|
"STATS.endurance": "Endurance",
|
||||||
"STATS.intelligence": "Intelligence",
|
"STATS.intelligence": "Intelligence",
|
||||||
|
@ -75,6 +75,11 @@ export class SoSActorSheet extends ActorSheet {
|
|||||||
let statName = event.currentTarget.attributes.name.value;
|
let statName = event.currentTarget.attributes.name.value;
|
||||||
this.actor.rollStat(statName);
|
this.actor.rollStat(statName);
|
||||||
});
|
});
|
||||||
|
html.find('.skill-label a').click((event) => {
|
||||||
|
const li = $(event.currentTarget).parents(".item");
|
||||||
|
const skill = this.actor.getOwnedItem(li.data("item-id"));
|
||||||
|
this.actor.rollSkill(skill);
|
||||||
|
});
|
||||||
html.find('.edge-draw').click((event) => {
|
html.find('.edge-draw').click((event) => {
|
||||||
this.actor.resetDeck();
|
this.actor.resetDeck();
|
||||||
this.render(true);
|
this.render(true);
|
||||||
|
@ -141,7 +141,8 @@ export class SoSActor extends Actor {
|
|||||||
mode: 'stat',
|
mode: 'stat',
|
||||||
stat: duplicate(this.data.data.stats[statKey]),
|
stat: duplicate(this.data.data.stats[statKey]),
|
||||||
actor: this,
|
actor: this,
|
||||||
modifierList: SoSUtility.fillRange(-10, +10)
|
modifierList: SoSUtility.fillRange(-10, +10),
|
||||||
|
tnList: SoSUtility.fillRange(6, 20)
|
||||||
}
|
}
|
||||||
let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData);
|
let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData);
|
||||||
new SoSFlipDialog(flipData, html).render(true);
|
new SoSFlipDialog(flipData, html).render(true);
|
||||||
@ -149,5 +150,18 @@ export class SoSActor extends Actor {
|
|||||||
//console.log("STAT", this);
|
//console.log("STAT", this);
|
||||||
//let result = this.cardDeck.doFlipStat( duplicate(this.data.data.stat[statKey]) );
|
//let result = this.cardDeck.doFlipStat( duplicate(this.data.data.stat[statKey]) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async rollSkill( skill ) {
|
||||||
|
let flipData = {
|
||||||
|
mode: 'skill',
|
||||||
|
statList: duplicate(this.data.data.stats),
|
||||||
|
skill: duplicate(skill),
|
||||||
|
actor: this,
|
||||||
|
modifierList: SoSUtility.fillRange(-10, +10),
|
||||||
|
tnList: SoSUtility.fillRange(6, 20)
|
||||||
|
}
|
||||||
|
let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData);
|
||||||
|
new SoSFlipDialog(flipData, html).render(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,24 +71,145 @@ export class SoSCardDeck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
doFlipFromDeck( ) {
|
getCardSuit( cardName ) {
|
||||||
|
if ( cardName[0] == 'c') return 'club';
|
||||||
|
if ( cardName[0] == 'd') return 'diamond';
|
||||||
|
if ( cardName[0] == 'h') return 'hearth';
|
||||||
|
if ( cardName[0] == 's') return 'spade';
|
||||||
|
if ( cardName[0] == 'j') return 'joker';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
drawFromDeck() {
|
||||||
let card = this.data.deck.pop();
|
let card = this.data.deck.pop();
|
||||||
this.data.discard.push( card );
|
this.data.discard.push( card );
|
||||||
console.log("CARD IS : ", card, this.data.deck.length );
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
getFromEdge( cardName) {
|
||||||
|
let card = this.data.cardEdge.find( card => card.cardName == cardName); // Get the card
|
||||||
|
let newEdge = this.data.cardEdge.filter(card => card.cardName != cardName); // Remove used card
|
||||||
|
this.data.cardEdge = newEdge;
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
getCardValue( cardName ) {
|
||||||
|
console.log(cardName);
|
||||||
|
let parsed = cardName.match( /\w(\d\d)/i );
|
||||||
|
let value = Number( parsed[1] );
|
||||||
|
if ( value > 10 ) value -= 10;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
isCardFace(cardName) {
|
||||||
|
let parsed = cardName.match( /\w(\d\d)/i );
|
||||||
|
let value = Number( parsed[1] );
|
||||||
|
return (value > 10) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
async doFlipFromDeckOrEdge( flipData ) {
|
||||||
|
flipData.cardSlot = [ { total: 0}];
|
||||||
|
flipData.isTrump = false;
|
||||||
|
flipData.isJoker = false;
|
||||||
|
flipData.fullTrump = false;
|
||||||
|
|
||||||
|
// Select card origin
|
||||||
|
if ( flipData.cardOrigin == "Deck") {
|
||||||
|
flipData.cardSlot[0].card1 = this.drawFromDeck();
|
||||||
|
} else {
|
||||||
|
flipData.cardSlot[0].card1 = this.getFromEdge( flipData.edgeName );
|
||||||
|
}
|
||||||
|
|
||||||
|
let cardsuit = this.getCardSuit(flipData.cardSlot[0].card1.cardName);
|
||||||
|
if ( cardsuit == 'joker' ) {
|
||||||
|
console.log("THIS IS A JOKER !!!!");
|
||||||
|
flipData.cardSlot[0].total = 0;
|
||||||
|
flipData.cardSlot[0].isJoker = true;
|
||||||
|
flipData.cardSlot[0].card1Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[0].card1.cardName}.webp`;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
console.log("First card : ", flipData.cardSlot[0].card1);
|
||||||
|
// Face check for first card
|
||||||
|
flipData.cardSlot[0].value1 = this.getCardValue(flipData.cardSlot[0].card1.cardName);
|
||||||
|
flipData.cardSlot[0].isFace1 = this.isCardFace(flipData.cardSlot[0].card1.cardName);
|
||||||
|
flipData.cardSlot[0].card1Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[0].card1.cardName}.webp`;
|
||||||
|
flipData.cardSlot[0].card2 = false;
|
||||||
|
if ( flipData.cardSlot[0].isFace1 ) {
|
||||||
|
flipData.cardSlot[0].card2 = this.drawFromDeck();
|
||||||
|
flipData.cardSlot[0].value2 = this.getCardValue(flipData.cardSlot[0].card2.cardName);
|
||||||
|
flipData.cardSlot[0].isFace2 = this.isCardFace(flipData.cardSlot[0].card2.cardName);
|
||||||
|
flipData.cardSlot[0].card2Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[0].card2.cardName}.webp`;
|
||||||
|
} else {
|
||||||
|
flipData.cardSlot[0].value2 = 0; // Safe init
|
||||||
|
}
|
||||||
|
flipData.cardSlot[0].total = flipData.cardSlot[0].value1 + flipData.cardSlot[0].value2;
|
||||||
|
|
||||||
|
// Trump check
|
||||||
|
flipData.cardSlot[0].cardsuit = cardsuit;
|
||||||
|
if ( cardsuit == flipData.stat.cardsuit ) {
|
||||||
|
// This is a trump !
|
||||||
|
flipData.cardSlot[1] = { total: 0 };
|
||||||
|
flipData.isTrump = true;
|
||||||
|
flipData.cardSlot[1].card1 = this.drawFromDeck();
|
||||||
|
flipData.cardSlot[1].card1Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[1].card1.cardName}.webp`;
|
||||||
|
flipData.cardSlot[1].cardsuit = this.getCardSuit(flipData.cardSlot[1].card1.cardName);
|
||||||
|
flipData.cardSlot[1].value1 = this.getCardValue(flipData.cardSlot[1].card1.cardName);
|
||||||
|
flipData.cardSlot[1].isFace1 = this.isCardFace(flipData.cardSlot[1].card1.cardName);
|
||||||
|
if ( flipData.cardSlot[1].isFace1 ) {
|
||||||
|
flipData.cardSlot[1].card2 = this.drawFromDeck();
|
||||||
|
flipData.cardSlot[1].value2 = this.getCardValue(flipData.cardSlot[1].card2.cardName);
|
||||||
|
flipData.cardSlot[1].isFace2 = this.isCardFace(flipData.cardSlot[1].card2.cardName);
|
||||||
|
flipData.cardSlot[1].card2Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[1].card2.cardName}.webp`;
|
||||||
|
} else {
|
||||||
|
flipData.cardSlot[1].value2 = 0; // Safe init
|
||||||
|
}
|
||||||
|
if ( flipData.cardSlot[1].cardsuit == cardsuit ) {
|
||||||
|
flipData.fullTrump = true;
|
||||||
|
}
|
||||||
|
flipData.cardSlot[1].total = flipData.cardSlot[1].value1 + flipData.cardSlot[1].value2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card Total
|
||||||
|
flipData.cardTotal = flipData.cardSlot[0].total;
|
||||||
|
if ( flipData.fullTrump ) {
|
||||||
|
flipData.cardTotal = flipData.cardSlot[0].total + flipData.cardSlot[1].total;
|
||||||
|
} else if (flipData.isTrump) {
|
||||||
|
flipData.cardTotal = (flipData.cardSlot[0].total > flipData.cardSlot[1].total) ? flipData.cardSlot[0].total : flipData.cardSlot[1].total;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute final result and compare
|
||||||
|
if ( flipData.mode == 'stat' ) {
|
||||||
|
flipData.baseScore = flipData.stat.value;
|
||||||
|
} else if (flipData.mode == 'skill') {
|
||||||
|
flipData.baseScore = Math.floor(flipData.stat.value/2) + flipData.skill.data.value;
|
||||||
|
}
|
||||||
|
flipData.finalScore = flipData.baseScore + flipData.cardTotal + Number(flipData.modifier);
|
||||||
|
flipData.magnitude = flipData.finalScore - flipData.tn;
|
||||||
|
flipData.result = (flipData.magnitude >= 0) ? "Success": "Failure";
|
||||||
|
|
||||||
|
console.log(flipData);
|
||||||
|
this.data.actor.saveDeck();
|
||||||
|
flipData.alias = this.data.actor.name;
|
||||||
|
let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-flip.html', flipData);
|
||||||
|
ChatMessage.create( { content: html });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
getDeckHTML( ) {
|
getDeckHTML( ) {
|
||||||
return "<img class='view-deck flip-card' src='systems/foundryvtt-shadows-over-sol/img/cards/card_back.webp' />";
|
return "<a class='view-deck'><img class='flip-card deck-card' src='systems/foundryvtt-shadows-over-sol/img/cards/card_back.webp' /></a>";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
getEdgeHTML( ) {
|
getEdgeHTML( ) {
|
||||||
let html = "";
|
let html = "";
|
||||||
for (let edge of this.data.cardEdge) {
|
for (let edge of this.data.cardEdge) {
|
||||||
html += `<img class='view-discard flip-card' src='systems/foundryvtt-shadows-over-sol/img/cards/${edge.cardName}.webp' />`
|
html += `<a class='view-edge'><img class='flip-card edge-card' data-edge-card='${edge.cardName}' src='systems/foundryvtt-shadows-over-sol/img/cards/${edge.cardName}.webp' /></a>`
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ export class SoSFlipDialog extends Dialog {
|
|||||||
title: 'Flip Dialog',
|
title: 'Flip Dialog',
|
||||||
content: html,
|
content: html,
|
||||||
buttons: {
|
buttons: {
|
||||||
'flip-close': { label: 'Flip and Close', callback: html => this.onFlipClose() }
|
'flip-close': { label: 'Cancel and Close', callback: html => this.onFlipClose() }
|
||||||
},
|
},
|
||||||
default: 'flip'
|
default: 'flip'
|
||||||
};
|
};
|
||||||
@ -18,7 +18,7 @@ export class SoSFlipDialog extends Dialog {
|
|||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
onFlipClose( ) {
|
onFlipClose( ) {
|
||||||
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@ -27,11 +27,22 @@ export class SoSFlipDialog extends Dialog {
|
|||||||
$('.view-deck').remove();
|
$('.view-deck').remove();
|
||||||
$("#view-deck").append(await flipData.actor.cardDeck.getDeckHTML());
|
$("#view-deck").append(await flipData.actor.cardDeck.getDeckHTML());
|
||||||
|
|
||||||
$('.view-discard').remove();
|
|
||||||
$("#view-discard").append(await flipData.actor.cardDeck.getDiscardTopHTML());
|
|
||||||
|
|
||||||
$('.view-edge').remove();
|
$('.view-edge').remove();
|
||||||
$("#view-edge").append(await flipData.actor.cardDeck.getEdgeHTML());
|
$("#view-edge").append(await flipData.actor.cardDeck.getEdgeHTML());
|
||||||
|
|
||||||
|
$('.edge-card').click((event) => {
|
||||||
|
let flipData = duplicate(this.flipData);
|
||||||
|
flipData.modifier = $('#modifier').val();
|
||||||
|
flipData.tn = $('#tn').val();
|
||||||
|
flipData.edgeName = event.currentTarget.attributes['data-edge-card'].value;
|
||||||
|
flipData.cardOrigin = "Edge";
|
||||||
|
if ( flipData.mode == 'skill') {
|
||||||
|
flipData.stat = duplicate( flipData.statList[ $('#statSelect').val() ] );
|
||||||
|
}
|
||||||
|
this.flipData.actor.cardDeck.doFlipFromDeckOrEdge(flipData);
|
||||||
|
this.onFlipClose();
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@ -53,10 +64,20 @@ export class SoSFlipDialog extends Dialog {
|
|||||||
// Setup everything onload
|
// Setup everything onload
|
||||||
$(function () { onLoad(); });
|
$(function () { onLoad(); });
|
||||||
|
|
||||||
html.find('#do-flip-deck').click((event) => {
|
html.find('.class-view-deck').click((event) => {
|
||||||
dialog.flipData.actor.cardDeck.doFlipFromDeck();
|
let flipData = duplicate(this.flipData);
|
||||||
dialog.updateFlip( dialog.flipData);
|
flipData.modifier = html.find('#modifier').val();
|
||||||
|
flipData.tn = html.find('#tn').val();
|
||||||
|
if ( flipData.mode == 'skill') {
|
||||||
|
console.log("SKILL STAT : ", html.find('#statSelect').val() );
|
||||||
|
flipData.stat = duplicate( flipData.statList[ html.find('#statSelect').val() ] );
|
||||||
|
}
|
||||||
|
flipData.cardOrigin = "Deck";
|
||||||
|
flipData.tn = html.find('#tn').val();
|
||||||
|
dialog.flipData.actor.cardDeck.doFlipFromDeckOrEdge(flipData);
|
||||||
|
dialog.onFlipClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -239,7 +239,11 @@ table {border: 1px solid #7a7971;}
|
|||||||
width: 90px;
|
width: 90px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
.card-icon {
|
||||||
|
height: 128px;
|
||||||
|
width: 90px !important;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
.card-img {
|
.card-img {
|
||||||
height: 128px;
|
height: 128px;
|
||||||
width: 90px;
|
width: 90px;
|
||||||
|
27
templates/chat-flip.html
Normal file
27
templates/chat-flip.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<h4>{{alias}} flips a card from {{cardOrigin}}{{#if isTrump}} with a Trump ! {{/if}}</h4>
|
||||||
|
<h4>This a {{mode}} {{#if skill}} {{skill.name}} {{/if}} Flip, with {{localize stat.label}} ({{stat.value}} - {{stat.cardsuit}})</h4>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr class="flexrow">
|
||||||
|
{{#each cardSlot as |slot|}}
|
||||||
|
<td class="flexcol">
|
||||||
|
<span><img class="chat-icon card-icon" src="{{slot.card1Path}}" /></span>
|
||||||
|
{{#if slot.isFace1}}
|
||||||
|
<span></span><img class="chat-icon card-icon" src="{{slot.card2Path}}" /></span>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
{{/each}}
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{#if isJoker}}
|
||||||
|
<label>This is a Critical Failure ! You deck has been resetted !</label>
|
||||||
|
{{else}}
|
||||||
|
<div class="flexcol">
|
||||||
|
<label>Card(s) total value : {{cardTotal}}</label>
|
||||||
|
<label>Stat/Skill base value : {{baseScore}} </label>
|
||||||
|
<label>Modifier : {{numberFormat modifier decimals=0 sign=false}}</label>
|
||||||
|
<label>Final result : {{finalScore}} compared to TN {{tn}}</label>
|
||||||
|
<label><strong>This is a {{result}} with a magnitude of {{magnitude}}</strong></label>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
@ -2,37 +2,59 @@
|
|||||||
|
|
||||||
<h2 class="compdialog" id="statSkillFlip">Flip Dialog !</h2>
|
<h2 class="compdialog" id="statSkillFlip">Flip Dialog !</h2>
|
||||||
|
|
||||||
<h3 class="compdialog" id="flipSubTitle">
|
|
||||||
{{#if (eq mode 'stat')}}
|
{{#if (eq mode 'stat')}}
|
||||||
Stat Only Flip : {{localize stat.label}}
|
<h3 class="compdialog" id="flipSubTitle">
|
||||||
|
Stat Only Flip : {{localize stat.label}} ({{stat.value}}, {{stat.cardsuit}})
|
||||||
|
</h3>
|
||||||
{{else}}
|
{{else}}
|
||||||
Skill Flip : {{skill.name}}
|
<h3 class="compdialog" id="flipSubTitle">
|
||||||
|
Select Stat
|
||||||
|
<select name="stat" id="statSelect" data-dtype="String">
|
||||||
|
{{#select statList}}
|
||||||
|
{{#each statList as |stat key|}}
|
||||||
|
<option value={{key}}>{{localize stat.label}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/select}}
|
||||||
|
</select>
|
||||||
|
</h3>
|
||||||
|
<h3 class="compdialog" id="flipSubTitle">
|
||||||
|
Skill Flip : {{skill.name}} ({{skill.data.value}})
|
||||||
|
</h3>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</h3>
|
|
||||||
|
|
||||||
<section class="sheet-body">
|
<section class="sheet-body">
|
||||||
<div class="tab" data-group="primary">
|
<div class="tab" data-group="primary">
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<label for="categorie generic-label">Modifier</label>
|
<label for="categorie generic-label">Target Number (TN) : </label>
|
||||||
|
<select name="tn" id="tn" data-dtype="number">
|
||||||
|
{{#select tn}}
|
||||||
|
{{#each tnList as |key|}}
|
||||||
|
<option value={{key}} {{#if (eq key 10)}}selected{{/if}}>{{numberFormat key decimals=0 sign=false}}</option>
|
||||||
|
{{/each}}
|
||||||
|
{{/select}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flexrow">
|
||||||
|
<label for="categorie generic-label">Flip Modifier</label>
|
||||||
<select name="modifier" id="modifier" data-dtype="number">
|
<select name="modifier" id="modifier" data-dtype="number">
|
||||||
{{#select modifier}}
|
{{#select modifier}}
|
||||||
{{#each modifierList as |key|}}
|
{{#each modifierList as |key|}}
|
||||||
<option value={{key}} {{#if (eq key 0)}}selected{{/if}}>{{numberFormat key decimals=0 sign=true}}</option>
|
<option value={{key}} {{#if (eq key 0)}}selected{{/if}}>{{numberFormat key decimals=0 sign=true}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/select}}
|
{{/select}}
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="flexrow">
|
||||||
|
<label class="generic-label">Click on deck to flip from deck, or click on the relevant Edge card to flip from it!</label>
|
||||||
</div>
|
</div>
|
||||||
<div id="flowrow">
|
<div id="flexrow">
|
||||||
<span id="view-deck"></span>
|
<span class="class-view-deck" id="view-deck"></span>
|
||||||
<span class="generic-label" id="do-flip-deck"><a>Flip from deck !</a></span>
|
<span class="class-view-edge" id="view-edge"></span>
|
||||||
</div>
|
</div>
|
||||||
<div id="view-discard">
|
<div id="flexrow">
|
||||||
</div>
|
<label class="generic-label"></label>
|
||||||
<div id="view-edge">
|
|
||||||
</div>
|
|
||||||
<div id="view-draw">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</form>
|
</form>
|
Loading…
Reference in New Issue
Block a user