nitro-v36-with-renderer-v36
/
src /
components /
room /
widgets /
avatar-info /
menu /
AvatarInfoWidgetOwnAvatarView.tsx
AvatarInfoWidgetOwnAvatarView.tsx 19.7 Ko
Télécharger le fichier
import {
AvatarAction,
AvatarExpressionEnum,
CreateLinkEvent,
GetRoomEngine,
RoomControllerLevel,
RoomObjectCategory,
RoomObjectVariable,
RoomUnitDropHandItemComposer
} from '@octane/renderer';
import { Dispatch, FC, SetStateAction, useState } from 'react';
import {
AvatarInfoUser,
DispatchUiEvent,
GetCanStandUp,
GetConfigurationValue,
GetOwnPosture,
GetOwnRoomObject,
GetUserProfile,
HasHabboClub,
HasHabboVip,
IsRidingHorse,
LocalizeText,
localizeWithFallback,
PostureTypeEnum,
SendMessageComposer
} from '../../../../../api';
import { NativeTextTree } from '../../../../../common/native-text/NativeTextTree';
import { HelpNameChangeEvent } from '../../../../../events';
import { useAvatarEffects, useRoom, useWiredTools } from '../../../../../hooks';
import { ContextMenuHeaderView } from '../../context-menu/ContextMenuHeaderView';
import { ContextMenuListItemView } from '../../context-menu/ContextMenuListItemView';
import { ContextMenuView } from '../../context-menu/ContextMenuView';
interface AvatarInfoWidgetOwnAvatarViewProps {
avatarInfo: AvatarInfoUser;
isDancing: boolean;
setIsDecorating: Dispatch<SetStateAction<boolean>>;
onClose: () => void;
}
interface AirSign {
id: number;
label?: string;
icon?: string;
}
const MODE_NORMAL = 0;
const MODE_CLUB_DANCES = 1;
const MODE_EXPRESSIONS = 3;
const MODE_SIGNS = 4;
/** Official AvatarInfoWidget.isSwimming: the swim effects, not a posture. */
const SWIM_EFFECTS = [29, 30, 185];
const AIR_SIGNS: AirSign[] = [
{ id: 1, label: '1' },
{ id: 2, label: '2' },
{ id: 3, label: '3' },
{ id: 4, label: '4' },
{ id: 5, label: '5' },
{ id: 6, label: '6' },
{ id: 7, label: '7' },
{ id: 8, label: '8' },
{ id: 9, label: '9' },
{ id: 10, label: '10' },
{ id: 11, icon: 'heart' },
{ id: 12, icon: 'skull' },
{ id: 0, label: '0' },
{ id: 13, icon: 'exclamation' },
{ id: 15, icon: 'smile' },
{ id: 14, icon: 'soccer' },
{ id: 17, icon: 'yellow' },
{ id: 16, icon: 'red' }
];
export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProps> = (props) =>
{
const { avatarInfo = null, isDancing = false, setIsDecorating = null, onClose = null } = props;
const hasClub = HasHabboClub();
const hasVip = HasHabboVip();
const ownPosture = GetOwnPosture();
const ownRoomObject = GetOwnRoomObject();
const activeEffectId = ownRoomObject?.model.getValue<number>(RoomObjectVariable.FIGURE_EFFECT) ?? 0;
const { effects: inventoryEffects = [], activeEffectType = 0 } = useAvatarEffects();
// Official hasEffectOn: only an effect from the effects inventory in use counts, so riding or swimming does not.
const hasActiveEffect = activeEffectType > 0 && inventoryEffects.some((effect) => effect.type === activeEffectType);
const isRidingHorse = IsRidingHorse();
const isSwimming = SWIM_EFFECTS.includes(activeEffectId);
const [mode, setMode] = useState(isDancing && hasClub && !hasActiveEffect ? MODE_CLUB_DANCES : MODE_NORMAL);
const { roomSession = null } = useRoom();
const isHandItemBlocked = GetRoomEngine().activeRoomHasHanditemControlBlocked;
const { openInspectionForUser, showInspectButton } = useWiredTools();
const expressionsMenuEnabled = GetConfigurationValue('avatar.expressions_menu.enabled', true);
const signsEnabled = GetConfigurationValue('avatar.signs.enabled', true);
const handItemDropEnabled = GetConfigurationValue('handitem.drop.enabled', true);
const sittingEnabled = GetConfigurationValue('avatar.sitting.enabled', true);
const expression67Enabled = GetConfigurationValue('avatar.expression.67.enabled', false);
const processAction = (name: string) =>
{
let hideMenu = true;
if (name)
{
if (!hasVip && ['blow', 'dance_sixseven', 'expression_67', 'laugh'].includes(name))
{
CreateLinkEvent('habboUI/open/hccenter');
hideMenu = false;
}
else if (name.startsWith('sign_'))
{
const sign = parseInt(name.split('_')[1]);
roomSession.sendSignMessage(sign);
}
else
{
switch (name)
{
case 'decorate':
setIsDecorating(true);
break;
case 'change_name':
DispatchUiEvent(new HelpNameChangeEvent(HelpNameChangeEvent.INIT));
break;
case 'change_looks':
CreateLinkEvent('avatar-editor/show');
break;
case 'customize_nick':
CreateLinkEvent('customize/show');
break;
case 'badge_leaderboard':
CreateLinkEvent('badge-leaderboard/show');
break;
case 'expressions':
hideMenu = false;
setMode(MODE_EXPRESSIONS);
break;
case 'sit':
roomSession.sendPostureMessage(PostureTypeEnum.POSTURE_SIT);
break;
case 'stand':
roomSession.sendPostureMessage(PostureTypeEnum.POSTURE_STAND);
break;
case 'wave':
roomSession.sendExpressionMessage(AvatarExpressionEnum.WAVE.ordinal);
break;
case 'blow':
roomSession.sendExpressionMessage(AvatarExpressionEnum.BLOW.ordinal);
break;
case 'expression_67':
roomSession.sendExpressionMessage(AvatarExpressionEnum.EXPRESSION_67.ordinal);
break;
case 'dance_sixseven':
// A running dance would override the six-seven animation.
if (isDancing) roomSession.sendDanceMessage(0);
roomSession.sendExpressionMessage(AvatarExpressionEnum.EXPRESSION_67.ordinal);
break;
case 'laugh':
roomSession.sendExpressionMessage(AvatarExpressionEnum.LAUGH.ordinal);
break;
case 'idle':
roomSession.sendExpressionMessage(AvatarExpressionEnum.IDLE.ordinal);
break;
case 'dance_menu':
hideMenu = false;
setMode(MODE_CLUB_DANCES);
break;
case 'dance':
roomSession.sendDanceMessage(1);
break;
case 'dance_stop':
roomSession.sendDanceMessage(0);
break;
case 'dance_1':
case 'dance_2':
case 'dance_3':
case 'dance_4':
roomSession.sendDanceMessage(parseInt(name.charAt(name.length - 1)));
break;
case 'signs':
hideMenu = false;
setMode(MODE_SIGNS);
break;
case 'back':
hideMenu = false;
setMode(MODE_NORMAL);
break;
case 'drop_carry_item':
SendMessageComposer(new RoomUnitDropHandItemComposer());
break;
case 'inspect':
openInspectionForUser(avatarInfo.roomIndex);
break;
case 'customize_nick':
CreateLinkEvent('customize/show');
break;
case 'badge_leaderboard':
CreateLinkEvent('badge-leaderboard/show');
break;
}
}
}
if (hideMenu) onClose();
};
const isShowDecorate = () =>
hasClub && (avatarInfo.amIOwner || avatarInfo.amIAnyRoomController || avatarInfo.roomControllerLevel > RoomControllerLevel.GUEST);
const canUsePremiumExpression = !hasActiveEffect && !isSwimming;
const premiumExpressionClassNames = hasVip ? [] : ['air-avatar-menu-item--locked'];
return (
<NativeTextTree>
<ContextMenuView
category={RoomObjectCategory.UNIT}
classNames={['octane-avatar-action-menu', 'octane-avatar-action-menu--own']}
collapsable={true}
freezePositionOnHover={true}
maximumVerticalLeadRatio={0.05}
objectId={avatarInfo.roomIndex}
repositionKey={mode}
showCaretIcon={false}
tallAvatarOffset={25}
userType={avatarInfo.userType}
onClose={onClose}
>
<ContextMenuHeaderView className="cursor-pointer" onClick={() => GetUserProfile(avatarInfo.webID)}>
{avatarInfo.name}
</ContextMenuHeaderView>
<div className="air-avatar-menu-buttons">
{mode === MODE_NORMAL && (
<>
{avatarInfo.allowNameChange && (
<ContextMenuListItemView classNames={['air-avatar-menu-item--link']} onClick={() => processAction('change_name')}>
{LocalizeText('widget.avatar.change_name')}
</ContextMenuListItemView>
)}
{isShowDecorate() && (
<ContextMenuListItemView onClick={() => processAction('decorate')}>
{LocalizeText('widget.avatar.decorate')}
</ContextMenuListItemView>
)}
<ContextMenuListItemView classNames={['air-avatar-menu-item--link']} onClick={() => processAction('change_looks')}>
{LocalizeText('widget.memenu.myclothes')}
</ContextMenuListItemView>
{expressionsMenuEnabled ? (
<ContextMenuListItemView onClick={() => processAction('expressions')}>
{LocalizeText('infostand.link.expressions')}
</ContextMenuListItemView>
) : (
<ContextMenuListItemView onClick={() => processAction('wave')}>{LocalizeText('widget.memenu.wave')}</ContextMenuListItemView>
)}
{hasClub && !isRidingHorse && (
<ContextMenuListItemView disabled={hasActiveEffect} onClick={() => processAction('dance_menu')}>
{LocalizeText('widget.memenu.dance')}
</ContextMenuListItemView>
)}
{!isDancing && !hasClub && !isRidingHorse && (
<ContextMenuListItemView disabled={hasActiveEffect} onClick={() => processAction('dance')}>
{LocalizeText('widget.memenu.dance')}
</ContextMenuListItemView>
)}
{isDancing && !hasClub && !isRidingHorse && (
<ContextMenuListItemView onClick={() => processAction('dance_stop')}>
{LocalizeText('widget.memenu.dance.stop')}
</ContextMenuListItemView>
)}
{signsEnabled && (
<ContextMenuListItemView onClick={() => processAction('signs')}>
{LocalizeText('infostand.show.signs')}
</ContextMenuListItemView>
)}
{avatarInfo.carryItem > 0 && avatarInfo.carryItem < 999999 && handItemDropEnabled && !isHandItemBlocked && (
<ContextMenuListItemView onClick={() => processAction('drop_carry_item')}>
{LocalizeText('avatar.widget.drop_hand_item')}
</ContextMenuListItemView>
)}
{showInspectButton && (
<ContextMenuListItemView onClick={() => processAction('inspect')}>
{LocalizeText('infostand.button.wired_inspect')}
</ContextMenuListItemView>
)}
{/* Polaris-only actions are appended after the official AIR rows. */}
<ContextMenuListItemView onClick={() => processAction('customize_nick')}>
<span className="air-avatar-menu-extra-label">
{localizeWithFallback('widget.memenu.customize_nick', 'Custom nickname')}
</span>
</ContextMenuListItemView>
<ContextMenuListItemView onClick={() => processAction('badge_leaderboard')}>
<span className="air-avatar-menu-extra-label">
{localizeWithFallback('badge_leaderboard.title.total_badges', 'Badge leaderboard')}
</span>
</ContextMenuListItemView>
</>
)}
{mode === MODE_CLUB_DANCES && (
<>
<ContextMenuListItemView disabled={!isDancing} onClick={() => processAction('dance_stop')}>
{LocalizeText('widget.memenu.dance.stop')}
</ContextMenuListItemView>
<ContextMenuListItemView onClick={() => processAction('dance_1')}>{LocalizeText('widget.memenu.dance1')}</ContextMenuListItemView>
<ContextMenuListItemView onClick={() => processAction('dance_2')}>{LocalizeText('widget.memenu.dance2')}</ContextMenuListItemView>
<ContextMenuListItemView onClick={() => processAction('dance_3')}>{LocalizeText('widget.memenu.dance3')}</ContextMenuListItemView>
<ContextMenuListItemView onClick={() => processAction('dance_4')}>{LocalizeText('widget.memenu.dance4')}</ContextMenuListItemView>
<ContextMenuListItemView
classNames={premiumExpressionClassNames}
disabled={hasVip && !canUsePremiumExpression}
onClick={() => processAction('dance_sixseven')}
>
{!hasVip && <i className="air-avatar-menu-vip" aria-hidden="true" />}
{localizeWithFallback('widget.memenu.dance.sixseven', 'Six Seven')}
</ContextMenuListItemView>
<ContextMenuListItemView onClick={() => processAction('back')}>
{LocalizeText('generic.back')}
</ContextMenuListItemView>
</>
)}
{mode === MODE_EXPRESSIONS && (
<>
{sittingEnabled && !isSwimming && !isRidingHorse && ownPosture === AvatarAction.POSTURE_STAND && (
<ContextMenuListItemView onClick={() => processAction('sit')}>{LocalizeText('widget.memenu.sit')}</ContextMenuListItemView>
)}
{sittingEnabled && !isSwimming && !isRidingHorse && GetCanStandUp() && (
<ContextMenuListItemView onClick={() => processAction('stand')}>{LocalizeText('widget.memenu.stand')}</ContextMenuListItemView>
)}
<ContextMenuListItemView disabled={isSwimming} onClick={() => processAction('wave')}>
{LocalizeText('widget.memenu.wave')}
</ContextMenuListItemView>
<ContextMenuListItemView
classNames={premiumExpressionClassNames}
disabled={hasVip && !canUsePremiumExpression}
onClick={() => processAction('blow')}
>
{!hasVip && <i className="air-avatar-menu-vip" aria-hidden="true" />}
{LocalizeText('widget.memenu.blow')}
</ContextMenuListItemView>
{expression67Enabled && (
<ContextMenuListItemView
classNames={premiumExpressionClassNames}
disabled={hasVip && !canUsePremiumExpression}
onClick={() => processAction('expression_67')}
>
{!hasVip && <i className="air-avatar-menu-vip" aria-hidden="true" />}
{LocalizeText('widget.memenu.expression_67')}
</ContextMenuListItemView>
)}
<ContextMenuListItemView
classNames={premiumExpressionClassNames}
disabled={hasVip && !canUsePremiumExpression}
onClick={() => processAction('laugh')}
>
{!hasVip && <i className="air-avatar-menu-vip" aria-hidden="true" />}
{LocalizeText('widget.memenu.laugh')}
</ContextMenuListItemView>
<ContextMenuListItemView onClick={() => processAction('idle')}>{LocalizeText('widget.memenu.idle')}</ContextMenuListItemView>
<ContextMenuListItemView onClick={() => processAction('back')}>
{LocalizeText('generic.back')}
</ContextMenuListItemView>
</>
)}
{mode === MODE_SIGNS && (
<>
<div className="air-avatar-menu-signs-grid">
{AIR_SIGNS.map((sign) => (
<ContextMenuListItemView
key={sign.id}
classNames={['air-avatar-menu-sign-cell']}
onClick={() => processAction(`sign_${sign.id}`)}
>
{sign.label}
{sign.icon && <i className={`air-avatar-menu-sign-icon air-avatar-menu-sign-icon--${sign.icon}`} aria-hidden="true" />}
</ContextMenuListItemView>
))}
</div>
<ContextMenuListItemView onClick={() => processAction('back')}>
{LocalizeText('generic.back')}
</ContextMenuListItemView>
</>
)}
</div>
</ContextMenuView>
</NativeTextTree>
);
};
