mirror of
https://git.srhtnin.garden/sarah/srhtnin.github.io.git
synced 2026-10-13 18:38:11 +02:00
1038 lines
19 KiB
JavaScript
1038 lines
19 KiB
JavaScript
const DefaultColourPickerValue = "#000000";
|
|
|
|
const DefaultGardenerProfilePicture = Object.freeze({
|
|
PlantKey: "RedRose",
|
|
GrowthStage: 3
|
|
});
|
|
|
|
let PlayerCommentUsername = null;
|
|
let PlayerCommentOffset = 0;
|
|
const PlayerCommentPageSize = 20;
|
|
|
|
|
|
function RenderGardenOwnerHeading(
|
|
Username
|
|
) {
|
|
const Heading =
|
|
document.getElementById(
|
|
"GardenPageHeading"
|
|
);
|
|
|
|
if (Heading === null) {
|
|
return;
|
|
}
|
|
|
|
Heading.textContent =
|
|
Username === null
|
|
? "Your Garden"
|
|
: Username + "'s Garden";
|
|
}
|
|
|
|
|
|
function IsValidPlayerColour(
|
|
Colour
|
|
) {
|
|
return (
|
|
typeof Colour === "string" &&
|
|
/^#[0-9A-Fa-f]{6}$/.test(
|
|
Colour
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
function GetRelativeLuminance(
|
|
Colour
|
|
) {
|
|
const Channels = [
|
|
Colour.slice(1, 3),
|
|
Colour.slice(3, 5),
|
|
Colour.slice(5, 7)
|
|
].map(
|
|
Channel => {
|
|
const Value =
|
|
parseInt(
|
|
Channel,
|
|
16
|
|
) / 255;
|
|
|
|
return Value <= 0.04045
|
|
? Value / 12.92
|
|
: Math.pow(
|
|
(Value + 0.055) /
|
|
1.055,
|
|
2.4
|
|
);
|
|
}
|
|
);
|
|
|
|
return (
|
|
0.2126 * Channels[0] +
|
|
0.7152 * Channels[1] +
|
|
0.0722 * Channels[2]
|
|
);
|
|
}
|
|
|
|
|
|
function GetContrastRatio(
|
|
FirstLuminance,
|
|
SecondLuminance
|
|
) {
|
|
const Lighter = Math.max(
|
|
FirstLuminance,
|
|
SecondLuminance
|
|
);
|
|
|
|
const Darker = Math.min(
|
|
FirstLuminance,
|
|
SecondLuminance
|
|
);
|
|
|
|
return (
|
|
Lighter + 0.05
|
|
) / (
|
|
Darker + 0.05
|
|
);
|
|
}
|
|
|
|
|
|
function GetPlayerColourOutline(
|
|
Colour
|
|
) {
|
|
const ColourLuminance =
|
|
GetRelativeLuminance(
|
|
Colour
|
|
);
|
|
|
|
const LightLuminance =
|
|
GetRelativeLuminance(
|
|
"#cdd6f4"
|
|
);
|
|
|
|
const DarkLuminance =
|
|
GetRelativeLuminance(
|
|
"#11111b"
|
|
);
|
|
|
|
const LightContrast =
|
|
GetContrastRatio(
|
|
ColourLuminance,
|
|
LightLuminance
|
|
);
|
|
|
|
const DarkContrast =
|
|
GetContrastRatio(
|
|
ColourLuminance,
|
|
DarkLuminance
|
|
);
|
|
|
|
|
|
if (
|
|
DarkContrast >=
|
|
LightContrast
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
|
|
return "var(--Text)";
|
|
}
|
|
|
|
|
|
function GetPlayerColourShadow(
|
|
Outline
|
|
) {
|
|
const Shadows = [];
|
|
|
|
for (let Y = -2; Y <= 2; Y++) {
|
|
for (let X = -2; X <= 2; X++) {
|
|
if (X === 0 && Y === 0) {
|
|
continue;
|
|
}
|
|
|
|
Shadows.push(
|
|
`${X}px ${Y}px 0 ${Outline}`
|
|
);
|
|
}
|
|
}
|
|
|
|
return Shadows.join(
|
|
", "
|
|
);
|
|
}
|
|
|
|
|
|
function ApplyPlayerColour(
|
|
Element,
|
|
Colour
|
|
) {
|
|
Element.style.removeProperty(
|
|
"color"
|
|
);
|
|
|
|
Element.style.removeProperty(
|
|
"text-shadow"
|
|
);
|
|
|
|
if (
|
|
!IsValidPlayerColour(
|
|
Colour
|
|
)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const Outline =
|
|
GetPlayerColourOutline(
|
|
Colour
|
|
);
|
|
|
|
Element.style.color =
|
|
Colour;
|
|
|
|
|
|
if (Outline !== null) {
|
|
Element.style.textShadow =
|
|
GetPlayerColourShadow(
|
|
Outline
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
function GetGardenerProfilePictureSource(
|
|
ProfilePicture
|
|
) {
|
|
const Candidate =
|
|
ProfilePicture !== null &&
|
|
typeof ProfilePicture === "object" &&
|
|
!Array.isArray(ProfilePicture) &&
|
|
typeof ProfilePicture.PlantKey ===
|
|
"string" &&
|
|
Number.isInteger(
|
|
Number(
|
|
ProfilePicture.GrowthStage
|
|
)
|
|
)
|
|
? ProfilePicture
|
|
: DefaultGardenerProfilePicture;
|
|
|
|
if (
|
|
typeof Plants === "undefined" ||
|
|
typeof GetPlantImageSources !==
|
|
"function"
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
let Plant =
|
|
Plants[Candidate.PlantKey];
|
|
|
|
let GrowthStage = Number(
|
|
Candidate.GrowthStage
|
|
);
|
|
|
|
if (Plant === undefined) {
|
|
Plant = Plants[
|
|
DefaultGardenerProfilePicture
|
|
.PlantKey
|
|
];
|
|
|
|
GrowthStage =
|
|
DefaultGardenerProfilePicture
|
|
.GrowthStage;
|
|
}
|
|
|
|
if (Plant === undefined) {
|
|
return null;
|
|
}
|
|
|
|
const Images =
|
|
GetPlantImageSources(
|
|
Plant
|
|
);
|
|
|
|
const RequestedSource =
|
|
Images[GrowthStage - 1];
|
|
|
|
if (
|
|
typeof RequestedSource ===
|
|
"string" &&
|
|
RequestedSource.length > 0
|
|
) {
|
|
return RequestedSource;
|
|
}
|
|
|
|
for (
|
|
let Index = Images.length - 1;
|
|
Index >= 0;
|
|
Index--
|
|
) {
|
|
if (
|
|
typeof Images[Index] ===
|
|
"string" &&
|
|
Images[Index].length > 0
|
|
) {
|
|
return Images[Index];
|
|
}
|
|
}
|
|
|
|
if (
|
|
Candidate !==
|
|
DefaultGardenerProfilePicture
|
|
) {
|
|
return GetGardenerProfilePictureSource(
|
|
DefaultGardenerProfilePicture
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
function CreateGardenerAvatar(
|
|
ProfilePicture,
|
|
Username = "",
|
|
AdditionalClass = null
|
|
) {
|
|
const Avatar =
|
|
document.createElement(
|
|
"span"
|
|
);
|
|
|
|
Avatar.className =
|
|
"PlantTile GardenerAvatar";
|
|
|
|
if (
|
|
typeof AdditionalClass ===
|
|
"string" &&
|
|
AdditionalClass.length > 0
|
|
) {
|
|
Avatar.classList.add(
|
|
AdditionalClass
|
|
);
|
|
}
|
|
|
|
if (Username.length > 0) {
|
|
Avatar.title =
|
|
Username +
|
|
"'s profile picture";
|
|
}
|
|
|
|
const ImageSource =
|
|
GetGardenerProfilePictureSource(
|
|
ProfilePicture
|
|
);
|
|
|
|
if (ImageSource !== null) {
|
|
const Image =
|
|
document.createElement(
|
|
"img"
|
|
);
|
|
|
|
Image.className =
|
|
"PlantSprite";
|
|
|
|
Image.src = ImageSource;
|
|
Image.alt = "";
|
|
Image.draggable = false;
|
|
|
|
Avatar.appendChild(
|
|
Image
|
|
);
|
|
|
|
return Avatar;
|
|
}
|
|
|
|
const Placeholder =
|
|
document.createElement(
|
|
"span"
|
|
);
|
|
|
|
Placeholder.className =
|
|
"GardenerAvatarPlaceholder";
|
|
|
|
Placeholder.textContent = "□";
|
|
|
|
Avatar.appendChild(
|
|
Placeholder
|
|
);
|
|
|
|
return Avatar;
|
|
}
|
|
|
|
|
|
function GetGardenerPageUrl(
|
|
Username
|
|
) {
|
|
const Query =
|
|
new URLSearchParams({
|
|
Username: Username
|
|
});
|
|
|
|
return (
|
|
"/Pages/Gardener.html?" +
|
|
Query.toString()
|
|
);
|
|
}
|
|
|
|
|
|
function CreateGardenerIdentity(
|
|
Username,
|
|
Colour,
|
|
ProfilePicture,
|
|
LinkToGardener = true
|
|
) {
|
|
const Container =
|
|
document.createElement(
|
|
LinkToGardener
|
|
? "a"
|
|
: "span"
|
|
);
|
|
|
|
Container.className =
|
|
"GardenerIdentity";
|
|
|
|
if (LinkToGardener) {
|
|
Container.href =
|
|
GetGardenerPageUrl(
|
|
Username
|
|
);
|
|
}
|
|
|
|
const Avatar =
|
|
CreateGardenerAvatar(
|
|
ProfilePicture,
|
|
Username
|
|
);
|
|
|
|
const Name =
|
|
document.createElement(
|
|
"span"
|
|
);
|
|
|
|
Name.className =
|
|
"GardenerIdentityName";
|
|
|
|
Name.textContent = Username;
|
|
|
|
ApplyPlayerColour(
|
|
Name,
|
|
Colour
|
|
);
|
|
|
|
Container.append(
|
|
Avatar,
|
|
Name
|
|
);
|
|
|
|
return Container;
|
|
}
|
|
|
|
|
|
function CreateGardenerCommentCard(
|
|
Comment,
|
|
DeleteHandler = null
|
|
) {
|
|
const Article =
|
|
document.createElement(
|
|
"article"
|
|
);
|
|
|
|
Article.className =
|
|
"PlantTile GardenerComment";
|
|
|
|
Article.dataset.commentId =
|
|
String(Comment.Id);
|
|
|
|
const Header =
|
|
document.createElement(
|
|
"div"
|
|
);
|
|
|
|
Header.className =
|
|
"GardenerCommentHeader";
|
|
|
|
const Identity =
|
|
CreateGardenerIdentity(
|
|
Comment.Username,
|
|
Comment.Colour,
|
|
Comment.ProfilePicture
|
|
);
|
|
|
|
const Time =
|
|
document.createElement(
|
|
"time"
|
|
);
|
|
|
|
Time.className =
|
|
"GardenerCommentTime";
|
|
|
|
const CreatedAt = Number(
|
|
Comment.CreatedAt
|
|
);
|
|
|
|
if (Number.isFinite(CreatedAt)) {
|
|
const DateValue =
|
|
new Date(CreatedAt);
|
|
|
|
Time.dateTime =
|
|
DateValue.toISOString();
|
|
|
|
Time.textContent =
|
|
DateValue.toLocaleString();
|
|
}
|
|
|
|
Header.append(
|
|
Identity,
|
|
Time
|
|
);
|
|
|
|
const Text =
|
|
document.createElement(
|
|
"p"
|
|
);
|
|
|
|
Text.className =
|
|
"GardenerCommentText";
|
|
|
|
Text.textContent =
|
|
Comment.Text ?? "";
|
|
|
|
Article.append(
|
|
Header,
|
|
Text
|
|
);
|
|
|
|
if (
|
|
Comment.CanDelete === true &&
|
|
typeof DeleteHandler ===
|
|
"function"
|
|
) {
|
|
const DeleteButton =
|
|
document.createElement(
|
|
"button"
|
|
);
|
|
|
|
DeleteButton.type = "button";
|
|
DeleteButton.className =
|
|
"ActionButton GardenerCommentDelete";
|
|
DeleteButton.textContent =
|
|
"Delete";
|
|
|
|
DeleteButton.addEventListener(
|
|
"click",
|
|
() => DeleteHandler(
|
|
Number(Comment.Id),
|
|
DeleteButton
|
|
)
|
|
);
|
|
|
|
Article.appendChild(
|
|
DeleteButton
|
|
);
|
|
}
|
|
|
|
return Article;
|
|
}
|
|
|
|
|
|
async function GetGardenerComments(
|
|
Username,
|
|
Offset = 0,
|
|
Limit = 20
|
|
) {
|
|
const Response = await fetch(
|
|
ApiUrl + "/Comments.php",
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type":
|
|
"application/json"
|
|
},
|
|
body: JSON.stringify({
|
|
Action: "List",
|
|
SaveKey: GetSaveKey(),
|
|
Username: Username,
|
|
Offset: Offset,
|
|
Limit: Limit
|
|
})
|
|
}
|
|
);
|
|
|
|
return await Response.json();
|
|
}
|
|
|
|
|
|
async function DeleteGardenerCommentRequest(
|
|
CommentId
|
|
) {
|
|
const Response = await fetch(
|
|
ApiUrl + "/Comments.php",
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type":
|
|
"application/json"
|
|
},
|
|
body: JSON.stringify({
|
|
Action: "Delete",
|
|
SaveKey: GetSaveKey(),
|
|
CommentId: CommentId
|
|
})
|
|
}
|
|
);
|
|
|
|
return await Response.json();
|
|
}
|
|
|
|
|
|
async function GetProfile() {
|
|
const Response = await fetch(
|
|
ApiUrl + "/Profile.php",
|
|
{
|
|
method: "POST",
|
|
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
SaveKey: GetSaveKey()
|
|
})
|
|
}
|
|
);
|
|
|
|
return await Response.json();
|
|
}
|
|
|
|
|
|
async function SetUsername(
|
|
Username
|
|
) {
|
|
const Response = await fetch(
|
|
ApiUrl + "/SetUsername.php",
|
|
{
|
|
method: "POST",
|
|
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
SaveKey: GetSaveKey(),
|
|
Username: Username
|
|
})
|
|
}
|
|
);
|
|
|
|
return await Response.json();
|
|
}
|
|
|
|
async function SetUserConfig(
|
|
Changes
|
|
) {
|
|
const Response = await fetch(
|
|
ApiUrl + "/SetUserConfig.php",
|
|
{
|
|
method: "POST",
|
|
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
SaveKey: GetSaveKey(),
|
|
...Changes
|
|
})
|
|
}
|
|
);
|
|
|
|
return await Response.json();
|
|
}
|
|
|
|
|
|
async function SetColour(
|
|
Colour
|
|
) {
|
|
const Response =
|
|
await fetch(
|
|
ApiUrl +
|
|
"/SetColour.php",
|
|
{
|
|
method: "POST",
|
|
|
|
headers: {
|
|
"Content-Type":
|
|
"application/json"
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
SaveKey:
|
|
GetSaveKey(),
|
|
|
|
Colour:
|
|
Colour
|
|
})
|
|
}
|
|
);
|
|
|
|
return await Response.json();
|
|
}
|
|
|
|
async function RenderUser() {
|
|
const Profile =
|
|
await GetProfile();
|
|
|
|
const PlayerIdentity =
|
|
document.getElementById(
|
|
"PlayerIdentity"
|
|
);
|
|
|
|
const UsernameForm =
|
|
document.getElementById(
|
|
"UsernameForm"
|
|
);
|
|
|
|
if (
|
|
Profile.Success &&
|
|
Profile.Username !== null
|
|
) {
|
|
if (PlayerIdentity !== null) {
|
|
const Identity =
|
|
CreateGardenerIdentity(
|
|
Profile.Username,
|
|
Profile.Colour,
|
|
Profile.ProfilePicture,
|
|
false
|
|
);
|
|
|
|
Identity.classList.add(
|
|
"GardenPlayerIdentityContent"
|
|
);
|
|
|
|
Identity.querySelector(
|
|
".GardenerAvatar"
|
|
)?.classList.add(
|
|
"GardenerAvatarLarge"
|
|
);
|
|
|
|
PlayerIdentity.replaceChildren(
|
|
Identity
|
|
);
|
|
}
|
|
|
|
UsernameForm.hidden = true;
|
|
|
|
RenderGardenOwnerHeading(
|
|
Profile.Username
|
|
);
|
|
|
|
await LoadPlayerComments(
|
|
Profile.Username,
|
|
true
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
if (PlayerIdentity !== null) {
|
|
const Identity =
|
|
CreateGardenerIdentity(
|
|
"Unnamed",
|
|
null,
|
|
null,
|
|
false
|
|
);
|
|
|
|
Identity.classList.add(
|
|
"GardenPlayerIdentityContent"
|
|
);
|
|
|
|
Identity.querySelector(
|
|
".GardenerAvatar"
|
|
)?.classList.add(
|
|
"GardenerAvatarLarge"
|
|
);
|
|
|
|
PlayerIdentity.replaceChildren(
|
|
Identity
|
|
);
|
|
}
|
|
|
|
UsernameForm.hidden = false;
|
|
|
|
RenderGardenOwnerHeading(
|
|
null
|
|
);
|
|
|
|
PlayerCommentUsername = null;
|
|
PlayerCommentOffset = 0;
|
|
|
|
const CommentList =
|
|
document.getElementById(
|
|
"PlayerCommentList"
|
|
);
|
|
|
|
const CommentMessage =
|
|
document.getElementById(
|
|
"PlayerCommentMessage"
|
|
);
|
|
|
|
const LoadMoreButton =
|
|
document.getElementById(
|
|
"LoadMorePlayerCommentsButton"
|
|
);
|
|
|
|
CommentList?.replaceChildren();
|
|
|
|
if (CommentMessage !== null) {
|
|
CommentMessage.textContent =
|
|
"Set a username to receive comments.";
|
|
}
|
|
|
|
if (LoadMoreButton !== null) {
|
|
LoadMoreButton.hidden = true;
|
|
}
|
|
}
|
|
|
|
|
|
async function LoadPlayerComments(
|
|
Username,
|
|
Reset
|
|
) {
|
|
const List =
|
|
document.getElementById(
|
|
"PlayerCommentList"
|
|
);
|
|
|
|
const Message =
|
|
document.getElementById(
|
|
"PlayerCommentMessage"
|
|
);
|
|
|
|
const LoadMoreButton =
|
|
document.getElementById(
|
|
"LoadMorePlayerCommentsButton"
|
|
);
|
|
|
|
if (List === null) {
|
|
return;
|
|
}
|
|
|
|
if (Reset) {
|
|
PlayerCommentUsername = Username;
|
|
PlayerCommentOffset = 0;
|
|
List.replaceChildren();
|
|
}
|
|
|
|
if (
|
|
typeof PlayerCommentUsername !==
|
|
"string" ||
|
|
PlayerCommentUsername.length === 0
|
|
) {
|
|
if (Message !== null) {
|
|
Message.textContent =
|
|
"Set a username to receive comments.";
|
|
}
|
|
|
|
if (LoadMoreButton !== null) {
|
|
LoadMoreButton.hidden = true;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (LoadMoreButton !== null) {
|
|
LoadMoreButton.disabled = true;
|
|
}
|
|
|
|
try {
|
|
const Result =
|
|
await GetGardenerComments(
|
|
PlayerCommentUsername,
|
|
PlayerCommentOffset,
|
|
PlayerCommentPageSize
|
|
);
|
|
|
|
if (!Result.Success) {
|
|
throw new Error(
|
|
Result.Error ??
|
|
"Couldn't load comments."
|
|
);
|
|
}
|
|
|
|
const Comments =
|
|
Array.isArray(Result.Comments)
|
|
? Result.Comments
|
|
: [];
|
|
|
|
for (const Comment of Comments) {
|
|
List.appendChild(
|
|
CreateGardenerCommentCard(
|
|
Comment,
|
|
DeletePlayerComment
|
|
)
|
|
);
|
|
}
|
|
|
|
PlayerCommentOffset +=
|
|
Comments.length;
|
|
|
|
if (
|
|
Reset &&
|
|
Comments.length === 0
|
|
) {
|
|
const Empty =
|
|
document.createElement(
|
|
"p"
|
|
);
|
|
|
|
Empty.className =
|
|
"GardenerCommentsEmpty";
|
|
|
|
Empty.textContent =
|
|
"No comments yet.";
|
|
|
|
List.appendChild(
|
|
Empty
|
|
);
|
|
}
|
|
|
|
if (Message !== null) {
|
|
Message.textContent = "";
|
|
}
|
|
|
|
if (LoadMoreButton !== null) {
|
|
LoadMoreButton.hidden =
|
|
Result.HasMore !== true;
|
|
}
|
|
} catch (Error) {
|
|
console.error(
|
|
"Couldn't load player comments:",
|
|
Error
|
|
);
|
|
|
|
if (Message !== null) {
|
|
Message.textContent =
|
|
"Couldn't load comments.";
|
|
}
|
|
} finally {
|
|
if (LoadMoreButton !== null) {
|
|
LoadMoreButton.disabled = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
async function DeletePlayerComment(
|
|
CommentId,
|
|
Button
|
|
) {
|
|
Button.disabled = true;
|
|
|
|
const Message =
|
|
document.getElementById(
|
|
"PlayerCommentMessage"
|
|
);
|
|
|
|
try {
|
|
const Result =
|
|
await DeleteGardenerCommentRequest(
|
|
CommentId
|
|
);
|
|
|
|
if (!Result.Success) {
|
|
throw new Error(
|
|
Result.Error ??
|
|
"Couldn't delete comment."
|
|
);
|
|
}
|
|
|
|
await LoadPlayerComments(
|
|
PlayerCommentUsername,
|
|
true
|
|
);
|
|
|
|
if (Message !== null) {
|
|
Message.textContent =
|
|
"Comment deleted.";
|
|
}
|
|
} catch (Error) {
|
|
console.error(
|
|
"Couldn't delete player comment:",
|
|
Error
|
|
);
|
|
|
|
if (Message !== null) {
|
|
Message.textContent =
|
|
Error.message ??
|
|
"Couldn't delete comment.";
|
|
}
|
|
|
|
Button.disabled = false;
|
|
}
|
|
}
|
|
|
|
|
|
async function SubmitUsername(
|
|
Event
|
|
) {
|
|
Event.preventDefault();
|
|
|
|
const Input =
|
|
document.getElementById(
|
|
"UsernameInput"
|
|
);
|
|
|
|
const Message =
|
|
document.getElementById(
|
|
"UsernameMessage"
|
|
);
|
|
|
|
const Result =
|
|
await SetUsername(
|
|
Input.value
|
|
);
|
|
|
|
if (!Result.Success) {
|
|
Message.textContent =
|
|
Result.Error;
|
|
|
|
return;
|
|
}
|
|
|
|
Message.textContent =
|
|
"Username set.";
|
|
|
|
await RenderUser();
|
|
}
|
|
|
|
|
|
document.addEventListener(
|
|
"DOMContentLoaded",
|
|
() => {
|
|
const UsernameForm =
|
|
document.getElementById(
|
|
"UsernameForm"
|
|
);
|
|
|
|
if (UsernameForm !== null) {
|
|
UsernameForm.addEventListener(
|
|
"submit",
|
|
SubmitUsername
|
|
);
|
|
}
|
|
|
|
document.getElementById(
|
|
"LoadMorePlayerCommentsButton"
|
|
)?.addEventListener(
|
|
"click",
|
|
() => LoadPlayerComments(
|
|
PlayerCommentUsername,
|
|
false
|
|
)
|
|
);
|
|
}
|
|
);
|