import util from "./util.js" import InternalError from "./error/InternalError.js" import EAvatarRelationRank from "./enum/EAvatarRelationRank.js" import AvatarGroup from "./enum/AvatarGroup.js" import MotionType from "./enum/MotionType.js" import Queue from "./Queue.js" import Logger from "./Logger.js" const logger = new Logger('xverse-avatar') export default class XverseAvatar extends EventEmitter { constructor({userId: e, isHost: t, room: r, avatarId: n, isSelf: o, group: a=AvatarGroup.Npc}) { super(); E(this, "xAvatar"); E(this, "_isHost", !1); E(this, "_room"); E(this, "_withModel", !1); E(this, "_userId"); E(this, "group", AvatarGroup.User); E(this, "state", "idle"); E(this, "isLoading", !0); E(this, "_isMoving", !1); E(this, "_isRotating", !1); E(this, "_failed", !1); E(this, "disconnected", !1); E(this, "_avatarId"); E(this, "prioritySync", !1); E(this, "priority", EAvatarRelationRank.Stranger); E(this, "_avatarModel"); E(this, "_motionType", MotionType.Walk); E(this, "isSelf", !1); E(this, "_lastAnimTraceId", ""); E(this, "statusSyncQueue", new Queue); E(this, "extraInfo", {}); E(this, "setPosition", e=>{ var t; !this._room.signal.isUpdatedYUV || (t = this.xAvatar) == null || t.setPosition(positionPrecisionProtect(e)) } ); E(this, "setRotation", e=>{ var t; !this._room.signal.isUpdatedYUV || (t = this.xAvatar) == null || t.setRotation(rotationPrecisionProtect(e)) } ); E(this, "stopAnimation", ()=>{ var e, t; (t = (e = this.xAvatar) == null ? void 0 : e.controller) == null || t.stopAnimation() } ); E(this, "_playAnimation", async(e,t=!0,r=!1)=>{ var o; if (!this._room.signal.isUpdatedYUV) return; if (this.state !== "idle" && !r) return logger.debug("_playAnimation", "state is not idle"), Promise.resolve("_playAnimation, state is not idle"); const n = Date.now(); try { if (!((o = this.xAvatar) != null && o.controller)) return Promise.reject(new InternalError(`[avatar: ${this.userId}] Play animation failed: ${e}, no controller`)); this.isSelf && setTimeout(()=>{ logger.infoAndReportMeasurement({ tag: e, startTime: n, value: 0, metric: "playAnimationStart" }) } ); const a = util.uuid(); this._lastAnimTraceId = a, await this.xAvatar.controller.playAnimation(e, t), a === this._lastAnimTraceId && !this.isMoving && !t && e !== "Idle" && this.xAvatar.controller.playAnimation("Idle", t).catch(s=>{ logger.error(`[avatar: ${this.userId}] Play animation failed [force idle]`, s) } ), this.isSelf && logger.infoAndReportMeasurement({ tag: e, startTime: n, extra: { loop: t }, metric: "playAnimationEnd" }) } catch (a) { return logger.error(`[avatar: ${this.userId}] Play animation failed: ${e}`, a), this.isSelf && logger.infoAndReportMeasurement({ tag: e, startTime: n, metric: "playAnimationEnd", error: a, extra: { loop: t } }), Promise.reject(a) } } ); E(this, "changeComponents", async e=>{ const {mode: t, endAnimation: r=""} = e || {} , n = JSON.parse(JSON.stringify(e.avatarComponents)); let o = avatarComponentsValidate(n, this._avatarModel); return !ChangeComponentsMode[t] && !o && (o = new ParamError(`changeComponents failed, mode: ${t} is invalid`)), o ? (logger.error(o), Promise.reject(o)) : this._changeComponents({ avatarComponents: n, mode: t, endAnimation: r }).then(()=>{ this.isSelf && t !== ChangeComponentsMode.Preview && this.avatarComponentsSync(this.avatarComponents) } ) } ); E(this, "_changeComponents", async e=>{ var o; const {avatarComponents: t=[], mode: r} = e || {} , n = Date.now(); try { if (!this.xAvatar) return Promise.reject(new InternalError("changeComponents failed, without instance: xAvatar")); const a = await avatarComponentsModify(this._avatarModel, t) , s = [] , l = await avatarComponentsParser(this._avatarModel, a, this.avatarComponents); if (l.length === 0) return this.avatarComponents; await this.beforeChangeComponentsHook(e); for (const u of l) { const {id: c, type: h, url: f, suitComb: d} = u; s.push((o = this.xAvatar) == null ? void 0 : o.addComponent(c, h, f, d)) } return await Promise.all(s), this.emit("componentsChanged", { components: this.avatarComponents, mode: r }), this.isSelf && logger.infoAndReportMeasurement({ tag: "changeComponents", startTime: n, metric: "changeComponents", extra: { inputComponents: t, finalComponents: this.avatarComponents, mode: ChangeComponentsMode[r] } }), this.avatarComponents } catch (a) { return this.isSelf && logger.infoAndReportMeasurement({ tag: "changeComponents", startTime: n, metric: "changeComponents", error: a, extra: { inputComponents: t, finalComponents: this.avatarComponents, mode: ChangeComponentsMode[r] } }), Promise.reject(a) } } ); E(this, "avatarComponentsSync", e=>{ e = e.map(t=>({ type: t.type, id: t.id })), this._room.actionsHandler.avatarComponentsSync(e) } ); E(this, "hide", ()=>{ var e; if ((e = this.xAvatar) != null && e.hide()) return Promise.resolve(`avatar: ${this.userId} hide success`); { const t = `avatar: ${this.userId} hide failed ${!this.xAvatar && "without instance: xAvatar"}`; return logger.warn(t), Promise.reject(t) } } ); E(this, "show", ()=>{ var e; if ((e = this.xAvatar) != null && e.show()) return Promise.resolve(`avatar: ${this.userId} show success`); { const t = `avatar: ${this.userId} show failed ${!this.xAvatar && "without instance: xAvatar"}`; return logger.warn(t), Promise.reject(t) } } ); E(this, "sayTimer"); this._userId = e, this._room = r, this.isSelf = o || !1, this._withModel = !!n, this._isHost = t || !1, this._avatarId = n, this.group = a, this._room.modelManager.getAvatarModelList().then(s=>{ const l = s.find(u=>u.id === n); l && (this._avatarModel = l) } ) } get avatarId() { return this._avatarId } get isRender() { var e; return !!((e = this.xAvatar) != null && e.isRender) } get isHidden() { var e; return !!((e = this.xAvatar) != null && e.isHide) } get motionType() { return this._motionType } set motionType(e) { this._motionType = e } get nickname() { var e; return (e = this.xAvatar) == null ? void 0 : e.nickName } get words() { var e; return (e = this.xAvatar) == null ? void 0 : e.words } get isHost() { return this._isHost } get failed() { return this._failed } get scale() { var e; return (e = this.xAvatar) == null ? void 0 : e.scale } get animations() { var e; return !this.xAvatar || !this.xAvatar.controller ? [] : ((e = this.xAvatar) == null ? void 0 : e.getAvaliableAnimations()) || [] } get position() { var e; return (e = this.xAvatar) == null ? void 0 : e.position } get rotation() { var e; return (e = this.xAvatar) == null ? void 0 : e.rotation } get pose() { return { position: this.position, angle: this.rotation } } get id() { return this.userId } get isMoving() { return this._isMoving } set isMoving(e) { this._isMoving = e, this.state = e ? "moving" : "idle" } get isRotating() { return this._isRotating } set isRotating(e) { this._isRotating = e, this.state = e ? "rotating" : "idle" } get withModel() { return this._withModel } get avatarComponents() { var e; return JSON.parse(JSON.stringify(((e = this.xAvatar) == null ? void 0 : e.clothesList) || [])) } get userId() { return this._userId } get removeWhenDisconnected() { return this.extraInfo && this.extraInfo.removeWhenDisconnected !== void 0 ? this.extraInfo.removeWhenDisconnected : !0 } setConnectionStatus(e) { this.disconnected !== e && (this.disconnected = e, e ? this.emit("disconnected") : this.emit("reconnected"), logger.warn(`avatar ${this.userId} status changed, disconnected:`, e)) } setScale(e) { var t; (t = this.xAvatar) == null || t.setScale(e > 0 ? e : 1) } async playAnimation(e) { const {animationName: t, loop: r, extra: n} = e || {}; if (this.isSelf) { if (this.isMoving) try { await this.stopMoving() } catch (a) { return logger.error(`stopMoving error before playAnimation ${t}`, a), Promise.reject(`stopMoving error before playAnimation ${t}`) } const o = { info: { userId: this.userId, animation: t, loop: r, extra: encodeURIComponent(n || "") }, broadcastType: CoreBroadcastType.PlayAnimation }; this._room.avatarManager.broadcast.broadcast({ data: o }) } return this.emit("animationStart", { animationName: t, extra: safeDecodeURIComponent(n || "") }), this._playAnimation(t, r).then(()=>{ this.emit("animationEnd", { animationName: t, extra: safeDecodeURIComponent(n || "") }) } ) } async beforeChangeComponentsHook(e) {} turnTo(e) { if (this._room.viewMode === "observer") { this._room.sceneManager.cameraComponent.MainCamera.setTarget(ue4Position2Xverse(e.point)); return } return this._room.actionsHandler.turnTo(e).then(()=>{ this.emit("viewChanged", { extra: (e == null ? void 0 : e.extra) || "" }) } ) } async moveTo(e) { const {point: t, extra: r=""} = e || {}; if (!this.position) return Promise.reject(new ParamError("avatar position is empty")); if (typeof r != "string" || typeof r == "string" && r.length > 64) { const a = "extra shoud be string which length less than 64"; return logger.warn(a), Promise.reject(new ParamError(a)) } const o = util.getDistance(this.position, t) / 100 > 100 ? MotionType.Run : MotionType.Walk; return this._room.actionsHandler.moveTo({ point: t, motionType: o, extra: r }) } async stopMoving() { return this._room.actionsHandler.stopMoving() } rotateTo(e) { return this._room.actionsHandler.rotateTo(e) } setRayCast(e) { this.xAvatar && (this.xAvatar.isRayCastEnable = e) } say(e, t) { if (this.sayTimer && window.clearTimeout(this.sayTimer), !this.xAvatar) { logger.error("say failed, without instance: xAvatar"); return } this.xAvatar.say(e, { scale: this.xAvatar.scale, isUser: this.group === AvatarGroup.User }), !(t === void 0 || t <= 0) && (this.sayTimer = window.setTimeout(()=>{ this.silent() } , t)) } silent() { var e; if (!this.xAvatar) { logger.error("silent failed, without instance: xAvatar"); return } (e = this.xAvatar) == null || e.silent() } setMotionType({type: e=MotionType.Walk}) { return this.motionType === e ? Promise.resolve() : this._room.actionsHandler.setMotionType(e).then(()=>{ this._motionType = e } ) } setNickname(e) { return this._room.actionsHandler.setNickName(encodeURIComponent(e)) } _setNickname(e) { var r, n; if (!e) return; const t = safeDecodeURIComponent(e); ((r = this.xAvatar) == null ? void 0 : r.nickName) !== t && (this.isSelf && (this._room.updateCurrentNetworkOptions({ nickname: t }), this._room.options.nickname = t), (n = this.xAvatar) == null || n.setNickName(t, { scale: this.xAvatar.scale })) } _move(e) { var s; const {start: t, end: r, walkSpeed: n, moveAnimation: o="Walking", inter: a=[]} = e || {}; return (s = this.xAvatar) == null ? void 0 : s.move(t, r, n, o, a) } setPickBoxScale(e=1) { return this.xAvatar ? (this.xAvatar.setPickBoxScale(e), !0) : (logger.error("setPickBoxScale failed, without instance: xAvatar"), !1) } transfer(e) { const {player: t, camera: r, areaName: n, attitude: o, pathName: a} = e; return this._room.actionsHandler.transfer({ renderType: RenderType.RotationVideo, player: t, camera: r, areaName: n, attitude: o, pathName: a, tag: "transfer" }) } avatarLoadedHook() {} avatarStartMovingHook() {} avatarStopMovingHook() {} async statusSync(e) { var t, r, n; try { if ((t = e.event) != null && t.rotateEvent) { const {angle: o, speed: a} = e.event.rotateEvent , s = this.motionType === MotionType.Run ? "Running" : "Walking"; this.rotation && (this.rotation.yaw = this.rotation.yaw % 360, o.yaw - this.rotation.yaw > 180 && (o.yaw = 180 - o.yaw), this.isRotating = !0, await this.xAvatar.rotateTo(o, this.rotation, s).then(()=>{ this._playAnimation("Idle", !0), this.isRotating = !1 } )) } if (e.event && (((r = e.event) == null ? void 0 : r.points.length) || 0) > 1 && !this.isSelf) { this.isMoving = !0, e.playerState.attitude && (this._motionType = e.playerState.attitude); const o = this.motionType === MotionType.Run ? "Running" : "Walking" , a = this._room.skin.routeList.find(l=>l.areaName === this._room.currentState.areaName) , s = ((a == null ? void 0 : a.step) || 7.5) * 30 * (25 / 30); this.position && await this._move({ start: this.position, end: e.event.points[e.event.points.length - 1], walkSpeed: s, moveAnimation: o, inter: (n = e.event) == null ? void 0 : n.points.slice(0, -1) }).then(()=>{ this.isMoving = !1 } ) } } catch { return } } }