Add bot inventory

This commit is contained in:
Bill 2021-04-29 04:02:40 -04:00
parent 7476452476
commit f73ddf1add
11 changed files with 342 additions and 0 deletions

View File

@ -0,0 +1,86 @@
import { BotData } from 'nitro-renderer';
import { Reducer } from 'react';
import { BotItem } from '../utils/BotItem';
import { addSingleBotItem, processBotFragment, removeBotItemById } from '../utils/BotUtilities';
export interface IInventoryBotState
{
needsBotUpdate: boolean;
botItem: BotItem;
botItems: BotItem[];
}
export interface IInventoryBotAction
{
type: string;
payload: {
flag?: boolean;
botItem?: BotItem;
botId?: number;
botData?: BotData;
fragment?: BotData[];
}
}
export class InventoryBotActions
{
public static SET_NEEDS_UPDATE: string = 'IBA_SET_NEEDS_UPDATE';
public static SET_BOT_ITEM: string = 'IBA_SET_BOT_ITEM';
public static PROCESS_FRAGMENT: string = 'IBA_PROCESS_FRAGMENT';
public static ADD_BOT: string = 'IBA_ADD_BOT';
public static REMOVE_BOT: string = 'IBA_REMOVE_BOT';
}
export const initialInventoryBot: IInventoryBotState = {
needsBotUpdate: true,
botItem: null,
botItems: []
}
export const inventoryBotReducer: Reducer<IInventoryBotState, IInventoryBotAction> = (state, action) =>
{
switch(action.type)
{
case InventoryBotActions.SET_NEEDS_UPDATE:
return { ...state, needsBotUpdate: (action.payload.flag || false) };
case InventoryBotActions.SET_BOT_ITEM: {
let botItem = (action.payload.botItem || state.botItem || null);
let index = 0;
if(botItem)
{
const foundIndex = state.botItems.indexOf(botItem);
if(foundIndex > -1) index = foundIndex;
}
botItem = (state.botItems[index] || null);
return { ...state, botItem };
}
case InventoryBotActions.PROCESS_FRAGMENT: {
const botItems = [ ...state.botItems ];
processBotFragment(botItems, action.payload.fragment);
return { ...state, botItems };
}
case InventoryBotActions.ADD_BOT: {
const botItems = [ ...state.botItems ];
addSingleBotItem(action.payload.botData, botItems, true);
return { ...state, botItems };
}
case InventoryBotActions.REMOVE_BOT: {
const botItems = [ ...state.botItems ];
removeBotItemById(action.payload.botId, botItems);
return { ...state, botItems };
}
default:
return state;
}
}

View File

@ -0,0 +1,45 @@
import { BotData } from 'nitro-renderer';
export class BotItem
{
private _botData: BotData;
private _selected: boolean;
private _isUnseen: boolean;
constructor(botData: BotData)
{
this._botData = botData;
this._selected = false;
this._isUnseen = false;
}
public get id(): number
{
return this._botData.id;
}
public get botData(): BotData
{
return this._botData;
}
public get selected(): boolean
{
return this._selected;
}
public set selected(flag: boolean)
{
this._selected = flag;
}
public get isUnseen(): boolean
{
return this._isUnseen;
}
public set isUnseen(flag: boolean)
{
this._isUnseen = flag;
}
}

View File

@ -0,0 +1,2 @@
@import './item/InventoryBotItemView';
@import './results/InventoryBotResultsView';

View File

@ -0,0 +1,97 @@
import { GetBotInventoryComposer, RoomObjectVariable } from 'nitro-renderer';
import { FC, useEffect } from 'react';
import { GetRoomEngine } from '../../../../api';
import { SendMessageHook } from '../../../../hooks/messages/message-event';
import { LocalizeText } from '../../../../utils/LocalizeText';
import { RoomPreviewerView } from '../../../room-previewer/RoomPreviewerView';
import { useInventoryContext } from '../../context/InventoryContext';
import { InventoryBotActions } from '../../reducers/InventoryBotReducer';
import { attemptBotPlacement } from '../../utils/BotUtilities';
import { InventoryBotViewProps } from './InventoryBotView.types';
import { InventoryBotResultsView } from './results/InventoryBotResultsView';
export const InventoryBotView: FC<InventoryBotViewProps> = props =>
{
const { roomSession = null, roomPreviewer = null } = props;
const { botState = null, dispatchBotState = null } = useInventoryContext();
const { needsBotUpdate = false, botItem = null, botItems = [] } = botState;
useEffect(() =>
{
if(needsBotUpdate)
{
dispatchBotState({
type: InventoryBotActions.SET_NEEDS_UPDATE,
payload: {
flag: false
}
});
SendMessageHook(new GetBotInventoryComposer());
}
else
{
dispatchBotState({
type: InventoryBotActions.SET_BOT_ITEM,
payload: {
botItem: null
}
});
}
}, [ needsBotUpdate, botItems, dispatchBotState ]);
useEffect(() =>
{
if(!botItem || !roomPreviewer) return;
const botData = botItem.botData;
const roomEngine = GetRoomEngine();
let wallType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_WALL_TYPE);
let floorType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE);
let landscapeType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE);
wallType = (wallType && wallType.length) ? wallType : '101';
floorType = (floorType && floorType.length) ? floorType : '101';
landscapeType = (landscapeType && landscapeType.length) ? landscapeType : '1.1';
roomPreviewer.reset(false);
roomPreviewer.updateRoomWallsAndFloorVisibility(true, true);
roomPreviewer.updateObjectRoom(floorType, wallType, landscapeType);
roomPreviewer.addAvatarIntoRoom(botData.figure, 0);
}, [ roomPreviewer, botItem ]);
if(!botItems || !botItems.length)
{
return (
<div className="row h-100">
<div className="col-5 d-flex justify-content-center align-items-center">
<div className="empty-image"></div>
</div>
<div className="d-flex flex-column col justify-content-center">
<div className="h5 m-0 text-black fw-bold m-0 mb-2">
{ LocalizeText('inventory.empty.bots.title') }
</div>
<div className="h6 text-black">{ LocalizeText('inventory.empty.bots.desc') }</div>
</div>
</div>
);
}
return (
<div className="row h-100">
<div className="col-7">
<InventoryBotResultsView botItems={ botItems } />
</div>
<div className="d-flex flex-column col-5 justify-space-between">
<RoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
{ botItem && <div className="d-flex flex-column flex-grow-1">
<p className="flex-grow-1 fs-6 text-black my-2">{ botItem.botData.name }</p>
{ !!roomSession && <button type="button" className="btn btn-success" onClick={ event => attemptBotPlacement(botItem) }>{ LocalizeText('inventory.furni.placetoroom') }</button> }
</div> }
</div>
</div>
);
}

View File

@ -0,0 +1,7 @@
import { IRoomSession, RoomPreviewer } from 'nitro-renderer';
export interface InventoryBotViewProps
{
roomSession: IRoomSession;
roomPreviewer: RoomPreviewer;
}

View File

@ -0,0 +1,25 @@
.inventory-bot-item-container {
height: 48px;
max-height: 48px;
.inventory-bot-item {
width: 100%;
height: 100%;
border-color: $muted !important;
background-color: #CDD3D9;
overflow: hidden;
&.active {
border-color: $white !important;
background-color: #ECECEC;
}
.avatar-image {
width: 100% !important;
height: 100% !important;
background-position: center;
background-repeat: no-repeat;
background-position-y: -32px !important;
}
}
}

View File

@ -0,0 +1,46 @@
import { MouseEventType } from 'nitro-renderer';
import { FC, MouseEvent, useCallback, useState } from 'react';
import { AvatarImageView } from '../../../../avatar-image/AvatarImageView';
import { useInventoryContext } from '../../../context/InventoryContext';
import { InventoryBotActions } from '../../../reducers/InventoryBotReducer';
import { attemptBotPlacement } from '../../../utils/BotUtilities';
import { InventoryBotItemViewProps } from './InventoryBotItemView.types';
export const InventoryBotItemView: FC<InventoryBotItemViewProps> = props =>
{
const { botItem } = props;
const { botState = null, dispatchBotState = null } = useInventoryContext();
const [ isMouseDown, setMouseDown ] = useState(false);
const isActive = (botState.botItem === botItem);
const onMouseEvent = useCallback((event: MouseEvent) =>
{
switch(event.type)
{
case MouseEventType.MOUSE_DOWN:
dispatchBotState({
type: InventoryBotActions.SET_BOT_ITEM,
payload: { botItem }
});
setMouseDown(true);
return;
case MouseEventType.MOUSE_UP:
setMouseDown(false);
return;
case MouseEventType.ROLL_OUT:
if(!isMouseDown || !isActive) return;
attemptBotPlacement(botItem);
return;
}
}, [ isActive, isMouseDown, botItem, dispatchBotState ]);
return (
<div className="col pe-1 pb-1 inventory-bot-item-container">
<div className={ 'position-relative border border-2 rounded inventory-bot-item cursor-pointer ' + (isActive ? 'active' : '') } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent }>
<AvatarImageView figure={ botItem.botData.figure } direction={ 3 } headOnly={ true } />
</div>
</div>
);
}

View File

@ -0,0 +1,6 @@
import { BotItem } from '../../../utils/BotItem';
export interface InventoryBotItemViewProps
{
botItem: BotItem;
}

View File

@ -0,0 +1,5 @@
.bot-item-container {
height: 226px;
max-height: 226px;
overflow-y: auto;
}

View File

@ -0,0 +1,17 @@
import { FC } from 'react';
import { InventoryBotItemView } from '../item/InventoryBotItemView';
import { InventoryBotResultsViewProps } from './InventoryBotResultsView.types';
export const InventoryBotResultsView: FC<InventoryBotResultsViewProps> = props =>
{
const { botItems = [] } = props;
return (
<div className="row row-cols-5 align-content-start g-0 bot-item-container">
{ (botItems && botItems.length && botItems.map((item, index) =>
{
return <InventoryBotItemView key={ index } botItem={ item } />
})) || null }
</div>
);
}

View File

@ -0,0 +1,6 @@
import { BotItem } from '../../../utils/BotItem';
export interface InventoryBotResultsViewProps
{
botItems: BotItem[];
}