mirror of
https://github.com/billsonnn/nitro-react.git
synced 2025-01-31 10:22:36 +01:00
Avatar randomizer
This commit is contained in:
parent
f88efac443
commit
4370b20af7
28
src/utils/Randomizer.ts
Normal file
28
src/utils/Randomizer.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export class Randomizer
|
||||||
|
{
|
||||||
|
public static getRandomNumber(count: number): number
|
||||||
|
{
|
||||||
|
return Math.floor(Math.random() * count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getRandomElement<T>(elements: T[]): T
|
||||||
|
{
|
||||||
|
return elements[this.getRandomNumber(elements.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getRandomElements<T>(elements: T[], count: number): T[]
|
||||||
|
{
|
||||||
|
const result: T[] = new Array(count);
|
||||||
|
let len = elements.length;
|
||||||
|
const taken = new Array(len);
|
||||||
|
|
||||||
|
while(count--)
|
||||||
|
{
|
||||||
|
var x = this.getRandomNumber(len);
|
||||||
|
result[count] = elements[x in taken ? taken[x] : x];
|
||||||
|
taken[x] = --len in taken ? taken[len] : len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -3,3 +3,4 @@ export * from './LocalizeBadgeDescription';
|
|||||||
export * from './LocalizeBageName';
|
export * from './LocalizeBageName';
|
||||||
export * from './LocalizeShortNumber';
|
export * from './LocalizeShortNumber';
|
||||||
export * from './LocalizeText';
|
export * from './LocalizeText';
|
||||||
|
export * from './Randomizer';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { AvatarDirectionAngle, AvatarEditorFigureCategory, FigureSetIdsMessageEvent, UserFigureComposer, UserWardrobePageComposer, UserWardrobePageEvent } from 'nitro-renderer';
|
import { AvatarDirectionAngle, AvatarEditorFigureCategory, FigureSetIdsMessageEvent, UserFigureComposer, UserWardrobePageComposer, UserWardrobePageEvent } from 'nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
import { GetSessionDataManager } from '../../api';
|
import { GetClubMemberLevel, GetSessionDataManager } from '../../api';
|
||||||
import { AvatarEditorEvent } from '../../events/avatar-editor';
|
import { AvatarEditorEvent } from '../../events/avatar-editor';
|
||||||
import { CreateMessageHook, SendMessageHook } from '../../hooks';
|
import { CreateMessageHook, SendMessageHook } from '../../hooks';
|
||||||
import { useUiEvent } from '../../hooks/events/ui/ui-event';
|
import { useUiEvent } from '../../hooks/events/ui/ui-event';
|
||||||
@ -10,6 +10,7 @@ import { AvatarEditorViewProps } from './AvatarEditorView.types';
|
|||||||
import { AvatarEditorUtilities } from './common/AvatarEditorUtilities';
|
import { AvatarEditorUtilities } from './common/AvatarEditorUtilities';
|
||||||
import { BodyModel } from './common/BodyModel';
|
import { BodyModel } from './common/BodyModel';
|
||||||
import { FigureData } from './common/FigureData';
|
import { FigureData } from './common/FigureData';
|
||||||
|
import { generateRandomFigure } from './common/FigureGenerator';
|
||||||
import { HeadModel } from './common/HeadModel';
|
import { HeadModel } from './common/HeadModel';
|
||||||
import { IAvatarEditorCategoryModel } from './common/IAvatarEditorCategoryModel';
|
import { IAvatarEditorCategoryModel } from './common/IAvatarEditorCategoryModel';
|
||||||
import { LegModel } from './common/LegModel';
|
import { LegModel } from './common/LegModel';
|
||||||
@ -167,6 +168,14 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
|||||||
resetCategories();
|
resetCategories();
|
||||||
}, [ lastFigure, lastGender, loadAvatarInEditor, resetCategories ]);
|
}, [ lastFigure, lastGender, loadAvatarInEditor, resetCategories ]);
|
||||||
|
|
||||||
|
const randomizeFigure = useCallback(() =>
|
||||||
|
{
|
||||||
|
const figure = generateRandomFigure(figureData, figureData.gender, GetClubMemberLevel(), figureSetIds, [ FigureData.FACE ]);
|
||||||
|
|
||||||
|
loadAvatarInEditor(figure, figureData.gender, false);
|
||||||
|
resetCategories();
|
||||||
|
}, [ figureData, figureSetIds, loadAvatarInEditor, resetCategories ]);
|
||||||
|
|
||||||
const rotateFigure = useCallback((direction: number) =>
|
const rotateFigure = useCallback((direction: number) =>
|
||||||
{
|
{
|
||||||
if(direction < AvatarDirectionAngle.MIN_DIRECTION)
|
if(direction < AvatarDirectionAngle.MIN_DIRECTION)
|
||||||
@ -308,6 +317,9 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
|||||||
<button type="button" className="btn btn-sm btn-secondary" onClick={ clearFigure }>
|
<button type="button" className="btn btn-sm btn-secondary" onClick={ clearFigure }>
|
||||||
<i className="fas fa-trash" />
|
<i className="fas fa-trash" />
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" className="btn btn-sm btn-secondary" onClick={ randomizeFigure }>
|
||||||
|
<i className="fas fa-dice" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" className="btn btn-success btn-sm w-100" onClick={ saveFigure }>{ LocalizeText('avatareditor.save') }</button>
|
<button type="button" className="btn btn-success btn-sm w-100" onClick={ saveFigure }>{ LocalizeText('avatareditor.save') }</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,7 +72,7 @@ export class AvatarEditorUtilities
|
|||||||
const clubItemsDimmed = this.clubItemsDimmed;
|
const clubItemsDimmed = this.clubItemsDimmed;
|
||||||
const clubMemberLevel = GetClubMemberLevel();
|
const clubMemberLevel = GetClubMemberLevel();
|
||||||
|
|
||||||
for(const partColor of palette.colors.values())
|
for(const partColor of palette.colors.getValues())
|
||||||
{
|
{
|
||||||
if(partColor.isSelectable && (clubItemsDimmed || (clubMemberLevel >= partColor.clubLevel)))
|
if(partColor.isSelectable && (clubItemsDimmed || (clubMemberLevel >= partColor.clubLevel)))
|
||||||
{
|
{
|
||||||
@ -255,7 +255,7 @@ export class AvatarEditorUtilities
|
|||||||
|
|
||||||
if(!palette) return -1;
|
if(!palette) return -1;
|
||||||
|
|
||||||
for(const color of palette.colors.values())
|
for(const color of palette.colors.getValues())
|
||||||
{
|
{
|
||||||
if(!color.isSelectable || (GetClubMemberLevel() < color.clubLevel)) continue;
|
if(!color.isSelectable || (GetClubMemberLevel() < color.clubLevel)) continue;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ export class FigureData
|
|||||||
public static TROUSERS: string = 'lg';
|
public static TROUSERS: string = 'lg';
|
||||||
public static SHOES: string = 'sh';
|
public static SHOES: string = 'sh';
|
||||||
public static TROUSER_ACCESSORIES: string = 'wa';
|
public static TROUSER_ACCESSORIES: string = 'wa';
|
||||||
public static PREVIEW_AVATAR_DIRECTION: number = 4;
|
public static SET_TYPES = [ FigureData.FACE, FigureData.HAIR, FigureData.HAT, FigureData.HEAD_ACCESSORIES, FigureData.EYE_ACCESSORIES, FigureData.FACE_ACCESSORIES, FigureData.JACKET, FigureData.SHIRT, FigureData.CHEST_ACCESSORIES, FigureData.CHEST_PRINTS, FigureData.TROUSERS, FigureData.SHOES, FigureData.TROUSERS ];
|
||||||
|
|
||||||
private _data: Map<string, number>;
|
private _data: Map<string, number>;
|
||||||
private _colors: Map<string, number[]>;
|
private _colors: Map<string, number[]>;
|
||||||
@ -76,9 +76,9 @@ export class FigureData
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPartSetId(partSetId: string): number
|
public getPartSetId(setType: string): number
|
||||||
{
|
{
|
||||||
const existing = this._data.get(partSetId);
|
const existing = this._data.get(setType);
|
||||||
|
|
||||||
if(existing !== undefined) return existing;
|
if(existing !== undefined) return existing;
|
||||||
|
|
||||||
@ -134,15 +134,15 @@ export class FigureData
|
|||||||
return figureString;
|
return figureString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public savePartData(k: string, _arg_2: number, _arg_3: number[], _arg_4: boolean = false): void
|
public savePartData(setType: string, partId: number, colorIds: number[], update: boolean = false): void
|
||||||
{
|
{
|
||||||
this.savePartSetId(k, _arg_2, _arg_4);
|
this.savePartSetId(setType, partId, update);
|
||||||
this.savePartSetColourId(k, _arg_3, _arg_4);
|
this.savePartSetColourId(setType, colorIds, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
private savePartSetId(k: string, _arg_2: number, _arg_3: boolean = true): void
|
private savePartSetId(setType: string, partId: number, update: boolean = true): void
|
||||||
{
|
{
|
||||||
switch(k)
|
switch(setType)
|
||||||
{
|
{
|
||||||
case FigureData.FACE:
|
case FigureData.FACE:
|
||||||
case FigureData.HAIR:
|
case FigureData.HAIR:
|
||||||
@ -157,18 +157,18 @@ export class FigureData
|
|||||||
case FigureData.TROUSERS:
|
case FigureData.TROUSERS:
|
||||||
case FigureData.SHOES:
|
case FigureData.SHOES:
|
||||||
case FigureData.TROUSER_ACCESSORIES:
|
case FigureData.TROUSER_ACCESSORIES:
|
||||||
if(_arg_2 >= 0)
|
if(partId >= 0)
|
||||||
{
|
{
|
||||||
this._data.set(k, _arg_2);
|
this._data.set(setType, partId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this._data.delete(k);
|
this._data.delete(setType);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_arg_3) this.updateView();
|
if(update) this.updateView();
|
||||||
}
|
}
|
||||||
|
|
||||||
public savePartSetColourId(setType: string, colorIds: number[], update: boolean = true): void
|
public savePartSetColourId(setType: string, colorIds: number[], update: boolean = true): void
|
||||||
|
128
src/views/avatar-editor/common/FigureGenerator.ts
Normal file
128
src/views/avatar-editor/common/FigureGenerator.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { AvatarFigureContainer, IFigurePartSet, IPalette, IPartColor, SetType } from 'nitro-renderer';
|
||||||
|
import { GetAvatarRenderManager } from '../../../api';
|
||||||
|
import { Randomizer } from '../../../utils';
|
||||||
|
import { FigureData } from './FigureData';
|
||||||
|
|
||||||
|
const RANDOM_TRIES: number = 20;
|
||||||
|
|
||||||
|
function getTotalColors(partSet: IFigurePartSet): number
|
||||||
|
{
|
||||||
|
const parts = partSet.parts;
|
||||||
|
|
||||||
|
let totalColors = 0;
|
||||||
|
|
||||||
|
for(const part of parts) totalColors = Math.max(totalColors, part.colorLayerIndex);
|
||||||
|
|
||||||
|
return totalColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomSetTypes(requiredSets: string[], options: string[]): string[]
|
||||||
|
{
|
||||||
|
options = options.filter(option => (requiredSets.indexOf(option) === -1));
|
||||||
|
|
||||||
|
return [ ...requiredSets, ...Randomizer.getRandomElements(options, (Randomizer.getRandomNumber(options.length) + 1)) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomPartSet(setType: SetType, gender: string, clubLevel: number = 0, figureSetIds: number[] = []): IFigurePartSet
|
||||||
|
{
|
||||||
|
if(!setType) return null;
|
||||||
|
|
||||||
|
const options = setType.partSets.getValues();
|
||||||
|
|
||||||
|
let selectedSet: IFigurePartSet = null;
|
||||||
|
let randomTries = RANDOM_TRIES;
|
||||||
|
|
||||||
|
while(!selectedSet)
|
||||||
|
{
|
||||||
|
if(!randomTries) return setType.getDefaultPartSet(gender);
|
||||||
|
|
||||||
|
const randomSet = Randomizer.getRandomElement(options);
|
||||||
|
|
||||||
|
if(!randomSet.isSelectable || ((randomSet.gender !== 'U') && (randomSet.gender !== gender)) || (randomSet.clubLevel > clubLevel) || (randomSet.isSellable && (figureSetIds.indexOf(randomSet.id) === -1)))
|
||||||
|
{
|
||||||
|
randomTries--;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return randomSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomColor(options: IPartColor[], clubLevel: number = 0): IPartColor
|
||||||
|
{
|
||||||
|
const randomColor = Randomizer.getRandomElement(options);
|
||||||
|
|
||||||
|
if(!randomColor.isSelectable || (randomColor.clubLevel > clubLevel)) return null;
|
||||||
|
|
||||||
|
return randomColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomColors(palette: IPalette, partSet: IFigurePartSet, clubLevel: number = 0): number[]
|
||||||
|
{
|
||||||
|
if(!palette) return [];
|
||||||
|
|
||||||
|
const options = palette.colors.getValues();
|
||||||
|
|
||||||
|
let totalColors = getTotalColors(partSet);
|
||||||
|
let selectedColors: number[] = [];
|
||||||
|
let randomTries = RANDOM_TRIES;
|
||||||
|
|
||||||
|
while(totalColors)
|
||||||
|
{
|
||||||
|
if(!randomTries) break;
|
||||||
|
|
||||||
|
const randomColor = getRandomColor(options, clubLevel);
|
||||||
|
|
||||||
|
if(!randomColor)
|
||||||
|
{
|
||||||
|
randomTries--;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedColors.push(randomColor.id);
|
||||||
|
|
||||||
|
totalColors--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateRandomFigure(figureData: FigureData, gender: string, clubLevel: number = 0, figureSetIds: number[] = [], ignoredSets: string[] = []): string
|
||||||
|
{
|
||||||
|
const structure = GetAvatarRenderManager().structure;
|
||||||
|
const figureContainer = new AvatarFigureContainer('');
|
||||||
|
const requiredSets = getRandomSetTypes(structure.getMandatorySetTypeIds(gender, clubLevel), FigureData.SET_TYPES);
|
||||||
|
|
||||||
|
for(const setType of ignoredSets)
|
||||||
|
{
|
||||||
|
const partSetId = figureData.getPartSetId(setType);
|
||||||
|
const colors = figureData.getColorIds(setType);
|
||||||
|
|
||||||
|
figureContainer.updatePart(setType, partSetId, colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const type of requiredSets)
|
||||||
|
{
|
||||||
|
if(figureContainer.hasPartType(type)) continue;
|
||||||
|
|
||||||
|
const setType = (structure.figureData.getSetType(type) as SetType);
|
||||||
|
const selectedSet = getRandomPartSet(setType, gender, clubLevel, figureSetIds);
|
||||||
|
|
||||||
|
if(!selectedSet) continue;
|
||||||
|
|
||||||
|
let selectedColors: number[] = [];
|
||||||
|
|
||||||
|
if(selectedSet.isColorable)
|
||||||
|
{
|
||||||
|
selectedColors = getRandomColors(structure.figureData.getPalette(setType.paletteID), selectedSet, clubLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
figureContainer.updatePart(setType.type, selectedSet.id, selectedColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return figureContainer.getFigureString();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user