diff --git a/packages/communication/src/messages/parser/room/engine/ObjectsRollingParser.ts b/packages/communication/src/messages/parser/room/engine/ObjectsRollingParser.ts index 32240d8..cfa9136 100644 --- a/packages/communication/src/messages/parser/room/engine/ObjectsRollingParser.ts +++ b/packages/communication/src/messages/parser/room/engine/ObjectsRollingParser.ts @@ -1,6 +1,13 @@ import { IMessageDataWrapper, IMessageParser, ObjectRolling } from '@nitrots/api'; import { Vector3d } from '@nitrots/utils'; +function parseLocaleFloat(value: string): number +{ + if(!value) return 0; + + return parseFloat(value.replace(',', '.')); +} + export class ObjectsRollingParser implements IMessageParser { private _rollerId: number; @@ -31,8 +38,8 @@ export class ObjectsRollingParser implements IMessageParser while(totalItems > 0) { const id = wrapper.readInt(); - const height = parseFloat(wrapper.readString()); - const nextHeight = parseFloat(wrapper.readString()); + const height = parseLocaleFloat(wrapper.readString()); + const nextHeight = parseLocaleFloat(wrapper.readString()); const rollingData = new ObjectRolling(id, new Vector3d(x, y, height), new Vector3d(nextX, nextY, nextHeight)); this._itemsRolling.push(rollingData); @@ -46,8 +53,8 @@ export class ObjectsRollingParser implements IMessageParser const movementType = wrapper.readInt(); const unitId = wrapper.readInt(); - const height = parseFloat(wrapper.readString()); - const nextHeight = parseFloat(wrapper.readString()); + const height = parseLocaleFloat(wrapper.readString()); + const nextHeight = parseLocaleFloat(wrapper.readString()); switch(movementType) { diff --git a/packages/communication/src/messages/parser/room/furniture/floor/FurnitureFloorDataParser.ts b/packages/communication/src/messages/parser/room/furniture/floor/FurnitureFloorDataParser.ts index f8340d1..fb4ac09 100644 --- a/packages/communication/src/messages/parser/room/furniture/floor/FurnitureFloorDataParser.ts +++ b/packages/communication/src/messages/parser/room/furniture/floor/FurnitureFloorDataParser.ts @@ -1,6 +1,13 @@ import { IMessageDataWrapper, IObjectData } from '@nitrots/api'; import { FurnitureDataParser } from '../FurnitureDataParser'; +function parseLocaleFloat(value: string): number +{ + if(!value) return 0; + + return parseFloat(value.replace(',', '.')); +} + export class FurnitureFloorDataParser { private _itemId: number; @@ -57,8 +64,8 @@ export class FurnitureFloorDataParser this._x = wrapper.readInt(); this._y = wrapper.readInt(); this._direction = ((wrapper.readInt() % 8) * 45); - this._z = parseFloat(wrapper.readString()); - this._stackHeight = parseFloat(wrapper.readString()); + this._z = parseLocaleFloat(wrapper.readString()); + this._stackHeight = parseLocaleFloat(wrapper.readString()); this._extra = wrapper.readInt(); this._data = FurnitureDataParser.parseObjectData(wrapper); this._state = parseFloat(this._data && this._data.getLegacyString()) || 0; diff --git a/packages/communication/src/messages/parser/room/unit/RoomUnitParser.ts b/packages/communication/src/messages/parser/room/unit/RoomUnitParser.ts index 4736302..eb2f96d 100644 --- a/packages/communication/src/messages/parser/room/unit/RoomUnitParser.ts +++ b/packages/communication/src/messages/parser/room/unit/RoomUnitParser.ts @@ -1,6 +1,13 @@ import { IMessageDataWrapper, IMessageParser, RoomObjectType } from '@nitrots/api'; import { UserMessageData } from './UserMessageData'; +function parseLocaleFloat(value: string): number +{ + if(!value) return 0; + + return parseFloat(value.replace(',', '.')); +} + export class RoomUnitParser implements IMessageParser { private _users: UserMessageData[]; @@ -34,7 +41,7 @@ export class RoomUnitParser implements IMessageParser const roomIndex = wrapper.readInt(); const x = wrapper.readInt(); const y = wrapper.readInt(); - const z = parseFloat(wrapper.readString()); + const z = parseLocaleFloat(wrapper.readString()); const direction = wrapper.readInt(); const type = wrapper.readInt(); diff --git a/packages/communication/src/messages/parser/room/unit/RoomUnitStatusParser.ts b/packages/communication/src/messages/parser/room/unit/RoomUnitStatusParser.ts index b32dddd..79689c9 100644 --- a/packages/communication/src/messages/parser/room/unit/RoomUnitStatusParser.ts +++ b/packages/communication/src/messages/parser/room/unit/RoomUnitStatusParser.ts @@ -2,6 +2,13 @@ import { IMessageDataWrapper, IMessageParser } from '@nitrots/api'; import { RoomUnitStatusAction } from './RoomUnitStatusAction'; import { RoomUnitStatusMessage } from './RoomUnitStatusMessage'; +function parseLocaleFloat(value: string): number +{ + if(!value) return 0; + + return parseFloat(value.replace(',', '.')); +} + export class RoomUnitStatusParser implements IMessageParser { private _statuses: RoomUnitStatusMessage[]; @@ -45,7 +52,7 @@ export class RoomUnitStatusParser implements IMessageParser const unitId = wrapper.readInt(); const x = wrapper.readInt(); const y = wrapper.readInt(); - const z = parseFloat(wrapper.readString()); + const z = parseLocaleFloat(wrapper.readString()); const headDirection = ((wrapper.readInt() % 8) * 45); const direction = ((wrapper.readInt() % 8) * 45); const actions = wrapper.readString(); @@ -80,14 +87,14 @@ export class RoomUnitStatusParser implements IMessageParser { targetX = parseInt(values[0]); targetY = parseInt(values[1]); - targetZ = parseFloat(values[2]); + targetZ = parseLocaleFloat(values[2]); didMove = true; } break; } case 'sit': { - const sitHeight = parseFloat(parts[1]); + const sitHeight = parseLocaleFloat(parts[1]); if(parts.length >= 3) canStandUp = (parts[2] === '1'); @@ -96,9 +103,7 @@ export class RoomUnitStatusParser implements IMessageParser break; } case 'lay': { - const layHeight = parseFloat(parts[1]); - - height = Math.abs(layHeight); + height = parseLocaleFloat(parts[1]); break; } diff --git a/packages/room/src/object/visualization/avatar/AvatarVisualization.ts b/packages/room/src/object/visualization/avatar/AvatarVisualization.ts index 70ce9c8..59b2f6a 100644 --- a/packages/room/src/object/visualization/avatar/AvatarVisualization.ts +++ b/packages/room/src/object/visualization/avatar/AvatarVisualization.ts @@ -30,7 +30,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement private static BASE_Y_SCALE: number = 1000; private static AVATAR_SPRITE_DEFAULT_DEPTH: number = -0.01; private static AVATAR_OWN_DEPTH_ADJUST: number = 0.001; - private static AVATAR_SPRITE_LAYING_DEPTH: number = -0.409; + private static AVATAR_SPRITE_LAYING_DEPTH: number = 0.2; protected _data: AvatarVisualizationData; @@ -68,6 +68,9 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement private _isLaying: boolean; private _layInside: boolean; + private _layDepthOffset: number; + private _layXOffset: number; + private _layYOffset: number; private _isAnimating: boolean; private _extraSpritesStartIndex: number; private _forcedAnimFrames: number; @@ -123,6 +126,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement this._isLaying = false; this._layInside = false; + this._layDepthOffset = 0; this._isAnimating = false; this._extraSpritesStartIndex = AvatarVisualization.INITIAL_RESERVED_SPRITES; this._forcedAnimFrames = 0; @@ -320,12 +324,23 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement { sprite.offsetX = ((((-1 * scale) / 2) + _local_20[0]) - ((sprite.texture.width - scale) / 2)); sprite.offsetY = (((-(sprite.texture.height) + (scale / 4)) + _local_20[1]) + this._postureOffset); + + if(this._isLaying) + { + // Convert isometric ground-plane offsets to screen pixels + // isoX moves along one diagonal, isoY along the other + sprite.offsetX += (this._layXOffset - this._layYOffset); + sprite.offsetY += Math.floor((this._layXOffset + this._layYOffset) / 2); + } } if(this._isLaying) { - if(this._layInside) sprite.relativeDepth = -0.5; - else sprite.relativeDepth = (AvatarVisualization.AVATAR_SPRITE_LAYING_DEPTH + _local_20[2]); + // Compensate for avatar Z elevation so it sorts near the bed's depth + // layInside: avatar slightly behind bed (tucked in under blanket) + // !layInside: avatar slightly in front of bed (on top) + const laySign = this._layInside ? AvatarVisualization.AVATAR_SPRITE_LAYING_DEPTH : -AvatarVisualization.AVATAR_SPRITE_LAYING_DEPTH; + sprite.relativeDepth = (laySign - this._layDepthOffset + _local_20[2]); } else { @@ -347,8 +362,15 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement if(typingBubble) { - if(!this._isLaying) typingBubble.relativeDepth = ((AvatarVisualization.AVATAR_SPRITE_DEFAULT_DEPTH - 0.01) + _local_20[2]); - else typingBubble.relativeDepth = ((AvatarVisualization.AVATAR_SPRITE_LAYING_DEPTH - 0.01) + _local_20[2]); + if(!this._isLaying) + { + typingBubble.relativeDepth = ((AvatarVisualization.AVATAR_SPRITE_DEFAULT_DEPTH - 0.01) + _local_20[2]); + } + else + { + const laySign = this._layInside ? AvatarVisualization.AVATAR_SPRITE_LAYING_DEPTH : -AvatarVisualization.AVATAR_SPRITE_LAYING_DEPTH; + typingBubble.relativeDepth = ((laySign - this._layDepthOffset - 0.01) + _local_20[2]); + } } this._isAnimating = this._avatarImage.isAnimating(); @@ -449,7 +471,8 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement if(this._isLaying) { - sprite.relativeDepth = (AvatarVisualization.AVATAR_SPRITE_LAYING_DEPTH - ((0.001 * this.totalSprites) * offsetZ)); + const laySign = this._layInside ? AvatarVisualization.AVATAR_SPRITE_LAYING_DEPTH : -AvatarVisualization.AVATAR_SPRITE_LAYING_DEPTH; + sprite.relativeDepth = (laySign - this._layDepthOffset - ((0.001 * this.totalSprites) * offsetZ)); } else { @@ -918,7 +941,7 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement if((this._posture === 'sit') || (this._posture === 'lay')) { - this._postureOffset = (scale / 2); + this._postureOffset = ((AvatarVisualization.BASE_Y_SCALE + this._verticalOffset) * scale) / (4 * AvatarVisualization.BASE_Y_SCALE); } else { @@ -927,14 +950,30 @@ export class AvatarVisualization extends RoomObjectSpriteVisualization implement this._layInside = false; this._isLaying = false; + this._layDepthOffset = 0; + this._layXOffset = 0; + this._layYOffset = 0; if(this._posture === 'lay') { this._isLaying = true; - const _local_2 = parseInt(this._postureParameter); + // Format from server: "height;xOffset;yOffset" or just "height" + const parts = this._postureParameter ? this._postureParameter.split(';') : []; + const height = parts.length > 0 ? parseFloat(parts[0]) : 0; - if(_local_2 < 0) this._layInside = true; + if(height < 0) this._layInside = true; + + // Use the avatar's actual Z position for depth compensation + // This makes the renderer independent of what Z offset the emulator sends + const avatarZ = this.object ? this.object.getLocation().z : 0; + this._layDepthOffset = avatarZ * Math.sqrt(0.5); + + if(parts.length >= 3) + { + this._layXOffset = parseFloat(parts[1]) || 0; + this._layYOffset = parseFloat(parts[2]) || 0; + } } }