🆙 Fix Pet Infostand

This commit is contained in:
duckietm 2025-05-22 10:29:50 +02:00
parent 23c8eab372
commit aee7ffb392
4 changed files with 472 additions and 189 deletions

View File

@ -69,6 +69,8 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
const element = await TextureUtils.generateImage(new NitroSprite(event.image));
console.log ('boe');
element.onload = () => setImageElement(element);
didSetBadge = true;
@ -86,6 +88,7 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
{
const element = await TextureUtils.generateImage(new NitroSprite(texture));
element.onload = () => setImageElement(element);
})();
}

View File

@ -1,209 +1,343 @@
import { CreateLinkEvent, PetRespectComposer, PetType } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { FC, useEffect, useState, useCallback } from 'react';
import { FaTimes } from 'react-icons/fa';
import { AvatarInfoPet, ConvertSeconds, GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../../../../api';
import { Button, Column, Flex, LayoutCounterTimeView, LayoutPetImageView, LayoutRarityLevelView, Text, UserProfileIconView } from '../../../../../common';
import { useRoom, useSessionInfo } from '../../../../../hooks';
interface InfoStandWidgetPetViewProps
{
avatarInfo: AvatarInfoPet;
onClose: () => void;
// TypeScript interface for AvatarInfoPet
interface AvatarInfoPet {
id: number;
name: string;
petType: number;
petBreed: number;
petFigure: string;
posture: string;
level: number;
maximumLevel: number;
age: number;
ownerId: number;
ownerName: string;
respect: number;
dead?: boolean;
energy?: number;
maximumEnergy?: number;
happyness?: number;
maximumHappyness?: number;
experience?: number;
levelExperienceGoal?: number;
remainingGrowTime?: number;
remainingTimeToLive?: number;
maximumTimeToLive?: number;
rarityLevel?: number;
isOwner?: boolean;
}
export const InfoStandWidgetPetView: FC<InfoStandWidgetPetViewProps> = props =>
{
const { avatarInfo = null, onClose = null } = props;
const [ remainingGrowTime, setRemainingGrowTime ] = useState(0);
const [ remainingTimeToLive, setRemainingTimeToLive ] = useState(0);
const { roomSession = null } = useRoom();
const { petRespectRemaining = 0, respectPet = null } = useSessionInfo();
interface InfoStandWidgetPetViewProps {
avatarInfo: AvatarInfoPet;
onClose: () => void;
}
useEffect(() =>
{
setRemainingGrowTime(avatarInfo.remainingGrowTime);
setRemainingTimeToLive(avatarInfo.remainingTimeToLive);
}, [ avatarInfo ]);
const PetHeader: FC<{ name: string; petType: number; petBreed: number; onClose: () => void }> = ({ name, petType, petBreed, onClose }) => (
<div className="flex flex-col gap-1">
<Flex alignItems="center" gap={1} justifyContent="between">
<Text small wrap variant="white">
{name}
</Text>
<FaTimes
className="cursor-pointer fa-icon"
onClick={onClose}
aria-label={LocalizeText('generic.close')}
title={LocalizeText('generic.close')}
/>
</Flex>
<Text small wrap variant="white">
{LocalizeText(`pet.breed.${petType}.${petBreed}`)}
</Text>
<hr className="m-0" />
</div>
);
useEffect(() =>
{
if((avatarInfo.petType !== PetType.MONSTERPLANT) || avatarInfo.dead) return;
const MonsterplantStats: FC<{
avatarInfo: AvatarInfoPet;
remainingGrowTime: number;
remainingTimeToLive: number;
}> = ({ avatarInfo, remainingGrowTime, remainingTimeToLive }) => (
<>
<Column center gap={1}>
<LayoutPetImageView direction={4} figure={avatarInfo.petFigure} posture={avatarInfo.posture} />
<hr className="m-0" />
</Column>
<div className="flex flex-col gap-2">
{!avatarInfo.dead && (
<Column alignItems="center" gap={1}>
<Text center small wrap variant="white">
{LocalizeText('pet.level', ['level', 'maxlevel'], [avatarInfo.level.toString(), avatarInfo.maximumLevel.toString()])}
</Text>
</Column>
)}
<Column alignItems="center" gap={1}>
<Text small truncate variant="white">
{LocalizeText('infostand.pet.text.wellbeing')}
</Text>
<div className="bg-light-dark rounded relative overflow-hidden w-full">
<div className="flex justify-center items-center size-full absolute">
<Text small variant="white">
{avatarInfo.dead || remainingTimeToLive <= 0
? '00:00:00'
: `${ConvertSeconds(remainingTimeToLive).split(':')[1]}:${ConvertSeconds(remainingTimeToLive).split(':')[2]}:${ConvertSeconds(remainingTimeToLive).split(':')[3]}`}
</Text>
</div>
<div
className="bg-success rounded pet-stats"
style={{
width: avatarInfo.dead || remainingTimeToLive <= 0 ? '0' : `${(remainingTimeToLive / avatarInfo.maximumTimeToLive) * 100}%`,
}}
/>
</div>
</Column>
{remainingGrowTime > 0 && (
<Column alignItems="center" gap={1}>
<Text small truncate variant="white">
{LocalizeText('infostand.pet.text.growth')}
</Text>
<LayoutCounterTimeView
className="top-2 end-2"
day={ConvertSeconds(remainingGrowTime).split(':')[0]}
hour={ConvertSeconds(remainingGrowTime).split(':')[1]}
minutes={ConvertSeconds(remainingGrowTime).split(':')[2]}
seconds={ConvertSeconds(remainingGrowTime).split(':')[3]}
/>
</Column>
)}
<Column alignItems="center" gap={1}>
<Text small truncate variant="white">
{LocalizeText('infostand.pet.text.raritylevel', ['level'], [LocalizeText(`infostand.pet.raritylevel.${avatarInfo.rarityLevel}`)])}
</Text>
<LayoutRarityLevelView className="top-2 end-2" level={avatarInfo.rarityLevel} />
</Column>
<hr className="m-0" />
</div>
<div className="flex flex-col gap-1">
<Text small wrap variant="white">
{LocalizeText('pet.age', ['age'], [avatarInfo.age.toString()])}
</Text>
<hr className="m-0" />
</div>
</>
);
const interval = setInterval(() =>
{
setRemainingGrowTime(prevValue => (prevValue - 1));
setRemainingTimeToLive(prevValue => (prevValue - 1));
}, 1000);
// Sub-component: Regular Pet Stats
const RegularPetStats: FC<{ avatarInfo: AvatarInfoPet }> = ({ avatarInfo }) => (
<>
<div className="flex flex-col gap-1">
<div className="flex gap-1">
<Column fullWidth className="body-image pet p-1" overflow="hidden">
<LayoutPetImageView direction={4} figure={avatarInfo.petFigure} posture={avatarInfo.posture} />
</Column>
<Column grow gap={1}>
<Text center small wrap variant="white">
{LocalizeText('pet.level', ['level', 'maxlevel'], [avatarInfo.level.toString(), avatarInfo.maximumLevel.toString()])}
</Text>
<Column alignItems="center" gap={1}>
<Text small truncate variant="white">
{LocalizeText('infostand.pet.text.happiness')}
</Text>
<div className="bg-light-dark rounded relative overflow-hidden w-full">
<div className="flex justify-center items-center size-full absolute">
<Text small variant="white">
{avatarInfo.happyness + '/' + avatarInfo.maximumHappyness}
</Text>
</div>
<div
className="bg-info rounded pet-stats"
style={{ width: (avatarInfo.happyness / avatarInfo.maximumHappyness) * 100 + '%' }}
/>
</div>
</Column>
<Column alignItems="center" gap={1}>
<Text small truncate variant="white">
{LocalizeText('infostand.pet.text.experience')}
</Text>
<div className="bg-light-dark rounded relative overflow-hidden w-full">
<div className="flex justify-center items-center size-full absolute">
<Text small variant="white">
{avatarInfo.experience + '/' + avatarInfo.levelExperienceGoal}
</Text>
</div>
<div
className="bg-purple rounded pet-stats"
style={{ width: (avatarInfo.experience / avatarInfo.levelExperienceGoal) * 100 + '%' }}
/>
</div>
</Column>
<Column alignItems="center" gap={1}>
<Text small truncate variant="white">
{LocalizeText('infostand.pet.text.energy')}
</Text>
<div className="bg-light-dark rounded relative overflow-hidden w-full">
<div className="flex justify-center items-center size-full absolute">
<Text small variant="white">
{avatarInfo.energy + '/' + avatarInfo.maximumEnergy}
</Text>
</div>
<div
className="bg-success rounded pet-stats"
style={{ width: (avatarInfo.energy / avatarInfo.maximumEnergy) * 100 + '%' }}
/>
</div>
</Column>
</Column>
</div>
<hr className="m-0" />
</div>
<div className="flex flex-col gap-1">
<Text small wrap variant="white">
{LocalizeText('infostand.text.petrespect', ['count'], [avatarInfo.respect.toString()])}
</Text>
<Text small wrap variant="white">
{LocalizeText('pet.age', ['age'], [avatarInfo.age.toString()])}
</Text>
<hr className="m-0" />
</div>
</>
);
return () => clearInterval(interval);
}, [ avatarInfo ]);
export const InfoStandWidgetPetView: FC<InfoStandWidgetPetViewProps> = ({ avatarInfo, onClose }) => {
const [remainingGrowTime, setRemainingGrowTime] = useState(0);
const [remainingTimeToLive, setRemainingTimeToLive] = useState(0);
const { roomSession = null } = useRoom();
const { petRespectRemaining = 0, respectPet = null } = useSessionInfo();
if(!avatarInfo) return null;
useEffect(() => {
setRemainingGrowTime(avatarInfo.remainingGrowTime || 0);
setRemainingTimeToLive(avatarInfo.remainingTimeToLive || 0);
}, [avatarInfo]);
const processButtonAction = (action: string) =>
{
useEffect(() => {
if (avatarInfo.petType !== PetType.MONSTERPLANT || avatarInfo.dead) return;
const interval = setInterval(() => {
setRemainingGrowTime((prev) => (prev <= 0 ? 0 : prev - 1));
setRemainingTimeToLive((prev) => (prev <= 0 ? 0 : prev - 1));
}, 1000);
return () => clearInterval(interval);
}, [avatarInfo]);
const processButtonAction = useCallback(
async (action: string) => {
try {
let hideMenu = true;
if (!action) return;
if(!action || action == '') return;
switch(action)
{
case 'respect':
respectPet(avatarInfo.id);
if((petRespectRemaining - 1) >= 1) hideMenu = false;
break;
case 'buyfood':
CreateLinkEvent('catalog/open/' + GetConfigurationValue('catalog.links')['pets.buy_food']);
break;
case 'train':
roomSession?.requestPetCommands(avatarInfo.id);
break;
case 'treat':
SendMessageComposer(new PetRespectComposer(avatarInfo.id));
break;
case 'compost':
roomSession?.compostPlant(avatarInfo.id);
break;
case 'pick_up':
roomSession?.pickupPet(avatarInfo.id);
break;
switch (action) {
case 'respect':
await respectPet(avatarInfo.id);
if (petRespectRemaining - 1 >= 1) hideMenu = false;
break;
case 'buyfood':
CreateLinkEvent('catalog/open/' + GetConfigurationValue('catalog.links')['pets.buy_food']);
break;
case 'train':
roomSession?.requestPetCommands(avatarInfo.id);
break;
case 'treat':
SendMessageComposer(new PetRespectComposer(avatarInfo.id));
break;
case 'compost':
roomSession?.compostPlant(avatarInfo.id);
break;
case 'pick_up':
roomSession?.pickupPet(avatarInfo.id);
break;
}
if(hideMenu) onClose();
};
if (hideMenu) onClose();
} catch (error) {
console.error(`Failed to process action ${action}:`, error);
}
},
[avatarInfo, petRespectRemaining, respectPet, roomSession, onClose]
);
return (
<Column alignItems="end" gap={ 1 }>
<Column className="nitro-infostand rounded">
<Column className="container-fluid content-area" gap={ 1 } overflow="visible">
<div className="flex flex-col gap-1">
<Flex alignItems="center" gap={ 1 } justifyContent="between">
<Text small wrap variant="white">{ avatarInfo.name }</Text>
<FaTimes className="cursor-pointer fa-icon" onClick={ onClose } />
</Flex>
<Text small wrap variant="white">{ LocalizeText(`pet.breed.${ avatarInfo.petType }.${ avatarInfo.petBreed }`) }</Text>
<hr className="m-0" />
</div>
{ (avatarInfo.petType === PetType.MONSTERPLANT) &&
<>
<Column center gap={ 1 }>
<LayoutPetImageView direction={ 4 } figure={ avatarInfo.petFigure } posture={ avatarInfo.posture } />
<hr className="m-0" />
</Column>
<div className="flex flex-col gap-2">
{ !avatarInfo.dead &&
<Column alignItems="center" gap={ 1 }>
<Text center small wrap variant="white">{ LocalizeText('pet.level', [ 'level', 'maxlevel' ], [ avatarInfo.level.toString(), avatarInfo.maximumLevel.toString() ]) }</Text>
</Column> }
<Column alignItems="center" gap={ 1 }>
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.wellbeing') }</Text>
<div className="bg-light-dark rounded relative overflow-hidden w-full">
<div className="flex justify-center items-center size-full absolute">
<Text small variant="white">{ avatarInfo.dead ? '00:00:00' : ConvertSeconds((remainingTimeToLive == 0 ? avatarInfo.remainingTimeToLive : remainingTimeToLive)).split(':')[1] + ':' + ConvertSeconds((remainingTimeToLive == null || remainingTimeToLive == undefined ? 0 : remainingTimeToLive)).split(':')[2] + ':' + ConvertSeconds((remainingTimeToLive == null || remainingTimeToLive == undefined ? 0 : remainingTimeToLive)).split(':')[3] }</Text>
</div>
<div className="bg-success rounded pet-stats" style={ { width: avatarInfo.dead ? '0' : Math.round((avatarInfo.maximumTimeToLive * 100) / (remainingTimeToLive)).toString() } } />
</div>
</Column>
{ remainingGrowTime != 0 && remainingGrowTime > 0 &&
<Column alignItems="center" gap={ 1 }>
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.growth') }</Text>
<LayoutCounterTimeView className="top-2 end-2" day={ ConvertSeconds(remainingGrowTime).split(':')[0] } hour={ ConvertSeconds(remainingGrowTime).split(':')[1] } minutes={ ConvertSeconds(remainingGrowTime).split(':')[2] } seconds={ ConvertSeconds(remainingGrowTime).split(':')[3] } />
</Column> }
<Column alignItems="center" gap={ 1 }>
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.raritylevel', [ 'level' ], [ LocalizeText(`infostand.pet.raritylevel.${ avatarInfo.rarityLevel }`) ]) }</Text>
<LayoutRarityLevelView className="top-2 end-2" level={ avatarInfo.rarityLevel } />
</Column>
<hr className="m-0" />
</div>
<div className="flex flex-col gap-1">
<Text small wrap variant="white">{ LocalizeText('pet.age', [ 'age' ], [ avatarInfo.age.toString() ]) }</Text>
<hr className="m-0" />
</div>
</> }
{ (avatarInfo.petType !== PetType.MONSTERPLANT) &&
<>
<div className="flex flex-col gap-1">
<div className="flex gap-1">
<Column fullWidth className="body-image pet p-1" overflow="hidden">
<LayoutPetImageView direction={ 4 } figure={ avatarInfo.petFigure } posture={ avatarInfo.posture } />
</Column>
<Column grow gap={ 1 }>
<Text center small wrap variant="white">{ LocalizeText('pet.level', [ 'level', 'maxlevel' ], [ avatarInfo.level.toString(), avatarInfo.maximumLevel.toString() ]) }</Text>
<Column alignItems="center" gap={ 1 }>
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.happiness') }</Text>
<div className="bg-light-dark rounded relative overflow-hidden w-full">
<div className="flex justify-center items-center size-full absolute">
<Text small variant="white">{ avatarInfo.happyness + '/' + avatarInfo.maximumHappyness }</Text>
</div>
<div className="bg-info rounded pet-stats" style={ { width: (avatarInfo.happyness / avatarInfo.maximumHappyness) * 100 + '%' } } />
</div>
</Column>
<Column alignItems="center" gap={ 1 }>
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.experience') }</Text>
<div className="bg-light-dark rounded relative overflow-hidden w-full">
<div className="flex justify-center items-center size-full absolute">
<Text small variant="white">{ avatarInfo.experience + '/' + avatarInfo.levelExperienceGoal }</Text>
</div>
<div className="bg-purple rounded pet-stats" style={ { width: (avatarInfo.experience / avatarInfo.levelExperienceGoal) * 100 + '%' } } />
</div>
</Column>
<Column alignItems="center" gap={ 1 }>
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.energy') }</Text>
<div className="bg-light-dark rounded relative overflow-hidden w-full">
<div className="flex justify-center items-center size-full absolute">
<Text small variant="white">{ avatarInfo.energy + '/' + avatarInfo.maximumEnergy }</Text>
</div>
<div className="bg-success rounded pet-stats" style={ { width: (avatarInfo.energy / avatarInfo.maximumEnergy) * 100 + '%' } } />
</div>
</Column>
</Column>
</div>
<hr className="m-0" />
</div>
<div className="flex flex-col gap-1">
{ (avatarInfo.petType !== PetType.MONSTERPLANT) &&
<Text small wrap variant="white">{ LocalizeText('infostand.text.petrespect', [ 'count' ], [ avatarInfo.respect.toString() ]) }</Text> }
<Text small wrap variant="white">{ LocalizeText('pet.age', [ 'age' ], [ avatarInfo.age.toString() ]) }</Text>
<hr className="m-0" />
</div>
</> }
<div className="flex flex-col gap-1">
<div className="flex items-center gap-1">
<UserProfileIconView userId={ avatarInfo.ownerId } />
<Text small wrap variant="white">
{ LocalizeText('infostand.text.petowner', [ 'name' ], [ avatarInfo.ownerName ]) }
</Text>
</div>
</div>
</Column>
</Column>
<Flex gap={ 1 } justifyContent="end">
{ (avatarInfo.petType !== PetType.MONSTERPLANT) &&
<Button variant="dark" onClick={ event => processButtonAction('buyfood') }>
{ LocalizeText('infostand.button.buyfood') }
</Button> }
{ avatarInfo.isOwner && (avatarInfo.petType !== PetType.MONSTERPLANT) &&
<Button variant="dark" onClick={ event => processButtonAction('train') }>
{ LocalizeText('infostand.button.train') }
</Button> }
{ !avatarInfo.dead && ((avatarInfo.energy / avatarInfo.maximumEnergy) < 0.98) && (avatarInfo.petType === PetType.MONSTERPLANT) &&
<Button variant="dark" onClick={ event => processButtonAction('treat') }>
{ LocalizeText('infostand.button.pettreat') }
</Button> }
{ roomSession?.isRoomOwner && (avatarInfo.petType === PetType.MONSTERPLANT) &&
<Button variant="dark" onClick={ event => processButtonAction('compost') }>
{ LocalizeText('infostand.button.compost') }
</Button> }
{ avatarInfo.isOwner &&
<Button variant="dark" onClick={ event => processButtonAction('pick_up') }>
{ LocalizeText('inventory.pets.pickup') }
</Button> }
{ (petRespectRemaining > 0) && (avatarInfo.petType !== PetType.MONSTERPLANT) &&
<Button variant="dark" onClick={ event => processButtonAction('respect') }>
{ LocalizeText('infostand.button.petrespect', [ 'count' ], [ petRespectRemaining.toString() ]) }
</Button> }
</Flex>
const buttons = [
{
action: 'buyfood',
label: LocalizeText('infostand.button.buyfood'),
condition: avatarInfo.petType !== PetType.MONSTERPLANT,
},
{
action: 'train',
label: LocalizeText('infostand.button.train'),
condition: avatarInfo.isOwner && avatarInfo.petType !== PetType.MONSTERPLANT,
},
{
action: 'treat',
label: LocalizeText('infostand.button.pettreat'),
condition:
!avatarInfo.dead &&
avatarInfo.petType === PetType.MONSTERPLANT &&
avatarInfo.energy / avatarInfo.maximumEnergy < 0.98,
},
{
action: 'compost',
label: LocalizeText('infostand.button.compost'),
condition: roomSession?.isRoomOwner && avatarInfo.petType === PetType.MONSTERPLANT,
},
{
action: 'pick_up',
label: LocalizeText('inventory.pets.pickup'),
condition: avatarInfo.isOwner,
},
{
action: 'respect',
label: LocalizeText('infostand.button.petrespect', ['count'], [petRespectRemaining.toString()]),
condition: petRespectRemaining > 0 && avatarInfo.petType !== PetType.MONSTERPLANT,
},
];
if (!avatarInfo) return <Text variant="white">{LocalizeText('generic.loading')}</Text>;
return (
<Column alignItems="end" gap={1}>
<Column className="nitro-infostand rounded">
<Column className="container-fluid content-area" gap={1} overflow="visible">
<PetHeader
name={avatarInfo.name}
petType={avatarInfo.petType}
petBreed={avatarInfo.petBreed}
onClose={onClose}
/>
{avatarInfo.petType === PetType.MONSTERPLANT ? (
<MonsterplantStats
avatarInfo={avatarInfo}
remainingGrowTime={remainingGrowTime}
remainingTimeToLive={remainingTimeToLive}
/>
) : (
<RegularPetStats avatarInfo={avatarInfo} />
)}
<div className="flex flex-col gap-1">
<div className="flex items-center gap-1">
<UserProfileIconView userId={avatarInfo.ownerId} />
<Text small wrap variant="white">
{LocalizeText('infostand.text.petowner', ['name'], [avatarInfo.ownerName])}
</Text>
</div>
</div>
</Column>
);
</Column>
<Flex gap={1} justifyContent="end">
{buttons.map(
(button) =>
button.condition && (
<Button key={button.action} variant="dark" onClick={() => processButtonAction(button.action)}>
{button.label}
</Button>
)
)}
</Flex>
</Column>
);
};

145
src/css/room/InfoStand.css Normal file
View File

@ -0,0 +1,145 @@
.nitro-use-product-confirmation {
width: 350px;
.product-preview {
display: flex;
justify-content: center;
align-items: center;
width: 75px; /* Aligned with .body-image.pet */
height: 80px;
background: url('@/assets/images/room-widgets/avatar-info/preview-background.png') no-repeat center;
.pet-image {
width: 75px;
height: 80px;
}
.monsterplant-image {
width: 75px;
height: 80px;
background: url('@/assets/images/room-widgets/furni-context-menu/monsterplant-preview.png') no-repeat center;
}
}
}
.nitro-infostand {
position: relative;
width: clamp(160px, 20vw, 190px); /* Responsive width */
z-index: 30;
pointer-events: auto;
background: #212131;
box-shadow: inset 0 5px rgba(38, 38, 57, 0.6), inset 0 -4px rgba(25, 25, 37, 0.6);
border-radius: 0.5rem;
padding: 10px;
@media (max-width: 576px) {
top: -67px;
padding: 8px;
.text {
font-size: 0.85rem;
}
}
.form-control-sm {
height: 25px;
min-height: 25px;
padding: 0.1rem 0.25rem;
}
.body-image {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
max-width: 68px;
border-radius: 0.5rem;
&.pet {
max-width: 75px;
}
&.bot {
background-image: url('@/assets/images/infostand/bot_background.png');
background-repeat: no-repeat;
background-position: center;
}
&.furni {
background-color: transparent;
margin-right: 0;
}
}
.body-image-plant {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
max-width: 68px;
height: 85px;
max-height: 90px;
border-radius: 0.5rem;
}
.badge-image {
width: 45px;
height: 45px;
}
.motto-content {
min-height: 18px;
}
.motto-input {
width: 100%;
height: 100%;
font-size: 12px;
padding: 0;
outline: 0;
border: 0;
color: rgba(255, 255, 255, 1);
position: relative;
background: transparent;
resize: none;
&:focus {
font-style: italic;
}
}
.flex-tags {
flex-wrap: wrap;
margin-bottom: -10px;
.text-tags {
padding: 2px;
border-radius: 3px;
background: #333;
margin-right: 5px;
margin-bottom: 10px;
cursor: pointer;
}
}
.button-container {
pointer-events: auto;
}
.pet-stats {
height: 18px;
transition: width 0.3s ease-in-out; /* Smooth progress bar animation */
}
}
.nitro-rarity-level {
width: 36px;
height: 28px;
background: url('@/assets/images/infostand/rarity-level.png');
div {
line-height: 28px;
text-align: center;
color: $black;
font-weight: bold;
}
}

View File

@ -29,6 +29,7 @@ import './css/notification/NotificationCenterView.css';
import './css/purse/PurseView.css';
import './css/room/InfoStand.css';
import './css/room/NavigatorRoomSettings.css';
import './css/room/RoomWidgets.css';