mirror of
https://github.com/billsonnn/nitro-react.git
synced 2025-02-17 01:12:37 +01:00
Add pet inventory
This commit is contained in:
parent
428f4bb2c8
commit
7476452476
86
src/views/inventory/reducers/InventoryPetReducer.tsx
Normal file
86
src/views/inventory/reducers/InventoryPetReducer.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import { PetData } from 'nitro-renderer';
|
||||
import { Reducer } from 'react';
|
||||
import { PetItem } from '../utils/PetItem';
|
||||
import { addSinglePetItem, processPetFragment, removePetItemById } from '../utils/PetUtilities';
|
||||
|
||||
export interface IInventoryPetState
|
||||
{
|
||||
needsPetUpdate: boolean;
|
||||
petItem: PetItem;
|
||||
petItems: PetItem[];
|
||||
}
|
||||
|
||||
export interface IInventoryPetAction
|
||||
{
|
||||
type: string;
|
||||
payload: {
|
||||
flag?: boolean;
|
||||
petItem?: PetItem;
|
||||
petId?: number;
|
||||
petData?: PetData;
|
||||
fragment?: Map<number, PetData>;
|
||||
}
|
||||
}
|
||||
|
||||
export class InventoryPetActions
|
||||
{
|
||||
public static SET_NEEDS_UPDATE: string = 'IPA_SET_NEEDS_UPDATE';
|
||||
public static SET_PET_ITEM: string = 'IPA_SET_PET_ITEM';
|
||||
public static PROCESS_FRAGMENT: string = 'IPA_PROCESS_FRAGMENT';
|
||||
public static ADD_PET: string = 'IPA_ADD_PET';
|
||||
public static REMOVE_PET: string = 'IPA_REMOVE_PET';
|
||||
}
|
||||
|
||||
export const initialInventoryPet: IInventoryPetState = {
|
||||
needsPetUpdate: true,
|
||||
petItem: null,
|
||||
petItems: []
|
||||
}
|
||||
|
||||
export const inventoryPetReducer: Reducer<IInventoryPetState, IInventoryPetAction> = (state, action) =>
|
||||
{
|
||||
switch(action.type)
|
||||
{
|
||||
case InventoryPetActions.SET_NEEDS_UPDATE:
|
||||
return { ...state, needsPetUpdate: (action.payload.flag || false) };
|
||||
case InventoryPetActions.SET_PET_ITEM: {
|
||||
let petItem = (action.payload.petItem || state.petItem || null);
|
||||
|
||||
let index = 0;
|
||||
|
||||
if(petItem)
|
||||
{
|
||||
const foundIndex = state.petItems.indexOf(petItem);
|
||||
|
||||
if(foundIndex > -1) index = foundIndex;
|
||||
}
|
||||
|
||||
petItem = (state.petItems[index] || null);
|
||||
|
||||
return { ...state, petItem };
|
||||
}
|
||||
case InventoryPetActions.PROCESS_FRAGMENT: {
|
||||
const petItems = [ ...state.petItems ];
|
||||
|
||||
processPetFragment(petItems, (action.payload.fragment || null));
|
||||
|
||||
return { ...state, petItems };
|
||||
}
|
||||
case InventoryPetActions.ADD_PET: {
|
||||
const petItems = [ ...state.petItems ];
|
||||
|
||||
addSinglePetItem(action.payload.petData, petItems, true);
|
||||
|
||||
return { ...state, petItems };
|
||||
}
|
||||
case InventoryPetActions.REMOVE_PET: {
|
||||
const petItems = [ ...state.petItems ];
|
||||
|
||||
removePetItemById(action.payload.petId, petItems);
|
||||
|
||||
return { ...state, petItems };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
45
src/views/inventory/utils/PetItem.ts
Normal file
45
src/views/inventory/utils/PetItem.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { PetData } from 'nitro-renderer';
|
||||
|
||||
export class PetItem
|
||||
{
|
||||
private _petData: PetData;
|
||||
private _selected: boolean;
|
||||
private _isUnseen: boolean;
|
||||
|
||||
constructor(petData: PetData)
|
||||
{
|
||||
this._petData = petData;
|
||||
this._selected = false;
|
||||
this._isUnseen = false;
|
||||
}
|
||||
|
||||
public get id(): number
|
||||
{
|
||||
return this._petData.id;
|
||||
}
|
||||
|
||||
public get petData(): PetData
|
||||
{
|
||||
return this._petData;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
146
src/views/inventory/utils/PetUtilities.ts
Normal file
146
src/views/inventory/utils/PetUtilities.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import { PetData, RoomObjectCategory, RoomObjectPlacementSource, RoomObjectType } from 'nitro-renderer';
|
||||
import { GetRoomEngine, GetRoomSessionManager } from '../../../api';
|
||||
import { InventoryEvent } from '../../../events';
|
||||
import { dispatchUiEvent } from '../../../hooks/events/ui/ui-event';
|
||||
import { getPlacingItemId, setObjectMoverRequested, setPlacingItemId } from './InventoryUtilities';
|
||||
import { PetItem } from './PetItem';
|
||||
|
||||
export function cancelRoomObjectPlacement(): void
|
||||
{
|
||||
if(getPlacingItemId() === -1) return;
|
||||
|
||||
GetRoomEngine().cancelRoomObjectPlacement();
|
||||
|
||||
setPlacingItemId(-1);
|
||||
setObjectMoverRequested(false);
|
||||
}
|
||||
|
||||
export function attemptPetPlacement(petItem: PetItem, flag: boolean = false): boolean
|
||||
{
|
||||
const petData = petItem.petData;
|
||||
|
||||
if(!petData) return false;
|
||||
|
||||
const session = GetRoomSessionManager().getSession(1);
|
||||
|
||||
if(!session) return false;
|
||||
|
||||
if(!session.isRoomOwner)
|
||||
{
|
||||
if(!session.allowPets) return false;
|
||||
}
|
||||
|
||||
dispatchUiEvent(new InventoryEvent(InventoryEvent.HIDE_INVENTORY));
|
||||
|
||||
if(GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.INVENTORY, -(petData.id), RoomObjectCategory.UNIT, RoomObjectType.PET, petData.figureData.figuredata))
|
||||
{
|
||||
setPlacingItemId(petData.id);
|
||||
setObjectMoverRequested(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function mergePetFragments(fragment: Map<number, PetData>, totalFragments: number, fragmentNumber: number, fragments: Map<number, PetData>[])
|
||||
{
|
||||
if(totalFragments === 1) return fragment;
|
||||
|
||||
fragments[fragmentNumber] = fragment;
|
||||
|
||||
for(const frag of fragments)
|
||||
{
|
||||
if(!frag) return null;
|
||||
}
|
||||
|
||||
const merged: Map<number, PetData> = new Map();
|
||||
|
||||
for(const frag of fragments)
|
||||
{
|
||||
for(const [ key, value ] of frag) merged.set(key, value);
|
||||
|
||||
frag.clear();
|
||||
}
|
||||
|
||||
fragments = null;
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
function getAllItemIds(petItems: PetItem[]): number[]
|
||||
{
|
||||
const itemIds: number[] = [];
|
||||
|
||||
for(const petItem of petItems) itemIds.push(petItem.id);
|
||||
|
||||
return itemIds;
|
||||
}
|
||||
|
||||
export function processPetFragment(set: PetItem[], fragment: Map<number, PetData>): PetItem[]
|
||||
{
|
||||
const existingIds = getAllItemIds(set);
|
||||
const addedIds: number[] = [];
|
||||
const removedIds: number[] = [];
|
||||
|
||||
for(const key of fragment.keys()) (existingIds.indexOf(key) === -1) && addedIds.push(key);
|
||||
|
||||
for(const itemId of existingIds) (!fragment.get(itemId)) && removedIds.push(itemId);
|
||||
|
||||
const emptyExistingSet = (existingIds.length === 0);
|
||||
|
||||
for(const id of removedIds) removePetItemById(id, set);
|
||||
|
||||
for(const id of addedIds)
|
||||
{
|
||||
const parser = fragment.get(id);
|
||||
|
||||
if(!parser) continue;
|
||||
|
||||
addSinglePetItem(parser, set, true);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
export function removePetItemById(id: number, set: PetItem[]): PetItem
|
||||
{
|
||||
let index = 0;
|
||||
|
||||
while(index < set.length)
|
||||
{
|
||||
const petItem = set[index];
|
||||
|
||||
if(petItem && (petItem.id === id))
|
||||
{
|
||||
if(getPlacingItemId() === petItem.id)
|
||||
{
|
||||
cancelRoomObjectPlacement();
|
||||
|
||||
setTimeout(() => dispatchUiEvent(new InventoryEvent(InventoryEvent.SHOW_INVENTORY)), 1);
|
||||
}
|
||||
|
||||
set.splice(index, 1);
|
||||
|
||||
return petItem;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function addSinglePetItem(petData: PetData, set: PetItem[], unseen: boolean = true): PetItem
|
||||
{
|
||||
const petItem = new PetItem(petData);
|
||||
|
||||
if(unseen)
|
||||
{
|
||||
set.unshift(petItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
set.push(petItem);
|
||||
}
|
||||
|
||||
return petItem;
|
||||
}
|
2
src/views/inventory/views/pet/InventoryPetView.scss
Normal file
2
src/views/inventory/views/pet/InventoryPetView.scss
Normal file
@ -0,0 +1,2 @@
|
||||
@import './item/InventoryPetItemView';
|
||||
@import './results/InventoryPetResultsView';
|
97
src/views/inventory/views/pet/InventoryPetView.tsx
Normal file
97
src/views/inventory/views/pet/InventoryPetView.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
import { RequestPetsComposer, 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 { InventoryPetActions } from '../../reducers/InventoryPetReducer';
|
||||
import { attemptPetPlacement } from '../../utils/PetUtilities';
|
||||
import { InventoryPetViewProps } from './InventoryPetView.types';
|
||||
import { InventoryPetResultsView } from './results/InventoryPetResultsView';
|
||||
|
||||
export const InventoryPetView: FC<InventoryPetViewProps> = props =>
|
||||
{
|
||||
const { roomSession = null, roomPreviewer = null } = props;
|
||||
const { petState = null, dispatchPetState = null } = useInventoryContext();
|
||||
const { needsPetUpdate = false, petItem = null, petItems = [] } = petState;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(needsPetUpdate)
|
||||
{
|
||||
dispatchPetState({
|
||||
type: InventoryPetActions.SET_NEEDS_UPDATE,
|
||||
payload: {
|
||||
flag: false
|
||||
}
|
||||
});
|
||||
|
||||
SendMessageHook(new RequestPetsComposer());
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatchPetState({
|
||||
type: InventoryPetActions.SET_PET_ITEM,
|
||||
payload: {
|
||||
petItem: null
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}, [ needsPetUpdate, petItems, dispatchPetState ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!petItem || !roomPreviewer) return;
|
||||
|
||||
const petData = petItem.petData;
|
||||
|
||||
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.addPetIntoRoom(petData.figureData.figuredata);
|
||||
}, [ roomPreviewer, petItem ]);
|
||||
|
||||
if(!petItems || !petItems.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.pets.title') }
|
||||
</div>
|
||||
<div className="h6 text-black">{ LocalizeText('inventory.empty.pets.desc') }</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="row h-100">
|
||||
<div className="col-7">
|
||||
<InventoryPetResultsView petItems={ petItems } />
|
||||
</div>
|
||||
<div className="d-flex flex-column col-5 justify-space-between">
|
||||
<RoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
|
||||
{ petItem && <div className="d-flex flex-column flex-grow-1">
|
||||
<p className="flex-grow-1 fs-6 text-black my-2">{ petItem.petData.name }</p>
|
||||
{ !!roomSession && <button type="button" className="btn btn-success" onClick={ event => attemptPetPlacement(petItem) }>{ LocalizeText('inventory.furni.placetoroom') }</button> }
|
||||
</div> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
7
src/views/inventory/views/pet/InventoryPetView.types.ts
Normal file
7
src/views/inventory/views/pet/InventoryPetView.types.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { IRoomSession, RoomPreviewer } from 'nitro-renderer';
|
||||
|
||||
export interface InventoryPetViewProps
|
||||
{
|
||||
roomSession: IRoomSession;
|
||||
roomPreviewer: RoomPreviewer;
|
||||
}
|
17
src/views/inventory/views/pet/item/InventoryPetItemView.scss
Normal file
17
src/views/inventory/views/pet/item/InventoryPetItemView.scss
Normal file
@ -0,0 +1,17 @@
|
||||
.inventory-pet-item-container {
|
||||
height: 48px;
|
||||
max-height: 48px;
|
||||
|
||||
.inventory-pet-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-color: $muted !important;
|
||||
background-color: #CDD3D9;
|
||||
overflow: hidden;
|
||||
|
||||
&.active {
|
||||
border-color: $white !important;
|
||||
background-color: #ECECEC;
|
||||
}
|
||||
}
|
||||
}
|
46
src/views/inventory/views/pet/item/InventoryPetItemView.tsx
Normal file
46
src/views/inventory/views/pet/item/InventoryPetItemView.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { MouseEventType } from 'nitro-renderer';
|
||||
import { FC, MouseEvent, useCallback, useState } from 'react';
|
||||
import { PetImageView } from '../../../../pet-image/PetImageView';
|
||||
import { useInventoryContext } from '../../../context/InventoryContext';
|
||||
import { InventoryPetActions } from '../../../reducers/InventoryPetReducer';
|
||||
import { attemptPetPlacement } from '../../../utils/PetUtilities';
|
||||
import { InventoryPetItemViewProps } from './InventoryPetItemView.types';
|
||||
|
||||
export const InventoryPetItemView: FC<InventoryPetItemViewProps> = props =>
|
||||
{
|
||||
const { petItem } = props;
|
||||
const { petState = null, dispatchPetState = null } = useInventoryContext();
|
||||
const [ isMouseDown, setMouseDown ] = useState(false);
|
||||
const isActive = (petState.petItem === petItem);
|
||||
|
||||
const onMouseEvent = useCallback((event: MouseEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case MouseEventType.MOUSE_DOWN:
|
||||
dispatchPetState({
|
||||
type: InventoryPetActions.SET_PET_ITEM,
|
||||
payload: { petItem }
|
||||
});
|
||||
|
||||
setMouseDown(true);
|
||||
return;
|
||||
case MouseEventType.MOUSE_UP:
|
||||
setMouseDown(false);
|
||||
return;
|
||||
case MouseEventType.ROLL_OUT:
|
||||
if(!isMouseDown || !isActive) return;
|
||||
|
||||
attemptPetPlacement(petItem);
|
||||
return;
|
||||
}
|
||||
}, [ isActive, isMouseDown, petItem, dispatchPetState ]);
|
||||
|
||||
return (
|
||||
<div className="col pe-1 pb-1 inventory-pet-item-container">
|
||||
<div className={ 'position-relative border border-2 rounded inventory-pet-item cursor-pointer ' + (isActive ? 'active' : '') } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent }>
|
||||
<PetImageView figure={ petItem.petData.figureData.figuredata } direction={ 3 } headOnly={ true } />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { PetItem } from '../../../utils/PetItem';
|
||||
|
||||
export interface InventoryPetItemViewProps
|
||||
{
|
||||
petItem: PetItem;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
.pet-item-container {
|
||||
height: 226px;
|
||||
max-height: 226px;
|
||||
overflow-y: auto;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { FC } from 'react';
|
||||
import { InventoryPetItemView } from '../item/InventoryPetItemView';
|
||||
import { InventoryPetResultsViewProps } from './InventoryPetResultsView.types';
|
||||
|
||||
export const InventoryPetResultsView: FC<InventoryPetResultsViewProps> = props =>
|
||||
{
|
||||
const { petItems = [] } = props;
|
||||
|
||||
return (
|
||||
<div className="row row-cols-5 align-content-start g-0 pet-item-container">
|
||||
{ (petItems && petItems.length && petItems.map((item, index) =>
|
||||
{
|
||||
return <InventoryPetItemView key={ index } petItem={ item } />
|
||||
})) || null }
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { PetItem } from '../../../utils/PetItem';
|
||||
|
||||
export interface InventoryPetResultsViewProps
|
||||
{
|
||||
petItems: PetItem[];
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user