Merge pull request #68 from duckietm/Dev

Dev
This commit is contained in:
DuckieTM
2026-03-31 16:05:38 +02:00
committed by GitHub
13 changed files with 1770 additions and 75 deletions
+22 -14
View File
@@ -39,18 +39,26 @@
"wiredfurni.params.texts.placeholder_type.1": "Singolo",
"wiredfurni.params.texts.placeholder_type.2": "Multiplo",
"wiredfurni.params.texts.select_delimiter": "Seleziona il delimitatore:",
"groupforum.list.tab.most_active": "Meest active threads",
"groupforum.list.tab.my_forums": "Mijn group forums",
"groupforum.list.no_forums": "Er zijn geen forums",
"groupforum.view.threads": "Aantal threads",
"groupforum.thread.pin": "Pin hem vast",
"groupforum.thread.unpin": "Unpin bericht",
"groupforum.thread.lock": "Lock de thread",
"groupforum.thread.unlock": "Unlock de thread",
"groupforum.thread.hide": "Verberg thread",
"groupforum.thread.restore": "Maak thread weer zichtbaar",
"groupforum.thread.delete": "Verwijder thread + posts",
"groupforum.message.hide": "Verberg bericht",
"group.forum.enable.caption": "Enable / Disable Group forum",
"group.forum.enable.help": "Als je de group forum disabled dan verwijderen ook alle posts!"
"widget.memenu.dance1": "Ballo 1",
"widget.memenu.dance2": "Ballo 2",
"widget.memenu.dance3": "Ballo 3",
"widget.memenu.dance4": "Ballo 4",
"wiredfurni.params.action.sign.0": "Cartello 0",
"wiredfurni.params.action.sign.1": "Cartello 1",
"wiredfurni.params.action.sign.2": "Cartello 2",
"wiredfurni.params.action.sign.3": "Cartello 3",
"wiredfurni.params.action.sign.4": "Cartello 4",
"wiredfurni.params.action.sign.5": "Cartello 5",
"wiredfurni.params.action.sign.6": "Cartello 6",
"wiredfurni.params.action.sign.7": "Cartello 7",
"wiredfurni.params.action.sign.8": "Cartello 8",
"wiredfurni.params.action.sign.9": "Cartello 9",
"wiredfurni.params.action.sign.10": "Cartello 10",
"wiredfurni.params.action.sign.11": "Cartello 11",
"wiredfurni.params.action.sign.12": "Cartello 12",
"wiredfurni.params.action.sign.13": "Cartello 13",
"wiredfurni.params.action.sign.14": "Cartello 14",
"wiredfurni.params.action.sign.15": "Cartello 15",
"wiredfurni.params.action.sign.16": "Cartello 16",
"wiredfurni.params.action.sign.17": "Cartello 17"
}
+1 -1
View File
@@ -26,7 +26,7 @@
"game.center.enabled": false,
"guides.enabled": true,
"toolbar.hide.quests": true,
"catalog.style.new": true,
"catalog.style.new": false,
"navigator.room.models": [{
"clubLevel": 0,
"tileSize": 104,
+5
View File
@@ -31,6 +31,11 @@ export class AvatarInfoFurni implements IAvatarInfo
public availableForBuildersClub: boolean = false;
public tileSizeX: number = 1;
public tileSizeY: number = 1;
public allowStack: boolean = false;
public allowSit: boolean = false;
public allowLay: boolean = false;
public allowWalk: boolean = false;
public teleportTargetId: number = 0;
constructor(public readonly type: string)
{}
@@ -152,6 +152,18 @@ export class AvatarInfoUtilities
furniInfo.ownerId = model.getValue<number>(RoomObjectVariable.FURNITURE_OWNER_ID);
furniInfo.ownerName = model.getValue<string>(RoomObjectVariable.FURNITURE_OWNER_NAME);
furniInfo.usagePolicy = model.getValue<number>(RoomObjectVariable.FURNITURE_USAGE_POLICY);
furniInfo.allowStack = (model.getValue<number>(RoomObjectVariable.FURNITURE_ALLOW_STACK) > 0);
furniInfo.allowSit = (model.getValue<number>(RoomObjectVariable.FURNITURE_ALLOW_SIT) > 0);
furniInfo.allowLay = (model.getValue<number>(RoomObjectVariable.FURNITURE_ALLOW_LAY) > 0);
furniInfo.allowWalk = (model.getValue<number>(RoomObjectVariable.FURNITURE_ALLOW_WALK) > 0);
furniInfo.teleportTargetId = Number(model.getValue<number>(RoomObjectVariable.FURNITURE_TELEPORT_TARGET_ID) ?? 0);
const dimensionsX = model.getValue<number>(RoomObjectVariable.FURNITURE_DIMENSIONS_X);
const dimensionsY = model.getValue<number>(RoomObjectVariable.FURNITURE_DIMENSIONS_Y);
if(dimensionsX > 0) furniInfo.tileSizeX = dimensionsX;
if(dimensionsY > 0) furniInfo.tileSizeY = dimensionsY;
const guildId = model.getValue<number>(RoomObjectVariable.FURNITURE_GUILD_CUSTOMIZED_GUILD_ID);
+1
View File
@@ -66,4 +66,5 @@ export class WiredActionLayoutCode
public static EXECUTION_LIMIT_EXTRA: number = 65;
public static OR_EVAL_EXTRA: number = 66;
public static TEXT_OUTPUT_USERNAME_EXTRA: number = 67;
public static TEXT_OUTPUT_FURNI_NAME_EXTRA: number = 68;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

@@ -17,42 +17,48 @@ export const FurnitureYoutubeDisplayView: FC<{}> = FurnitureYoutubeDisplayViewPr
const onStateChange = (event: { target: YouTubePlayer; data: number }) =>
{
setPlayer(event.target);
if(objectId === -1) return;
switch(event.target.getPlayerState())
try
{
case -1:
case 1:
if(currentVideoState === 2)
{
//event.target.pauseVideo();
}
setPlayer(event.target);
if(currentVideoState !== 1) play();
return;
case 2:
if(currentVideoState !== 2) pause();
if(objectId === -1) return;
switch(event.target.getPlayerState())
{
case -1:
case 1:
if(currentVideoState !== 1) play();
return;
case 2:
if(currentVideoState !== 2) pause();
}
}
catch(err) {}
};
useEffect(() =>
{
if((currentVideoState === null) || !player) return;
if((currentVideoState === YoutubeVideoPlaybackStateEnum.PLAYING) && (player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PLAYING))
try
{
player.playVideo();
if((currentVideoState === YoutubeVideoPlaybackStateEnum.PLAYING) && (player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PLAYING))
{
player.playVideo();
return;
return;
}
if((currentVideoState === YoutubeVideoPlaybackStateEnum.PAUSED) && (player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PAUSED))
{
player.pauseVideo();
return;
}
}
if((currentVideoState === YoutubeVideoPlaybackStateEnum.PAUSED) && (player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PAUSED))
catch(err)
{
player.pauseVideo();
return;
setPlayer(null);
}
}, [ currentVideoState, player ]);
File diff suppressed because it is too large Load Diff
@@ -60,6 +60,7 @@ import { WiredExtraMoveNoAnimationView } from '../extras/WiredExtraMoveNoAnimati
import { WiredExtraOrEvalView } from '../extras/WiredExtraOrEvalView';
import { WiredExtraMovePhysicsView } from '../extras/WiredExtraMovePhysicsView';
import { WiredExtraRandomView } from '../extras/WiredExtraRandomView';
import { WiredExtraTextOutputFurniNameView } from '../extras/WiredExtraTextOutputFurniNameView';
import { WiredExtraTextOutputUsernameView } from '../extras/WiredExtraTextOutputUsernameView';
import { WiredExtraUnseenView } from '../extras/WiredExtraUnseenView';
@@ -195,6 +196,8 @@ export const WiredActionLayoutView = (code: number) =>
return <WiredExtraOrEvalView />;
case WiredActionLayoutCode.TEXT_OUTPUT_USERNAME_EXTRA:
return <WiredExtraTextOutputUsernameView />;
case WiredActionLayoutCode.TEXT_OUTPUT_FURNI_NAME_EXTRA:
return <WiredExtraTextOutputFurniNameView />;
case WiredActionLayoutCode.SEND_SIGNAL:
return <WiredActionSendSignalView />;
}
@@ -0,0 +1,123 @@
import { FC, useEffect, useMemo, useState } from 'react';
import { LocalizeText, WiredFurniType } from '../../../../api';
import { Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { NitroInput } from '../../../../layout';
import { WiredSourcesSelector } from '../WiredSourcesSelector';
import { WiredExtraBaseView } from './WiredExtraBaseView';
const TYPE_SINGLE = 1;
const TYPE_MULTIPLE = 2;
const DEFAULT_PLACEHOLDER_NAME = '';
const DEFAULT_DELIMITER = ', ';
const MAX_PLACEHOLDER_NAME_LENGTH = 32;
const MAX_DELIMITER_LENGTH = 16;
const PLACEHOLDER_WRAPPER_PATTERN = /^\$\((.*)\)$/;
const normalizePlaceholderType = (value: number) => ((value === TYPE_MULTIPLE) ? TYPE_MULTIPLE : TYPE_SINGLE);
const normalizeFurniSource = (value: number) => ((value === 0) || (value === 100) || (value === 200) || (value === 201) ? value : 0);
const normalizePlaceholderName = (value: string) =>
{
let normalizedValue = (value ?? '').trim().replace(/[\t\r\n]/g, '');
if(PLACEHOLDER_WRAPPER_PATTERN.test(normalizedValue))
{
normalizedValue = normalizedValue.substring(2, normalizedValue.length - 1).trim();
}
return normalizedValue.slice(0, MAX_PLACEHOLDER_NAME_LENGTH);
};
const normalizeDelimiter = (value: string) =>
{
if(value === undefined || value === null) return DEFAULT_DELIMITER;
return value.replace(/[\t\r\n]/g, '').slice(0, MAX_DELIMITER_LENGTH);
};
const splitStringData = (value: string) =>
{
if(!value?.length) return [ DEFAULT_PLACEHOLDER_NAME, DEFAULT_DELIMITER ];
const parts = value.split('\t');
if(parts.length <= 1) return [ value, DEFAULT_DELIMITER ];
return [ parts[0], parts[1] ];
};
const escapeHtml = (value: string) => value
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
export const WiredExtraTextOutputFurniNameView: FC<{}> = () =>
{
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
const [ placeholderName, setPlaceholderName ] = useState(DEFAULT_PLACEHOLDER_NAME);
const [ placeholderType, setPlaceholderType ] = useState(TYPE_SINGLE);
const [ delimiter, setDelimiter ] = useState(DEFAULT_DELIMITER);
const [ furniSource, setFurniSource ] = useState(0);
useEffect(() =>
{
if(!trigger) return;
const [ nextPlaceholderName, nextDelimiter ] = splitStringData(trigger.stringData);
setPlaceholderName(normalizePlaceholderName(nextPlaceholderName));
setDelimiter(normalizeDelimiter(nextDelimiter));
setPlaceholderType(normalizePlaceholderType((trigger.intData.length > 0) ? trigger.intData[0] : TYPE_SINGLE));
setFurniSource(normalizeFurniSource((trigger.intData.length > 1) ? trigger.intData[1] : 0));
}, [ trigger ]);
const previewToken = useMemo(() =>
{
const effectiveName = normalizePlaceholderName(placeholderName) || 'placeholder';
return `$(${ effectiveName })`;
}, [ placeholderName ]);
const previewHtml = useMemo(() => LocalizeText('wiredfurni.params.texts.placeholder_preview', [ 'placeholder' ], [ escapeHtml(previewToken) ]), [ previewToken ]);
const save = () =>
{
setIntParams([ normalizePlaceholderType(placeholderType), normalizeFurniSource(furniSource) ]);
setStringParam(`${ normalizePlaceholderName(placeholderName) }\t${ normalizeDelimiter(delimiter) }`);
};
return (
<WiredExtraBaseView
hasSpecialInput={ true }
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT }
save={ save }
cardStyle={ { width: 400 } }
footer={ <WiredSourcesSelector showFurni={ true } furniSource={ furniSource } onChangeFurni={ value => setFurniSource(normalizeFurniSource(value)) } /> }>
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-1">
<Text>{ LocalizeText('wiredfurni.params.texts.placeholder_name') }</Text>
<NitroInput maxLength={ MAX_PLACEHOLDER_NAME_LENGTH } type="text" value={ placeholderName } onChange={ event => setPlaceholderName(normalizePlaceholderName(event.target.value)) } />
</div>
<Text dangerouslySetInnerHTML={ { __html: previewHtml } } />
<div className="flex flex-col gap-1">
<Text>{ LocalizeText('wiredfurni.params.texts.placeholder_type') }</Text>
<label className="flex items-center gap-1 cursor-pointer">
<input checked={ (placeholderType === TYPE_SINGLE) } className="form-check-input" name="wiredTextOutputFurniNameType" type="radio" onChange={ () => setPlaceholderType(TYPE_SINGLE) } />
<Text>{ LocalizeText('wiredfurni.params.texts.placeholder_type.1') }</Text>
</label>
<label className="flex items-center gap-1 cursor-pointer">
<input checked={ (placeholderType === TYPE_MULTIPLE) } className="form-check-input" name="wiredTextOutputFurniNameType" type="radio" onChange={ () => setPlaceholderType(TYPE_MULTIPLE) } />
<Text>{ LocalizeText('wiredfurni.params.texts.placeholder_type.2') }</Text>
</label>
</div>
{ placeholderType === TYPE_MULTIPLE &&
<div className="flex flex-col gap-1">
<Text>{ LocalizeText('wiredfurni.params.texts.select_delimiter') }</Text>
<NitroInput maxLength={ MAX_DELIMITER_LENGTH } type="text" value={ delimiter } onChange={ event => setDelimiter(normalizeDelimiter(event.target.value)) } />
</div> }
</div>
</WiredExtraBaseView>
);
};
+9 -7
View File
@@ -60,7 +60,7 @@ export const useFurniEditor = () =>
const [ catalogItems, setCatalogItems ] = useState<CatalogRef[]>([]);
const [ interactions, setInteractions ] = useState<string[]>([]);
const [ furniDataEntry, setFurniDataEntry ] = useState<Record<string, unknown> | null>(null);
const pendingActionRef = useRef<string | null>(null);
const pendingActionRef = useRef<{ action: string; itemId: number } | null>(null);
const { simpleAlert = null } = useNotification();
const clearError = useCallback(() => setError(null), []);
@@ -161,7 +161,9 @@ export const useFurniEditor = () =>
useMessageEvent(FurniEditorResultEvent, (event: FurniEditorResultEvent) =>
{
const parser = event.getParser();
const action = pendingActionRef.current;
const pending = pendingActionRef.current;
const action = pending?.action ?? null;
const actionItemId = pending?.itemId ?? null;
pendingActionRef.current = null;
setLoading(false);
@@ -182,10 +184,10 @@ export const useFurniEditor = () =>
if(action === 'update')
{
// Auto-reload detail after update
if(selectedItem)
// Auto-reload detail after update using the ID from the original request
if(actionItemId)
{
SendMessageComposer(new FurniEditorDetailComposer(selectedItem.id));
SendMessageComposer(new FurniEditorDetailComposer(actionItemId));
}
if(simpleAlert)
@@ -231,7 +233,7 @@ export const useFurniEditor = () =>
{
setLoading(true);
setError(null);
pendingActionRef.current = 'update';
pendingActionRef.current = { action: 'update', itemId: id };
SendMessageComposer(new FurniEditorUpdateComposer(id, JSON.stringify(fields)));
}, []);
@@ -239,7 +241,7 @@ export const useFurniEditor = () =>
{
setLoading(true);
setError(null);
pendingActionRef.current = 'delete';
pendingActionRef.current = { action: 'delete', itemId: id };
SendMessageComposer(new FurniEditorDeleteComposer(id));
}, []);
@@ -1,5 +1,5 @@
import { ControlYoutubeDisplayPlaybackMessageComposer, GetRoomEngine, GetSessionDataManager, GetYoutubeDisplayStatusMessageComposer, RoomEngineTriggerWidgetEvent, RoomId, SecurityLevel, SetYoutubeDisplayPlaylistMessageComposer, YoutubeControlVideoMessageEvent, YoutubeDisplayPlaylist, YoutubeDisplayPlaylistsEvent, YoutubeDisplayVideoMessageEvent } from '@nitrots/nitro-renderer';
import { useState } from 'react';
import { useRef, useState } from 'react';
import { IsOwnerOfFurniture, SendMessageComposer, YoutubeVideoPlaybackStateEnum } from '../../../../api';
import { useMessageEvent, useNitroEvent } from '../../../events';
import { useFurniRemovedEvent } from '../../engine';
@@ -13,6 +13,7 @@ const useFurnitureYoutubeWidgetState = () =>
{
const [ objectId, setObjectId ] = useState(-1);
const [ category, setCategory ] = useState(-1);
const objectIdRef = useRef(-1);
const [ videoId, setVideoId ] = useState<string>(null);
const [ videoStart, setVideoStart ] = useState<number>(null);
const [ videoEnd, setVideoEnd ] = useState<number>(null);
@@ -23,6 +24,7 @@ const useFurnitureYoutubeWidgetState = () =>
const onClose = () =>
{
objectIdRef.current = -1;
setObjectId(-1);
setCategory(-1);
setVideoId(null);
@@ -64,6 +66,7 @@ const useFurnitureYoutubeWidgetState = () =>
if(!roomObject) return;
objectIdRef.current = event.objectId;
setObjectId(event.objectId);
setCategory(event.category);
setHasControl(GetSessionDataManager().hasSecurity(SecurityLevel.EMPLOYEE) || IsOwnerOfFurniture(roomObject));
@@ -74,8 +77,9 @@ const useFurnitureYoutubeWidgetState = () =>
useMessageEvent<YoutubeDisplayVideoMessageEvent>(YoutubeDisplayVideoMessageEvent, event =>
{
const parser = event.getParser();
const currentObjectId = objectIdRef.current;
if((objectId === -1) || (objectId !== parser.furniId)) return;
if((currentObjectId === -1) || (currentObjectId !== parser.furniId)) return;
setVideoId(parser.videoId);
setVideoStart(parser.startAtSeconds);
@@ -86,8 +90,9 @@ const useFurnitureYoutubeWidgetState = () =>
useMessageEvent<YoutubeDisplayPlaylistsEvent>(YoutubeDisplayPlaylistsEvent, event =>
{
const parser = event.getParser();
const currentObjectId = objectIdRef.current;
if((objectId === -1) || (objectId !== parser.furniId)) return;
if((currentObjectId === -1) || (currentObjectId !== parser.furniId)) return;
setPlaylists(parser.playlists);
setSelectedVideo(parser.selectedPlaylistId);
@@ -100,8 +105,9 @@ const useFurnitureYoutubeWidgetState = () =>
useMessageEvent<YoutubeControlVideoMessageEvent>(YoutubeControlVideoMessageEvent, event =>
{
const parser = event.getParser();
const currentObjectId = objectIdRef.current;
if((objectId === -1) || (objectId !== parser.furniId)) return;
if((currentObjectId === -1) || (currentObjectId !== parser.furniId)) return;
switch(parser.commandId)
{