Trade updates

This commit is contained in:
Bill 2021-07-05 02:46:47 -04:00
parent 4ea7dad180
commit 86233806f2
23 changed files with 709 additions and 61 deletions

View File

@ -14,6 +14,10 @@
transform: scale(2) translateZ(0); transform: scale(2) translateZ(0);
} }
.opacity-0-5 {
opacity: 0.5;
}
.text-shadow { .text-shadow {
text-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); text-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
} }

View File

@ -0,0 +1,27 @@
import { InventoryEvent } from './InventoryEvent';
export class InventoryTradeRequestEvent extends InventoryEvent
{
public static REQUEST_TRADE: string = 'ITSE_REQUEST_TRADE';
private _objectId: number;
private _username: string;
constructor(objectId: number, username: string)
{
super(InventoryTradeRequestEvent.REQUEST_TRADE);
this._objectId = objectId;
this._username = username;
}
public get objectId(): number
{
return this._objectId;
}
public get username(): string
{
return this._username;
}
}

View File

@ -0,0 +1,28 @@
import { TradeUserData } from '../../views/inventory/common/TradeUserData';
import { InventoryEvent } from './InventoryEvent';
export class InventoryTradeStartEvent extends InventoryEvent
{
public static START_TRADE: string = 'ITSE_START_TRADE';
private _ownUserTradeData: TradeUserData;
private _otherUserTradeData: TradeUserData;
constructor(ownUserTradeData: TradeUserData, otherUserTradeData: TradeUserData)
{
super(InventoryTradeStartEvent.START_TRADE);
this._ownUserTradeData = ownUserTradeData;
this._otherUserTradeData = otherUserTradeData;
}
public get ownUserTradeData(): TradeUserData
{
return this._ownUserTradeData;
}
public get otherUserTradeData(): TradeUserData
{
return this._otherUserTradeData;
}
}

View File

@ -1 +1,3 @@
export * from './InventoryEvent'; export * from './InventoryEvent';
export * from './InventoryTradeRequestEvent';
export * from './InventoryTradeStartEvent';

View File

@ -1,15 +1,17 @@
import { BadgesEvent, BotAddedToInventoryEvent, BotInventoryMessageEvent, BotRemovedFromInventoryEvent, FurnitureListAddOrUpdateEvent, FurnitureListEvent, FurnitureListInvalidateEvent, FurnitureListItemParser, FurnitureListRemovedEvent, FurniturePostItPlacedEvent, PetAddedToInventoryEvent, PetData, PetInventoryEvent, PetRemovedFromInventory } from 'nitro-renderer'; import { AdvancedMap, BadgesEvent, BotAddedToInventoryEvent, BotInventoryMessageEvent, BotRemovedFromInventoryEvent, FurnitureListAddOrUpdateEvent, FurnitureListEvent, FurnitureListInvalidateEvent, FurnitureListItemParser, FurnitureListRemovedEvent, FurniturePostItPlacedEvent, PetAddedToInventoryEvent, PetData, PetInventoryEvent, PetRemovedFromInventory, TradingAcceptEvent, TradingCloseEvent, TradingCompletedEvent, TradingConfirmationEvent, TradingListItemEvent, TradingOpenEvent } from 'nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { GetRoomSession, GetSessionDataManager } from '../../api';
import { CreateMessageHook } from '../../hooks/messages/message-event'; import { CreateMessageHook } from '../../hooks/messages/message-event';
import { mergeFurniFragments } from './common/FurnitureUtilities'; import { mergeFurniFragments } from './common/FurnitureUtilities';
import { mergePetFragments } from './common/PetUtilities'; import { mergePetFragments } from './common/PetUtilities';
import { TradeState } from './common/TradeState';
import { TradeUserData } from './common/TradeUserData';
import { useInventoryContext } from './context/InventoryContext'; import { useInventoryContext } from './context/InventoryContext';
import { InventoryMessageHandlerProps } from './InventoryMessageHandler.types'; import { InventoryMessageHandlerProps } from './InventoryMessageHandler.types';
import { InventoryBadgeActions } from './reducers/InventoryBadgeReducer'; import { InventoryBadgeActions } from './reducers/InventoryBadgeReducer';
import { InventoryBotActions } from './reducers/InventoryBotReducer'; import { InventoryBotActions } from './reducers/InventoryBotReducer';
import { InventoryFurnitureActions } from './reducers/InventoryFurnitureReducer'; import { InventoryFurnitureActions } from './reducers/InventoryFurnitureReducer';
import { InventoryPetActions } from './reducers/InventoryPetReducer'; import { InventoryPetActions } from './reducers/InventoryPetReducer';
let furniMsgFragments: Map<number, FurnitureListItemParser>[] = null; let furniMsgFragments: Map<number, FurnitureListItemParser>[] = null;
let petMsgFragments: Map<number, PetData>[] = null; let petMsgFragments: Map<number, PetData>[] = null;
@ -161,6 +163,111 @@ export const InventoryMessageHandler: FC<InventoryMessageHandlerProps> = props =
}); });
}, [ dispatchBadgeState ]); }, [ dispatchBadgeState ]);
const onTradingAcceptEvent = useCallback((event: TradingAcceptEvent) =>
{
const parser = event.getParser();
dispatchFurnitureState({
type: InventoryFurnitureActions.SET_TRADE_ACCEPTANCE,
payload: {
userId: parser.userID,
flag: parser.userAccepts
}
});
}, [ dispatchFurnitureState ]);
const onTradingOpenEvent = useCallback((event: TradingOpenEvent) =>
{
const parser = event.getParser();
const ownUser = new TradeUserData();
const otherUser = new TradeUserData();
ownUser.userItems = new AdvancedMap();
otherUser.userItems = new AdvancedMap();
const userDataOne = GetRoomSession().userDataManager.getUserData(parser.userID);
const userDataTwo = GetRoomSession().userDataManager.getUserData(parser.otherUserID);
if(userDataOne.webID === GetSessionDataManager().userId)
{
ownUser.userId = userDataOne.webID;
ownUser.userName = userDataOne.name;
ownUser.canTrade = parser.userCanTrade;
otherUser.userId = userDataTwo.webID;
otherUser.userName = userDataTwo.name;
otherUser.canTrade = parser.otherUserCanTrade;
}
else if(userDataTwo.webID === GetSessionDataManager().userId)
{
ownUser.userId = userDataTwo.webID;
ownUser.userName = userDataTwo.name;
ownUser.canTrade = parser.otherUserCanTrade;
otherUser.userId = userDataOne.webID;
otherUser.userName = userDataOne.name;
otherUser.canTrade = parser.userCanTrade;
}
dispatchFurnitureState({
type: InventoryFurnitureActions.SET_TRADE_DATA,
payload: {
ownTradeUser: ownUser,
otherTradeUser: otherUser
}
});
}, [ dispatchFurnitureState ]);
const onTradingCloseEvent = useCallback((event: TradingCloseEvent) =>
{
const parser = event.getParser();
dispatchFurnitureState({
type: InventoryFurnitureActions.CLOSE_TRADE,
payload: {}
});
}, [ dispatchFurnitureState ]);
const onTradingCompletedEvent = useCallback((event: TradingCompletedEvent) =>
{
const parser = event.getParser();
dispatchFurnitureState({
type: InventoryFurnitureActions.SET_TRADE_STATE,
payload: {
tradeState: TradeState.TRADING_STATE_COMPLETED
}
});
}, [ dispatchFurnitureState ]);
const onTradingConfirmationEvent = useCallback((event: TradingConfirmationEvent) =>
{
const parser = event.getParser();
dispatchFurnitureState({
type: InventoryFurnitureActions.SET_TRADE_STATE,
payload: {
tradeState: TradeState.TRADING_STATE_CONFIRMED
}
});
}, [ dispatchFurnitureState ]);
const onTradingListItemEvent = useCallback((event: TradingListItemEvent) =>
{
const parser = event.getParser();
console.log(parser);
dispatchFurnitureState({
type: InventoryFurnitureActions.UPDATE_TRADE,
payload: {
tradeParser: event.getParser()
}
});
}, [ dispatchFurnitureState ]);
CreateMessageHook(FurnitureListAddOrUpdateEvent, onFurnitureListAddOrUpdateEvent); CreateMessageHook(FurnitureListAddOrUpdateEvent, onFurnitureListAddOrUpdateEvent);
CreateMessageHook(FurnitureListEvent, onFurnitureListEvent); CreateMessageHook(FurnitureListEvent, onFurnitureListEvent);
CreateMessageHook(FurnitureListInvalidateEvent, onFurnitureListInvalidateEvent); CreateMessageHook(FurnitureListInvalidateEvent, onFurnitureListInvalidateEvent);
@ -173,6 +280,12 @@ export const InventoryMessageHandler: FC<InventoryMessageHandlerProps> = props =
CreateMessageHook(PetRemovedFromInventory, onPetRemovedFromInventory); CreateMessageHook(PetRemovedFromInventory, onPetRemovedFromInventory);
CreateMessageHook(PetAddedToInventoryEvent, onPetAddedToInventoryEvent); CreateMessageHook(PetAddedToInventoryEvent, onPetAddedToInventoryEvent);
CreateMessageHook(BadgesEvent, onBadgesEvent); CreateMessageHook(BadgesEvent, onBadgesEvent);
CreateMessageHook(TradingAcceptEvent, onTradingAcceptEvent);
CreateMessageHook(TradingOpenEvent, onTradingOpenEvent);
CreateMessageHook(TradingCloseEvent, onTradingCloseEvent);
CreateMessageHook(TradingCompletedEvent, onTradingCompletedEvent);
CreateMessageHook(TradingConfirmationEvent, onTradingConfirmationEvent);
CreateMessageHook(TradingListItemEvent, onTradingListItemEvent);
return null; return null;
} }

View File

@ -6,7 +6,7 @@
height: 240px; height: 240px;
max-height: 430px; max-height: 430px;
resize: vertical; resize: vertical;
overflow:auto; overflow: auto;
} }
.empty-image { .empty-image {

View File

@ -1,7 +1,7 @@
import { IRoomSession, RoomEngineObjectEvent, RoomEngineObjectPlacedEvent, RoomPreviewer, RoomSessionEvent } from 'nitro-renderer'; import { IRoomSession, RoomEngineObjectEvent, RoomEngineObjectPlacedEvent, RoomPreviewer, RoomSessionEvent, TradingOpenComposer } from 'nitro-renderer';
import { FC, useCallback, useEffect, useReducer, useState } from 'react'; import { FC, useCallback, useEffect, useReducer, useState } from 'react';
import { GetRoomEngine } from '../../api'; import { GetConnection, GetRoomEngine } from '../../api';
import { InventoryEvent } from '../../events'; import { InventoryEvent, InventoryTradeRequestEvent } from '../../events';
import { useRoomEngineEvent } from '../../hooks/events/nitro/room/room-engine-event'; import { useRoomEngineEvent } from '../../hooks/events/nitro/room/room-engine-event';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event'; import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { useUiEvent } from '../../hooks/events/ui/ui-event'; import { useUiEvent } from '../../hooks/events/ui/ui-event';
@ -19,11 +19,12 @@ import { InventoryBadgeView } from './views/badge/InventoryBadgeView';
import { InventoryBotView } from './views/bot/InventoryBotView'; import { InventoryBotView } from './views/bot/InventoryBotView';
import { InventoryFurnitureView } from './views/furniture/InventoryFurnitureView'; import { InventoryFurnitureView } from './views/furniture/InventoryFurnitureView';
import { InventoryPetView } from './views/pet/InventoryPetView'; import { InventoryPetView } from './views/pet/InventoryPetView';
import { InventoryTradeView } from './views/trade/InventoryTradeView';
const tabs = [ InventoryTabs.FURNITURE, InventoryTabs.BOTS, InventoryTabs.PETS, InventoryTabs.BADGES ];
export const InventoryView: FC<InventoryViewProps> = props => export const InventoryView: FC<InventoryViewProps> = props =>
{ {
const tabs = [ InventoryTabs.FURNITURE, InventoryTabs.BOTS, InventoryTabs.PETS, InventoryTabs.BADGES ];
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const [ currentTab, setCurrentTab ] = useState<string>(tabs[0]); const [ currentTab, setCurrentTab ] = useState<string>(tabs[0]);
const [ roomSession, setRoomSession ] = useState<IRoomSession>(null); const [ roomSession, setRoomSession ] = useState<IRoomSession>(null);
@ -46,12 +47,18 @@ export const InventoryView: FC<InventoryViewProps> = props =>
case InventoryEvent.TOGGLE_INVENTORY: case InventoryEvent.TOGGLE_INVENTORY:
setIsVisible(value => !value); setIsVisible(value => !value);
return; return;
case InventoryTradeRequestEvent.REQUEST_TRADE: {
const tradeEvent = (event as InventoryTradeRequestEvent);
GetConnection().send(new TradingOpenComposer(tradeEvent.objectId));
}
} }
}, []); }, []);
useUiEvent(InventoryEvent.SHOW_INVENTORY, onInventoryEvent); useUiEvent(InventoryEvent.SHOW_INVENTORY, onInventoryEvent);
useUiEvent(InventoryEvent.HIDE_INVENTORY, onInventoryEvent); useUiEvent(InventoryEvent.HIDE_INVENTORY, onInventoryEvent);
useUiEvent(InventoryEvent.TOGGLE_INVENTORY, onInventoryEvent); useUiEvent(InventoryEvent.TOGGLE_INVENTORY, onInventoryEvent);
useUiEvent(InventoryTradeRequestEvent.REQUEST_TRADE, onInventoryEvent);
const onRoomEngineObjectPlacedEvent = useCallback((event: RoomEngineObjectPlacedEvent) => const onRoomEngineObjectPlacedEvent = useCallback((event: RoomEngineObjectPlacedEvent) =>
{ {
@ -96,6 +103,19 @@ export const InventoryView: FC<InventoryViewProps> = props =>
} }
}, []); }, []);
useEffect(() =>
{
if(!isVisible)
{
if(furnitureState.tradeData)
{
setIsVisible(true);
if(currentTab !== InventoryTabs.FURNITURE) setCurrentTab(InventoryTabs.FURNITURE);
}
}
}, [ furnitureState.tradeData, isVisible, currentTab ]);
return ( return (
<InventoryContextProvider value={ { furnitureState, dispatchFurnitureState, botState, dispatchBotState, petState, dispatchPetState, badgeState, dispatchBadgeState } }> <InventoryContextProvider value={ { furnitureState, dispatchFurnitureState, botState, dispatchBotState, petState, dispatchPetState, badgeState, dispatchBadgeState } }>
<InventoryMessageHandler /> <InventoryMessageHandler />
@ -121,6 +141,7 @@ export const InventoryView: FC<InventoryViewProps> = props =>
<InventoryPetView roomSession={ roomSession } roomPreviewer={ roomPreviewer } /> } <InventoryPetView roomSession={ roomSession } roomPreviewer={ roomPreviewer } /> }
{ (currentTab === InventoryTabs.BADGES ) && { (currentTab === InventoryTabs.BADGES ) &&
<InventoryBadgeView /> } <InventoryBadgeView /> }
{ furnitureState.tradeData && <InventoryTradeView isInFurnitureView={ (currentTab === InventoryTabs.FURNITURE ) } /> }
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> } </NitroCardView> }
</InventoryContextProvider> </InventoryContextProvider>

View File

@ -28,6 +28,8 @@ export class FurnitureItem implements IFurnitureItem
constructor(parser: IFurnitureItemData) constructor(parser: IFurnitureItemData)
{ {
if(!parser) return;
this._locked = false; this._locked = false;
this._id = parser.itemId; this._id = parser.itemId;
this._type = parser.spriteId; this._type = parser.spriteId;
@ -189,26 +191,26 @@ export class FurnitureItem implements IFurnitureItem
public update(parser: IFurnitureItemData): void public update(parser: IFurnitureItemData): void
{ {
this._type = parser.spriteId; this._type = parser.spriteId;
this._ref = parser.ref; this._ref = parser.ref;
this._category = parser.category; this._category = parser.category;
this._groupable = (parser.isGroupable && !parser.rentable); this._groupable = (parser.isGroupable && !parser.rentable);
this._tradeable = parser.tradable; this._tradeable = parser.tradable;
this._recyclable = parser.isRecycleable; this._recyclable = parser.isRecycleable;
this._sellable = parser.sellable; this._sellable = parser.sellable;
this._stuffData = parser.stuffData; this._stuffData = parser.stuffData;
this._extra = parser.extra; this._extra = parser.extra;
this._secondsToExpiration = parser.secondsToExpiration; this._secondsToExpiration = parser.secondsToExpiration;
this._expirationTimeStamp = parser.expirationTimeStamp; this._expirationTimeStamp = parser.expirationTimeStamp;
this._hasRentPeriodStarted = parser.hasRentPeriodStarted; this._hasRentPeriodStarted = parser.hasRentPeriodStarted;
this._creationDay = parser.creationDay; this._creationDay = parser.creationDay;
this._creationMonth = parser.creationMonth; this._creationMonth = parser.creationMonth;
this._creationYear = parser.creationYear; this._creationYear = parser.creationYear;
this._slotId = parser.slotId; this._slotId = parser.slotId;
this._songId = parser.songId; this._songId = parser.songId;
this._flatId = parser.flatId; this._flatId = parser.flatId;
this._isRented = parser.rentable; this._isRented = parser.rentable;
this._isWallItem = parser.isWallItem; this._isWallItem = parser.isWallItem;
} }
public clone(): FurnitureItem public clone(): FurnitureItem

View File

@ -310,7 +310,7 @@ function addGroupableFurnitureItem(set: GroupItem[], item: FurnitureItem, unseen
return existingGroup; return existingGroup;
} }
function createGroupItem(type: number, category: number, stuffData: IObjectData, extra: number = NaN, flag: boolean = false): GroupItem export function createGroupItem(type: number, category: number, stuffData: IObjectData, extra: number = NaN, flag: boolean = false): GroupItem
{ {
// const iconImage: HTMLImageElement = null; // const iconImage: HTMLImageElement = null;

View File

@ -20,21 +20,42 @@ export class GroupItem
private _hasUnseenItems: boolean; private _hasUnseenItems: boolean;
private _items: FurnitureItem[]; private _items: FurnitureItem[];
constructor(type: number, category: number, roomEngine: IRoomEngine, stuffData: IObjectData, extra: number) constructor(type: number = -1, category: number = -1, roomEngine: IRoomEngine = null, stuffData: IObjectData = null, extra: number = -1)
{ {
this._type = type; this._type = type;
this._category = category; this._category = category;
this._roomEngine = roomEngine; this._roomEngine = roomEngine;
this._stuffData = stuffData; this._stuffData = stuffData;
this._extra = extra; this._extra = extra;
this._isWallItem = false; this._isWallItem = false;
this._iconUrl = null; this._iconUrl = null;
this._name = null; this._name = null;
this._description = null; this._description = null;
this._locked = false; this._locked = false;
this._selected = false; this._selected = false;
this._hasUnseenItems = false; this._hasUnseenItems = false;
this._items = []; this._items = [];
}
public clone(): GroupItem
{
const groupItem = new GroupItem();
groupItem._type = this._type;
groupItem._category = this._category;
groupItem._roomEngine = this._roomEngine;
groupItem._stuffData = this._stuffData;
groupItem._extra = this._extra;
groupItem._isWallItem = this._isWallItem;
groupItem._iconUrl = this._iconUrl;
groupItem._name = this._name;
groupItem._description = this._description;
groupItem._locked = this._locked;
groupItem._selected = this._selected;
groupItem._hasUnseenItems = this._hasUnseenItems;
groupItem._items = this._items;
return groupItem;
} }
public prepareGroup(): void public prepareGroup(): void
@ -251,11 +272,12 @@ export class GroupItem
this._items = items; this._items = items;
} }
public lockItemIds(itemIds: number[]): void public lockItemIds(itemIds: number[]): boolean
{ {
const items = [ ...this._items ]; const items = [ ...this._items ];
let index = 0; let index = 0;
let updated = false;
while(index < items.length) while(index < items.length)
{ {
@ -264,6 +286,8 @@ export class GroupItem
if(item.locked !== locked) if(item.locked !== locked)
{ {
updated = true;
const newItem = item.clone(); const newItem = item.clone();
newItem.locked = locked; newItem.locked = locked;
@ -275,6 +299,8 @@ export class GroupItem
} }
this._items = items; this._items = items;
return updated;
} }
private setName(): void private setName(): void

View File

@ -0,0 +1,10 @@
export class TradeState
{
public static TRADING_STATE_READY: number = 0;
public static TRADING_STATE_RUNNING: number = 1;
public static TRADING_STATE_COUNTDOWN: number = 2;
public static TRADING_STATE_CONFIRMING: number = 3;
public static TRADING_STATE_CONFIRMED: number = 4;
public static TRADING_STATE_COMPLETED: number = 5;
public static TRADING_STATE_CANCELLED: number = 6;
}

View File

@ -0,0 +1,15 @@
import { AdvancedMap } from 'nitro-renderer';
import { GroupItem } from './GroupItem';
export class TradeUserData
{
constructor(
public userId: number = -1,
public userName: string = '',
public userItems: AdvancedMap<string, GroupItem> = null,
public itemCount: number = 0,
public creditsCount: number = 0,
public accepts: boolean = false,
public canTrade: boolean = false,
public items: AdvancedMap<string, GroupItem> = new AdvancedMap()) {}
}

View File

@ -0,0 +1,73 @@
import { AdvancedMap, IObjectData, ItemDataStructure, StringDataType } from 'nitro-renderer';
import { GetSessionDataManager } from '../../../api';
import { FurniCategory } from './FurniCategory';
import { FurnitureItem } from './FurnitureItem';
import { createGroupItem } from './FurnitureUtilities';
import { GroupItem } from './GroupItem';
function isExternalImage(spriteId: number): boolean
{
const furnitureData = GetSessionDataManager().getWallItemData(spriteId);
return (furnitureData && furnitureData.isExternalImage);
}
export function parseTradeItems(items: ItemDataStructure[], _arg_2: AdvancedMap<string, GroupItem>): void
{
const totalItems = items.length;
if(!totalItems) return;
for(const item of items)
{
const spriteId = item.spriteId;
const category = item.category;
let name = (item.furniType + spriteId);
if(!item.isGroupable || isExternalImage(spriteId))
{
name = ('itemid' + item.itemId);
}
if(item.category === FurniCategory._Str_5186)
{
name = (item.itemId + 'poster' + item.stuffData.getLegacyString());
}
else if(item.category === FurniCategory._Str_12454)
{
name = '';
}
let groupItem = ((item.isGroupable && !isExternalImage(item.spriteId)) ? _arg_2.getValue(name) : null);
if(!groupItem)
{
groupItem = createGroupItem(spriteId, category, item.stuffData);
_arg_2.add(name, groupItem);
}
groupItem.push(new FurnitureItem(item));
}
}
export function _Str_16998(spriteId: number, stuffData: IObjectData): string
{
let type = spriteId.toString();
const _local_4 = (stuffData as StringDataType);
if(!(stuffData instanceof StringDataType)) return type;
let _local_5 = 1;
while(_local_5 < 5)
{
type = (type + (',' + _local_4.getValue(_local_5)));
_local_5++;
}
return type;
}

View File

@ -1,14 +1,22 @@
import { FurnitureListItemParser } from 'nitro-renderer'; import { AdvancedMap, FurnitureListItemParser, TradingListItemParser } from 'nitro-renderer';
import { Reducer } from 'react'; import { Reducer } from 'react';
import { FurnitureItem } from '../common/FurnitureItem'; import { FurnitureItem } from '../common/FurnitureItem';
import { addFurnitureItem, processFurniFragment, removeFurniItemById } from '../common/FurnitureUtilities'; import { addFurnitureItem, processFurniFragment, removeFurniItemById } from '../common/FurnitureUtilities';
import { GroupItem } from '../common/GroupItem'; import { GroupItem } from '../common/GroupItem';
import { TradeState } from '../common/TradeState';
import { TradeUserData } from '../common/TradeUserData';
import { parseTradeItems } from '../common/TradingUtilities';
export interface IInventoryFurnitureState export interface IInventoryFurnitureState
{ {
needsFurniUpdate: boolean; needsFurniUpdate: boolean;
groupItem: GroupItem; groupItem: GroupItem;
groupItems: GroupItem[]; groupItems: GroupItem[];
tradeData: {
ownUser: TradeUserData;
otherUser: TradeUserData;
state: number;
}
} }
export interface IInventoryFurnitureAction export interface IInventoryFurnitureAction
@ -20,6 +28,11 @@ export interface IInventoryFurnitureAction
parsers?: FurnitureListItemParser[]; parsers?: FurnitureListItemParser[];
itemId?: number; itemId?: number;
fragment?: Map<number, FurnitureListItemParser>; fragment?: Map<number, FurnitureListItemParser>;
ownTradeUser?: TradeUserData;
otherTradeUser?: TradeUserData;
tradeState?: number;
userId?: number;
tradeParser?: TradingListItemParser;
} }
} }
@ -30,12 +43,18 @@ export class InventoryFurnitureActions
public static PROCESS_FRAGMENT: string = 'IFA_PROCESS_FRAGMENT'; public static PROCESS_FRAGMENT: string = 'IFA_PROCESS_FRAGMENT';
public static ADD_OR_UPDATE_FURNITURE: string = 'IFA_ADD_OR_UPDATE_FURNITURE'; public static ADD_OR_UPDATE_FURNITURE: string = 'IFA_ADD_OR_UPDATE_FURNITURE';
public static REMOVE_FURNITURE: string = 'IFA_REMOVE_FURNITURE'; public static REMOVE_FURNITURE: string = 'IFA_REMOVE_FURNITURE';
public static SET_TRADE_DATA: string = 'IFA_SET_TRADE_DATA';
public static SET_TRADE_STATE: string = 'IFA_SET_TRADE_STATE';
public static SET_TRADE_ACCEPTANCE: string = 'FA_SET_TRADE_ACCEPTANCE';
public static CLOSE_TRADE: string = 'IFA_CLOSE_STRING';
public static UPDATE_TRADE: string = 'IFA_UPDATE_TRADE';
} }
export const initialInventoryFurniture: IInventoryFurnitureState = { export const initialInventoryFurniture: IInventoryFurnitureState = {
needsFurniUpdate: true, needsFurniUpdate: true,
groupItem: null, groupItem: null,
groupItems: [] groupItems: [],
tradeData: null
} }
export const InventoryFurnitureReducer: Reducer<IInventoryFurnitureState, IInventoryFurnitureAction> = (state, action) => export const InventoryFurnitureReducer: Reducer<IInventoryFurnitureState, IInventoryFurnitureAction> = (state, action) =>
@ -131,6 +150,122 @@ export const InventoryFurnitureReducer: Reducer<IInventoryFurnitureState, IInven
return { ...state, groupItems }; return { ...state, groupItems };
} }
case InventoryFurnitureActions.SET_TRADE_DATA: {
const tradeData = { ...state.tradeData };
tradeData.ownUser = (action.payload.ownTradeUser || null);
tradeData.otherUser = (action.payload.otherTradeUser || null);
tradeData.state = TradeState.TRADING_STATE_RUNNING;
return { ...state, tradeData };
}
case InventoryFurnitureActions.SET_TRADE_STATE: {
const tradeData = { ...state.tradeData };
tradeData.state = (action.payload.tradeState || TradeState.TRADING_STATE_RUNNING);
return { ...state, tradeData };
}
case InventoryFurnitureActions.SET_TRADE_ACCEPTANCE: {
const tradeData = { ...state.tradeData };
const userId = (action.payload.userId || -1);
if(tradeData.ownUser.userId === userId)
{
const ownUserData = Object.assign({}, tradeData.ownUser);
ownUserData.accepts = (action.payload.flag || false);
tradeData.ownUser = ownUserData;
}
else if(tradeData.otherUser.userId === userId)
{
const otherUserData = Object.assign({}, tradeData.otherUser);
otherUserData.accepts = (action.payload.flag || false);
tradeData.otherUser = otherUserData;
}
return { ...state, tradeData };
}
case InventoryFurnitureActions.CLOSE_TRADE: {
const tradeData = null;
return { ...state, tradeData };
}
case InventoryFurnitureActions.UPDATE_TRADE: {
const tradeData = { ...state.tradeData };
const groupItems = [ ...state.groupItems ];
const parser = (action.payload.tradeParser || null);
if(parser)
{
const firstUserItems: AdvancedMap<string, GroupItem> = new AdvancedMap();
const secondUserItems: AdvancedMap<string, GroupItem> = new AdvancedMap();
parseTradeItems(parser.firstUserItemArray, firstUserItems);
parseTradeItems(parser.secondUserItemArray, secondUserItems);
const ownUserData = Object.assign({}, tradeData.ownUser);
const otherUserData = Object.assign({}, tradeData.otherUser);
if(tradeData.ownUser.userId === parser.firstUserID)
{
ownUserData.creditsCount = parser.firstUserNumCredits;
ownUserData.itemCount = parser.firstUserNumItems;
ownUserData.items = firstUserItems;
}
else if(tradeData.ownUser.userId === parser.secondUserID)
{
ownUserData.creditsCount = parser.secondUserNumCredits;
ownUserData.itemCount = parser.secondUserNumItems;
ownUserData.items = secondUserItems;
}
if(tradeData.otherUser.userId === parser.firstUserID)
{
otherUserData.creditsCount = parser.firstUserNumCredits;
otherUserData.itemCount = parser.firstUserNumItems;
otherUserData.items = firstUserItems;
}
else if(tradeData.otherUser.userId === parser.secondUserID)
{
otherUserData.creditsCount = parser.secondUserNumCredits;
otherUserData.itemCount = parser.secondUserNumItems;
otherUserData.items = secondUserItems;
}
tradeData.ownUser = ownUserData;
tradeData.otherUser = otherUserData;
const tradeIds: number[] = [];
for(const groupItem of ownUserData.items.getValues())
{
let i = 0;
while(i < groupItem.getTotalCount())
{
const item = groupItem.getItemByIndex(i);
if(item) tradeIds.push(item.ref);
i++;
}
}
for(const groupItem of groupItems) groupItem.lockItemIds(tradeIds);
}
return { ...state, groupItems, tradeData };
}
default: default:
return state; return state;
} }

View File

@ -1,6 +1,6 @@
import { FurnitureListComposer, RoomObjectVariable, Vector3d } from 'nitro-renderer'; import { FurnitureListComposer, IObjectData, RoomObjectVariable, TradingListAddItemComposer, TradingListAddItemsComposer, Vector3d } from 'nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { GetRoomEngine } from '../../../../api'; import { GetConnection, GetRoomEngine } from '../../../../api';
import { SendMessageHook } from '../../../../hooks/messages/message-event'; import { SendMessageHook } from '../../../../hooks/messages/message-event';
import { LocalizeText } from '../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../utils/LocalizeText';
import { LimitedEditionCompactPlateView } from '../../../shared/limited-edition/compact-plate/LimitedEditionCompactPlateView'; import { LimitedEditionCompactPlateView } from '../../../shared/limited-edition/compact-plate/LimitedEditionCompactPlateView';
@ -8,19 +8,122 @@ import { RoomPreviewerView } from '../../../shared/room-previewer/RoomPreviewerV
import { FurniCategory } from '../../common/FurniCategory'; import { FurniCategory } from '../../common/FurniCategory';
import { attemptItemPlacement } from '../../common/FurnitureUtilities'; import { attemptItemPlacement } from '../../common/FurnitureUtilities';
import { GroupItem } from '../../common/GroupItem'; import { GroupItem } from '../../common/GroupItem';
import { IFurnitureItem } from '../../common/IFurnitureItem';
import { TradeState } from '../../common/TradeState';
import { _Str_16998 } from '../../common/TradingUtilities';
import { useInventoryContext } from '../../context/InventoryContext'; import { useInventoryContext } from '../../context/InventoryContext';
import { InventoryFurnitureActions } from '../../reducers/InventoryFurnitureReducer'; import { InventoryFurnitureActions } from '../../reducers/InventoryFurnitureReducer';
import { InventoryFurnitureViewProps } from './InventoryFurnitureView.types'; import { InventoryFurnitureViewProps } from './InventoryFurnitureView.types';
import { InventoryFurnitureResultsView } from './results/InventoryFurnitureResultsView'; import { InventoryFurnitureResultsView } from './results/InventoryFurnitureResultsView';
import { InventoryFurnitureSearchView } from './search/InventoryFurnitureSearchView'; import { InventoryFurnitureSearchView } from './search/InventoryFurnitureSearchView';
const MAX_ITEMS_TO_TRADE: number = 3;
export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props => export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
{ {
const { roomSession = null, roomPreviewer = null } = props; const { roomSession = null, roomPreviewer = null } = props;
const { furnitureState = null, dispatchFurnitureState = null } = useInventoryContext(); const { furnitureState = null, dispatchFurnitureState = null } = useInventoryContext();
const { needsFurniUpdate = false, groupItem = null, groupItems = [] } = furnitureState; const { needsFurniUpdate = false, groupItem = null, groupItems = [], tradeData = null } = furnitureState;
const [ filteredGroupItems, setFilteredGroupItems ] = useState<GroupItem[]>(groupItems); const [ filteredGroupItems, setFilteredGroupItems ] = useState<GroupItem[]>(groupItems);
const isTrading = useMemo(() =>
{
if(!tradeData) return false;
return (tradeData.state >= TradeState.TRADING_STATE_RUNNING);
}, [ tradeData ]);
const canTradeItem = useCallback((isWallItem: boolean, spriteId: number, category: number, groupable: boolean, stuffData: IObjectData) =>
{
if(!tradeData || !tradeData.ownUser || tradeData.ownUser.accepts || !tradeData.ownUser.items) return false;
if(tradeData.ownUser.items.length < MAX_ITEMS_TO_TRADE) return true;
if(!groupable) return false;
let type = spriteId.toString();
if(category === FurniCategory._Str_5186)
{
type = ((type + 'poster') + stuffData.getLegacyString());
}
else
{
if(category === FurniCategory._Str_12454)
{
type = _Str_16998(spriteId, stuffData);
}
else
{
type = (((isWallItem) ? 'I' : 'S') + type);
}
}
return !!tradeData.ownUser.items.getValue(type);
}, [ tradeData ]);
const attemptItemOffer = useCallback((count: number) =>
{
if(!tradeData || !groupItem || !isTrading) return;
const tradeItems = groupItem.getTradeItems(count);
if(!tradeItems || !tradeItems.length) return;
let coreItem: IFurnitureItem = null;
const itemIds: number[] = [];
for(const item of tradeItems)
{
itemIds.push(item.id);
if(!coreItem) coreItem = item;
}
const tradedIds: number[] = [];
if(isTrading)
{
const ownItemCount = tradeData.ownUser.items.length;
if((ownItemCount + itemIds.length) <= 1500)
{
if(!coreItem.isGroupable && (itemIds.length))
{
GetConnection().send(new TradingListAddItemComposer(itemIds.pop()));
}
else
{
const tradeIds: number[] = [];
for(const itemId of itemIds)
{
if(canTradeItem(coreItem.isWallItem, coreItem.type, coreItem.category, coreItem.isGroupable, coreItem.stuffData))
{
tradedIds.push(itemId);
}
}
if(tradedIds.length)
{
if(tradedIds.length === 1)
{
GetConnection().send(new TradingListAddItemComposer(tradedIds.pop()));
}
else
{
GetConnection().send(new TradingListAddItemsComposer(...tradedIds));
}
}
}
}
else
{
//this._notificationService.alert('${trading.items.too_many_items.desc}', '${trading.items.too_many_items.title}');
}
}
}, [ canTradeItem, groupItem, isTrading, tradeData ]);
useEffect(() => useEffect(() =>
{ {
if(needsFurniUpdate) if(needsFurniUpdate)
@ -123,14 +226,18 @@ export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
</div> </div>
<div className="position-relative d-flex flex-column col-5 justify-space-between"> <div className="position-relative d-flex flex-column col-5 justify-space-between">
<RoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } /> <RoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
{ groupItem && groupItem.stuffData.isUnique && { groupItem &&groupItem.stuffData.isUnique &&
<div className="position-absolute limited-edition-info-container"> <div className="position-absolute limited-edition-info-container">
<LimitedEditionCompactPlateView uniqueNumber={ groupItem.stuffData.uniqueNumber } uniqueSeries={ groupItem.stuffData.uniqueSeries } /> <LimitedEditionCompactPlateView uniqueNumber={ groupItem.stuffData.uniqueNumber } uniqueSeries={ groupItem.stuffData.uniqueSeries } />
</div> } </div> }
{ groupItem && <div className="d-flex flex-column flex-grow-1"> { groupItem &&
<p className="flex-grow-1 fs-6 text-black my-2">{ groupItem.name }</p> <div className="d-flex flex-column flex-grow-1">
{ !!roomSession && <button type="button" className="btn btn-success btn-sm" onClick={ event => attemptItemPlacement(groupItem) }>{ LocalizeText('inventory.furni.placetoroom') }</button> } <p className="flex-grow-1 fs-6 text-black my-2">{ groupItem.name }</p>
</div> } { !!roomSession && !isTrading &&
<button type="button" className="btn btn-success btn-sm" onClick={ event => attemptItemPlacement(groupItem) }>{ LocalizeText('inventory.furni.placetoroom') }</button> }
{ isTrading &&
<button type="button" className="btn btn-primary btn-sm" onClick={ event => attemptItemOffer(1) }>{ LocalizeText('inventory.trading.offer') }</button> }
</div> }
</div> </div>
</div> </div>
); );

View File

@ -10,6 +10,7 @@ export const InventoryFurnitureItemView: FC<InventoryFurnitureItemViewProps> = p
{ {
const { groupItem } = props; const { groupItem } = props;
const { furnitureState, dispatchFurnitureState } = useInventoryContext(); const { furnitureState, dispatchFurnitureState } = useInventoryContext();
const { tradeData = null } = furnitureState;
const [ isMouseDown, setMouseDown ] = useState(false); const [ isMouseDown, setMouseDown ] = useState(false);
const isActive = (furnitureState.groupItem === groupItem); const isActive = (furnitureState.groupItem === groupItem);
@ -40,7 +41,7 @@ export const InventoryFurnitureItemView: FC<InventoryFurnitureItemViewProps> = p
return ( return (
<div className="col pe-1 pb-1 inventory-furniture-item-container"> <div className="col pe-1 pb-1 inventory-furniture-item-container">
<div className={ 'position-relative border border-2 rounded inventory-furniture-item cursor-pointer ' + (isActive ? 'active ' : '') + (groupItem.stuffData.isUnique ? 'unique-item ' : '') } style={ { backgroundImage: imageUrl }} onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent }> <div className={ 'position-relative border border-2 rounded inventory-furniture-item cursor-pointer ' + (isActive ? 'active ' : '') + (groupItem.stuffData.isUnique ? 'unique-item ' : '') + (!groupItem.getUnlockedCount() ? 'opacity-0-5 ' : '') } style={ { backgroundImage: imageUrl }} onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent }>
{groupItem.getUnlockedCount() > 1 && {groupItem.getUnlockedCount() > 1 &&
<span className="position-absolute badge border bg-danger px-1 rounded-circle">{groupItem.getUnlockedCount()}</span> } <span className="position-absolute badge border bg-danger px-1 rounded-circle">{groupItem.getUnlockedCount()}</span> }
{ groupItem.stuffData.isUnique && { groupItem.stuffData.isUnique &&

View File

@ -9,7 +9,7 @@ export const InventoryFurnitureResultsView: FC<InventoryFurnitureResultsViewProp
return ( return (
<div className="h-100 overflow-hidden"> <div className="h-100 overflow-hidden">
<div className="row row-cols-5 align-content-start g-0 w-100 h-100 overflow-auto"> <div className="row row-cols-5 align-content-start g-0 w-100 h-100 overflow-auto">
{ groupItems && (groupItems.length > 0) && groupItems.map((item, index) => { groupItems && (groupItems.length > 0) && groupItems.map((item, index) =>
{ {
return <InventoryFurnitureItemView key={ index } groupItem={ item } /> return <InventoryFurnitureItemView key={ index } groupItem={ item } />
}) } }) }

View File

@ -0,0 +1,37 @@
import { FC } from 'react';
import { LocalizeText } from '../../../../utils/LocalizeText';
import { useInventoryContext } from '../../context/InventoryContext';
import { InventoryTradeViewProps } from './InventoryTradeView.types';
import { InventoryTradeItemView } from './item/InventoryTradeItemView';
const MAX_ITEMS_COUNT: number = 9;
export const InventoryTradeView: FC<InventoryTradeViewProps> = props =>
{
const { isInFurnitureView = false } = props;
const { furnitureState = null } = useInventoryContext();
const { tradeData = null } = furnitureState;
return (
<div className="row h-100 mt-2">
<div className="d-flex flex-column col-6">
<div className="badge bg-primary w-100 p-1 mb-1 me-1">{ LocalizeText('inventory.trading.you') }</div>
<div className="row row-cols-3 align-content-start g-0 w-100 h-100">
{ Array.from(Array(MAX_ITEMS_COUNT), (e, i) =>
{
return <InventoryTradeItemView key={ i } groupItem={ (tradeData.ownUser.items.getWithIndex(i) || null) } />;
}) }
</div>
</div>
<div className="d-flex flex-column col-6">
<div className="badge bg-primary w-100 p-1 mb-1 me-1">{ tradeData.otherUser.userName }</div>
<div className="row row-cols-3 align-content-start g-0 w-100 h-100">
{ Array.from(Array(MAX_ITEMS_COUNT), (e, i) =>
{
return <InventoryTradeItemView key={ i } groupItem={ (tradeData.otherUser.items.getWithIndex(i) || null) } />;
}) }
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,4 @@
export interface InventoryTradeViewProps
{
isInFurnitureView: boolean;
}

View File

@ -0,0 +1,37 @@
import { FC, useCallback } from 'react';
import { LimitedEditionStyledNumberView } from '../../../../shared/limited-edition/styled-number/LimitedEditionStyledNumberView';
import { InventoryTradeItemViewProps } from './InventoryTradeItemView.types';
export const InventoryTradeItemView: FC<InventoryTradeItemViewProps> = props =>
{
const { groupItem = null } = props;
const onClick = useCallback(() =>
{
}, []);
if(!groupItem)
{
return (
<div className="col pe-1 pb-1 inventory-furniture-item-container">
<div className="position-relative border border-2 rounded inventory-furniture-item cursor-pointer" />
</div>
);
}
const imageUrl = `url(${ groupItem.iconUrl })`;
return (
<div className="col pe-1 pb-1 inventory-furniture-item-container">
<div className="position-relative border border-2 rounded inventory-furniture-item cursor-pointer" style={ { backgroundImage: imageUrl }} onClick={ onClick }>
{ groupItem.getUnlockedCount() > 1 &&
<span className="position-absolute badge border bg-danger px-1 rounded-circle">{ groupItem.getUnlockedCount() }</span> }
{ groupItem.stuffData.isUnique &&
<div className="position-absolute unique-item-counter">
<LimitedEditionStyledNumberView value={ groupItem.stuffData.uniqueNumber } />
</div> }
</div>
</div>
);
}

View File

@ -0,0 +1,6 @@
import { GroupItem } from '../../../common/GroupItem';
export interface InventoryTradeItemViewProps
{
groupItem: GroupItem;
}

View File

@ -37,11 +37,11 @@ export const PurseView: FC<PurseViewProps> = props =>
<PurseContextProvider value={ { purseState, dispatchPurseState }}> <PurseContextProvider value={ { purseState, dispatchPurseState }}>
<PurseMessageHandler /> <PurseMessageHandler />
<div className="nitro-purse rounded d-flex flex-row py-1 justify-content-between"> <div className="nitro-purse rounded d-flex flex-row py-1 justify-content-between">
{ currencies && currencies.map((currency, index) => { currencies && currencies.map(currency =>
{ {
if(displayedCurrencies.indexOf(currency.type) === -1) return null; if(displayedCurrencies.indexOf(currency.type) === -1) return null;
return <CurrencyView key={ index } currency={ currency } />; return <CurrencyView key={ currency.type } currency={ currency } />;
}) } }) }
<div className="notification-button px-2" onClick={ toggleNotificationCenter }> <div className="notification-button px-2" onClick={ toggleNotificationCenter }>
<i className="fas fa-bars" /> <i className="fas fa-bars" />

View File

@ -1,6 +1,6 @@
import { IFurnitureData, Nitro, NitroEvent, ObjectDataFactory, PetFigureData, PetType, RoomAdsUpdateComposer, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnumItemExtradataParameter, SecurityLevel, Vector3d } from 'nitro-renderer'; import { IFurnitureData, Nitro, NitroEvent, ObjectDataFactory, PetFigureData, PetType, RoomAdsUpdateComposer, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnumItemExtradataParameter, SecurityLevel, Vector3d } from 'nitro-renderer';
import { GetConnection, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../api'; import { GetConnection, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../api';
import { WiredSelectObjectEvent } from '../../../events'; import { InventoryTradeRequestEvent, WiredSelectObjectEvent } from '../../../events';
import { dispatchUiEvent } from '../../../hooks/events'; import { dispatchUiEvent } from '../../../hooks/events';
import { LocalizeText } from '../../../utils/LocalizeText'; import { LocalizeText } from '../../../utils/LocalizeText';
import { RoomWidgetObjectNameEvent, RoomWidgetUpdateChatInputContentEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent } from '../events'; import { RoomWidgetObjectNameEvent, RoomWidgetUpdateChatInputContentEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateInfostandFurniEvent, RoomWidgetUpdateInfostandPetEvent, RoomWidgetUpdateInfostandRentableBotEvent, RoomWidgetUpdateInfostandUserEvent } from '../events';
@ -114,7 +114,7 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
this.container.roomSession.sendTakeRightsMessage((message as RoomWidgetUserActionMessage).userId); this.container.roomSession.sendTakeRightsMessage((message as RoomWidgetUserActionMessage).userId);
break; break;
case RoomWidgetUserActionMessage.START_TRADING: case RoomWidgetUserActionMessage.START_TRADING:
//if(userData) this._widget.inventoryTrading.startTrade(userData.roomIndex, userData.name); dispatchUiEvent(new InventoryTradeRequestEvent(userData.roomIndex, userData.name));
break; break;
// case RoomWidgetUserActionMessage.RWUAM_OPEN_HOME_PAGE: // case RoomWidgetUserActionMessage.RWUAM_OPEN_HOME_PAGE:
// this._container.sessionDataManager._Str_21275((message as RoomWidgetUserActionMessage).userId, _local_3.name); // this._container.sessionDataManager._Str_21275((message as RoomWidgetUserActionMessage).userId, _local_3.name);