diff --git a/packages/client/package.json b/packages/client/package.json index 354193f..5ae9fc7 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -11,12 +11,12 @@ }, "dependencies": { "@geckos.io/client": "^3.0.0", - "@react-three/fiber": "^8.8.9", - "@types/three": "^0.144.0", + "@react-three/fiber": "^8.16.8", + "@types/three": "^0.166.0", "react": "^18.2.0", "react-dom": "^18.2.0", "@bananu7-rts/server": "*", - "three": "^0.144.0" + "three": "^0.166.1" }, "devDependencies": { "@types/react": "^18.0.17", diff --git a/packages/client/src/components/CommandPalette.tsx b/packages/client/src/components/CommandPalette.tsx index 61abb1b..e65c9ec 100644 --- a/packages/client/src/components/CommandPalette.tsx +++ b/packages/client/src/components/CommandPalette.tsx @@ -116,8 +116,8 @@ export function CommandPalette(props: Props) { Produce {up.unitType} {up.unitType} - {cost}💰 {time}🕑 + {cost}💰

This excellent unit will serve you well, and I would tell you how but the tooltip data isn't diff --git a/packages/client/src/components/MatchController.tsx b/packages/client/src/components/MatchController.tsx index a41a971..a03307c 100644 --- a/packages/client/src/components/MatchController.tsx +++ b/packages/client/src/components/MatchController.tsx @@ -350,6 +350,7 @@ export function MatchController(props: MatchControllerProps) { board={matchMetadata.board} playerIndex={props.ctrl.getPlayerIndex()} units={lastUpdatePacket ? lastUpdatePacket.units : []} + projectiles={lastUpdatePacket ? lastUpdatePacket.projectiles : []} selectedUnits={selectedUnits} selectedCommand={selectedCommand} select={boardSelectUnits} diff --git a/packages/client/src/components/SpectateController.tsx b/packages/client/src/components/SpectateController.tsx index 854da8d..3325c57 100644 --- a/packages/client/src/components/SpectateController.tsx +++ b/packages/client/src/components/SpectateController.tsx @@ -191,6 +191,7 @@ export function SpectateController(props: SpectateControllerProps) { board={matchMetadata.board} playerIndex={0} // TODO - spectator has no player index units={lastUpdatePacket ? lastUpdatePacket.units : []} + projectiles={lastUpdatePacket ? lastUpdatePacket.projectiles : []} selectedUnits={selectedUnits} selectedCommand={undefined} // the board needs selected command to show e.g. build preview select={boardSelectUnits} diff --git a/packages/client/src/debug/ConeIndicator.tsx b/packages/client/src/debug/ConeIndicator.tsx new file mode 100644 index 0000000..1a4ca54 --- /dev/null +++ b/packages/client/src/debug/ConeIndicator.tsx @@ -0,0 +1,29 @@ +import { ThreeCache } from '../gfx/ThreeCache' +import * as THREE from 'three'; + +import { UnitAction } from '@bananu7-rts/server/src/types' + +const cache = new ThreeCache(); + +const coneGeometry = new THREE.ConeGeometry(0.5, 2, 8); +export function ConeIndicator(props: {action: UnitAction, smoothing: boolean}) { + let indicatorColor = 0xeeeeee; + if (props.action === 'Moving') + indicatorColor = 0x55ff55; + else if (props.action === 'Attacking') + indicatorColor = 0xff5555; + else if (props.action === 'Harvesting') + indicatorColor = 0x5555ff; + // indicate discrepancy between server and us + else if (props.smoothing) + indicatorColor = 0xffff55; + + return ( + + ); +} diff --git a/packages/client/src/gfx/Board3D.tsx b/packages/client/src/gfx/Board3D.tsx index b7be36f..0ecef9d 100644 --- a/packages/client/src/gfx/Board3D.tsx +++ b/packages/client/src/gfx/Board3D.tsx @@ -9,13 +9,16 @@ import { import * as THREE from 'three'; -import { Board, Unit, GameMap, UnitId, Position, TilePos, Building } from '@bananu7-rts/server/src/types' +import { Board, Unit, GameMap, UnitId, Position, TilePos, Building, Projectile, ProjectileTarget } from '@bananu7-rts/server/src/types' +import { getAttackerComponent } from '@bananu7-rts/server/src/game/components' +import { notEmpty } from '@bananu7-rts/server/src/tsutil' import { SelectionCircle } from './SelectionCircle' import { Line3D } from './Line3D' import { Map3D, Box } from './Map3D' import { Unit3D } from './Unit3D' import { Building3D } from './Building3D' import { BuildPreview } from './BuildPreview' +import { Projectile3D } from './Projectile3D' import { UNIT_DISPLAY_CATALOG, BuildingDisplayEntry } from './UnitDisplayCatalog' import { SelectedCommand } from '../game/SelectedCommand' @@ -25,6 +28,7 @@ export interface Props { board: Board; playerIndex: number; units: Unit[]; + projectiles: Projectile[], selectedUnits: Set; selectedCommand: SelectedCommand | undefined; @@ -114,8 +118,45 @@ export function Board3D(props: Props) { selectInBox={selectInBox} pointerMove={setPointer} /> + { units } { buildPreview } ); } + +function Projectiles(props: { projectiles: Projectile[], units: Unit[] }) { + const projectiles = props.projectiles.map(projectile => { + // TODO how to display projectiles trying to reach units that don't exist anymore? + + const target = getPositionFromProjectileTarget(projectile.target, props.units); + if (!target) { + return null; + } + + return ( + + ) + + }).filter(notEmpty); + + return ( + { projectiles } + ) +} + +function getPositionFromProjectileTarget(target: ProjectileTarget, units: Unit[]): Position | undefined { + if (target.type === "positionTarget") { + return target.position; + } else { + const targetUnit = units.find(u => u.id === target.unitId); + return targetUnit ? targetUnit.position : undefined; + } +} + \ No newline at end of file diff --git a/packages/client/src/gfx/Projectile3D.tsx b/packages/client/src/gfx/Projectile3D.tsx new file mode 100644 index 0000000..caa3657 --- /dev/null +++ b/packages/client/src/gfx/Projectile3D.tsx @@ -0,0 +1,62 @@ +import { useRef } from 'react' +import { useFrame } from '@react-three/fiber' +import * as THREE from 'three'; + +import { Position, Milliseconds } from '@bananu7-rts/server/src/types' +import { ThreeCache } from './ThreeCache' + +const cache = new ThreeCache(); + +export type ProjectileProps = { + origin: Position, + target: Position, + flightTime: Milliseconds, + flightTimeLeft: Milliseconds, +} + +export function Projectile3D(props: ProjectileProps) { + const projectilePosition = new THREE.Vector3(props.origin.x, 5, props.origin.y); + const projectileRef = useRef(null); + + const startPos = new THREE.Vector3(props.origin.x, 0, props.origin.y); + const targetPos = new THREE.Vector3(props.target.x, 0, props.target.y); + + const flightTimeLeft = useRef(props.flightTimeLeft); + + // if(time_since_fire * projectile_speed > distance(target, shot_location)) hit(target, projectile); + + useFrame((s, dt) => { + if(!projectileRef.current) + return; + + flightTimeLeft.current -= dt * 1000; + if (flightTimeLeft.current <= 0) { + projectileRef.current.visible = false; + return; + } + + const range = 20; + const e = 1 - (flightTimeLeft.current / props.flightTime); + const y = parabolaHeight(range, 10, e); + + projectileRef.current.position.lerpVectors(startPos, targetPos, e); + projectileRef.current.position.y = y; + }); + + return ( + + ); +} + +function parabolaHeight(length: number, height: number, epsilon: number) { + const k = length; + const h = height; + + const x = epsilon * k; + + return 4*h * (x/k - (x*x)/(k*k)); +} diff --git a/packages/client/src/gfx/ThreeCache.ts b/packages/client/src/gfx/ThreeCache.ts index 4db006c..da1d49c 100644 --- a/packages/client/src/gfx/ThreeCache.ts +++ b/packages/client/src/gfx/ThreeCache.ts @@ -29,6 +29,20 @@ export class ThreeCache { return geometry; } } + + spheres: Map = new Map(); + getSphereGeometry(radius: number) { + const cached = this.spheres.get(radius); + if (cached) { + return cached; + } else { + const widthSegments = 24; + const heightSegments = 8; + const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments); + this.spheres.set(radius, geometry); + return geometry; + } + } standardMaterials: Map = new Map(); getStandardMaterial(color: number) { diff --git a/packages/client/src/gfx/Unit3D.tsx b/packages/client/src/gfx/Unit3D.tsx index 6c7c4d3..4fae9d9 100644 --- a/packages/client/src/gfx/Unit3D.tsx +++ b/packages/client/src/gfx/Unit3D.tsx @@ -10,6 +10,7 @@ import { import * as THREE from 'three'; import { Board, Unit, GameMap, UnitId, Position, UnitAction } from '@bananu7-rts/server/src/types' + import { SelectionCircle } from './SelectionCircle' import { Line3D } from './Line3D' import { Map3D, Box } from './Map3D' @@ -17,6 +18,7 @@ import { ThreeCache } from './ThreeCache' import { FileModel } from './FileModel' import { UnitDisplayEntry } from './UnitDisplayCatalog' import { Horizon } from '../debug/Horizon' +import { ConeIndicator } from '../debug/ConeIndicator' import { debugFlags } from '../debug/flags' const cache = new ThreeCache(); @@ -28,30 +30,6 @@ const invisibleMaterial = new THREE.MeshBasicMaterial({ opacity:0, }); -const coneGeometry = new THREE.ConeGeometry(0.5, 2, 8); -function ConeIndicator(props: {action: UnitAction, smoothing: boolean}) { - // TODO - this will be replaced with animations etc - let indicatorColor = 0xeeeeee; - if (props.action === 'Moving') - indicatorColor = 0x55ff55; - else if (props.action === 'Attacking') - indicatorColor = 0xff5555; - else if (props.action === 'Harvesting') - indicatorColor = 0x5555ff; - // indicate discrepancy between server and us - else if (props.smoothing) - indicatorColor = 0xffff55; - - return ( - - ); -} - type Unit3DProps = { unit: Unit, displayEntry: UnitDisplayEntry, diff --git a/packages/client/src/gfx/UnitDisplayCatalog.ts b/packages/client/src/gfx/UnitDisplayCatalog.ts index cdc0ec4..b0d65c0 100644 --- a/packages/client/src/gfx/UnitDisplayCatalog.ts +++ b/packages/client/src/gfx/UnitDisplayCatalog.ts @@ -36,6 +36,11 @@ export const UNIT_DISPLAY_CATALOG : UnitDisplayCatalog = { selectorSize: 6, }), 'Trooper': () => ({ + isBuilding: false, + modelPath: 'peasant_1.glb', + selectorSize: 1, + }), + 'Catapult': () => ({ isBuilding: false, modelPath: 'catapult.glb', selectorSize: 2.5, diff --git a/packages/server/src/game.ts b/packages/server/src/game.ts index 97a35df..0eaa45a 100644 --- a/packages/server/src/game.ts +++ b/packages/server/src/game.ts @@ -21,16 +21,19 @@ import { buildPresenceAndBuildingMaps } from './game/presence.js' export function newGame(matchId: string, board: Board): Game { const units = createStartingUnits(2, board); + const startingResources = 1500; return { matchId, state: {id: 'Lobby'}, tickNumber: 0, // TODO factor number of players in creation // TODO handle disconnect separately from elimination - players: [{resources: 50, stillInGame: true}, {resources: 50, stillInGame: true}], + players: [{resources: startingResources, stillInGame: true}, {resources: startingResources, stillInGame: true}], board, units, + projectiles: [], lastUnitId: units.length, + lastProjectileId: 1, winCondition: 'BuildingElimination', } } @@ -208,6 +211,7 @@ export function tick(dt: Milliseconds, g: Game): UpdatePacket[] { } g.tickNumber += 1; + updateProjectiles(dt, g); updateUnits(dt, g); break; } @@ -235,6 +239,7 @@ export function tick(dt: Milliseconds, g: Game): UpdatePacket[] { units: unitUpdates, player: p, state: g.state, + projectiles: g.projectiles, } }); } @@ -275,6 +280,12 @@ function updateUnits(dt: Milliseconds, g: Game) { }); } +function updateProjectiles(dt: Milliseconds, g: Game) { + for (const projectile of g.projectiles) { + projectile.flightTimeLeft -= dt; + } +} + function eliminated(g: Game): PlayerIndex[] { const isBuilding = (u: Unit) => !!u.components.find(c => c.type === 'Building'); diff --git a/packages/server/src/game/unit/unit.ts b/packages/server/src/game/unit/unit.ts index 7a93b8c..ba9e9fc 100644 --- a/packages/server/src/game/unit/unit.ts +++ b/packages/server/src/game/unit/unit.ts @@ -1,6 +1,6 @@ import { Unit, UnitId, Milliseconds, PlayerState, GameWithPresenceCache, - Hp, Mover, Attacker, Harvester, ProductionFacility, Builder, Vision, Building, Component + Hp, Mover, Attacker, Harvester, ProductionFacility, Builder, Vision, Building, Component, Position, ProjectileTarget } from '../../types' import * as V from '../../vector.js' @@ -64,17 +64,46 @@ export const detectNearbyEnemy = (unit: Unit, units: Unit[]) => { return target; } -const attemptDamage = (ac: Attacker, target: Unit) => { - if (ac.cooldown === 0) { - // TODO - attack cooldown +const attemptDamage = (gm: GameWithPresenceCache, unit: Unit, ac: Attacker, target: Unit) => { + if (ac.cooldown !== 0) + return; + + ac.cooldown = ac.attackRate; + + // depending on the attacker type, either fire a projectile or deal direct damage + // TODO: windup + if (ac.kind === "projectile") { + const projectileTarget: ProjectileTarget = { + type: "positionTarget", + position: getUnitReferencePosition(target), + }; + + fireProjectile(gm, unit, ac, projectileTarget); + } else { const hp = getHpComponent(target); if (hp) { hp.hp -= ac.damage; } - ac.cooldown = ac.attackRate; } } +function fireProjectile(gm: GameWithPresenceCache, unit: Unit, ac: Attacker, target: ProjectileTarget) { + const projectileSpeed = 10; // units per s // TODO ac.projectileSpeed, but that'd require a separate RangedAttacker component + const distanceToTarget = target.type === "positionTarget" + ? V.distance(getUnitReferencePosition(unit), target.position) + : 0 // TODO target units// unitInteractionDistance(unit, ); + const flightTime: Milliseconds = (distanceToTarget / projectileSpeed) * 1000; + + gm.game.projectiles.push({ + id: ++gm.game.lastProjectileId, + damage: ac.damage, + target, + origin: {x: unit.position.x, y: unit.position.y }, + flightTime: flightTime, + flightTimeLeft: flightTime, + }) +} + export const aggro = (unit: Unit, gm: GameWithPresenceCache, ac: Attacker, target: Unit, dt: Milliseconds) => { // first let the movement system do its thing const movementTolerance = ac.range - ATTACK_RANGE_COMPENSATION; @@ -83,7 +112,8 @@ export const aggro = (unit: Unit, gm: GameWithPresenceCache, ac: Attacker, targe unit.state.action = 'Attacking'; const targetPos = getUnitReferencePosition(target); unit.direction = V.angleFromTo(unit.position, targetPos); - attemptDamage(ac, target); + + attemptDamage(gm, unit, ac, target); } // in any other case we can't do much else } diff --git a/packages/server/src/game/units.ts b/packages/server/src/game/units.ts index 9556e1b..226fb4c 100644 --- a/packages/server/src/game/units.ts +++ b/packages/server/src/game/units.ts @@ -13,7 +13,7 @@ const UNIT_CATALOG : Catalog = { 'Harvester': () => [ { type: 'Hp', maxHp: 50, hp: 50 }, { type: 'Mover', speed: 10 }, - { type: 'Attacker', damage: 5, attackRate: 1000, range: 2, cooldown: 0 }, + { type: 'Attacker', damage: 5, attackRate: 1000, range: 2, cooldown: 0, kind: 'direct' }, { type: 'Harvester', harvestingTime: 2000, harvestingValue: 8, harvestingProgress: 0 }, { type: 'Builder', buildingsProduced: [ { buildingType: 'Base', buildTime: 5000, buildCost: 400 }, @@ -37,7 +37,8 @@ const UNIT_CATALOG : Catalog = { { type: 'Hp', maxHp: 600, hp: 600 }, { type: 'Building', size: 6 }, { type: 'ProductionFacility', unitsProduced: [ - {unitType: 'Trooper', productionTime: 5000, productionCost: 50} + {unitType: 'Trooper', productionTime: 5000, productionCost: 50}, + {unitType: 'Catapult', productionTime: 15000, productionCost: 150} ]}, { type: 'Vision', range: 5 }, ], @@ -48,8 +49,14 @@ const UNIT_CATALOG : Catalog = { ], 'Trooper': () => [ { type: 'Hp', maxHp: 50, hp: 50 }, - { type: 'Mover', speed: 10 }, - { type: 'Attacker', damage: 10, attackRate: 500, range: 6, cooldown: 0 }, + { type: 'Mover', speed: 12 }, + { type: 'Attacker', damage: 8, attackRate: 600, range: 2, cooldown: 0, kind: 'direct' }, + { type: 'Vision', range: 10 }, + ], + 'Catapult': () => [ + { type: 'Hp', maxHp: 80, hp: 80 }, + { type: 'Mover', speed: 8 }, + { type: 'Attacker', damage: 10, attackRate: 2000, range: 10, cooldown: 0, kind: 'projectile' }, { type: 'Vision', range: 10 }, ] }; diff --git a/packages/server/src/types.ts b/packages/server/src/types.ts index 47cd36d..7cdc918 100644 --- a/packages/server/src/types.ts +++ b/packages/server/src/types.ts @@ -91,6 +91,7 @@ export type UpdatePacket = { tickNumber: number, units: Unit[], player: PlayerState, + projectiles: Projectile[], } // Components @@ -106,6 +107,7 @@ export type Attacker = { attackRate: Milliseconds, range: number, cooldown: Milliseconds, + kind: 'direct' | 'projectile', } export type Mover = { type: 'Mover', @@ -169,9 +171,9 @@ export type Vision = { // Internal Game stuff export type TilePos = { x: number, y: number } -export type PlayerIndex = number -export type UserId = string - +export type PlayerIndex = number; +export type UserId = string; +export type ProjectileId = number; export type UnitAction = 'Moving'|'Attacking'|'Harvesting'|'Idle'|'Producing'|'Building'; @@ -220,6 +222,24 @@ export type PlayerState = { export type WinCondition = 'BuildingElimination'|'OneLeft'; + +export type ProjectileTarget = { + type: "unitTarget", + unitId: UnitId, +} | { + type: "positionTarget", + position: Position, +} + +export type Projectile = { + id: ProjectileId, + damage: number, + target: ProjectileTarget, + origin: Position, + flightTime: Milliseconds, + flightTimeLeft: Milliseconds, +} + export type Game = { // uuid: UUID, TODO readonly matchId: MatchId, @@ -230,7 +250,9 @@ export type Game = { players: PlayerState[], tickNumber: number, units: Unit[], + projectiles: Projectile[], lastUnitId: number, + lastProjectileId: number, } export type GameMap = { diff --git a/packages/server/test/util.ts b/packages/server/test/util.ts index a34fc11..1f07472 100644 --- a/packages/server/test/util.ts +++ b/packages/server/test/util.ts @@ -33,7 +33,9 @@ export function createBasicGame(override: Partial, mapSize?: number): Game players: [createOnePlayerState(), createOnePlayerState()], board, units, + projectiles: [], lastUnitId: units.length, + lastProjectileId: 1, winCondition: 'OneLeft', }; diff --git a/yarn.lock b/yarn.lock index a893a1c..bdbdb4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1332,18 +1332,21 @@ dependencies: "@octokit/openapi-types" "^12.11.0" -"@react-three/fiber@^8.8.9": - version "8.12.0" - resolved "https://registry.yarnpkg.com/@react-three/fiber/-/fiber-8.12.0.tgz#80241992d780d436dfba59d90d9b61e52371fa3e" - integrity sha512-o6DkNtNHqcOFRbxaiY5xayelE/9+Z0z9wWu5awqjVbc0c/1QM6AttJH6rKW0U/O6LWxSfxDTARXVzknZqpDJ7A== +"@react-three/fiber@^8.16.8": + version "8.16.8" + resolved "https://registry.yarnpkg.com/@react-three/fiber/-/fiber-8.16.8.tgz#4d2fecda7b38f534de6bdac49ca37c815cf9a4ef" + integrity sha512-Lc8fjATtvQEfSd8d5iKdbpHtRm/aPMeFj7jQvp6TNHfpo8IQTW3wwcE1ZMrGGoUH+w2mnyS+0MK1NLPLnuzGkQ== dependencies: "@babel/runtime" "^7.17.8" "@types/react-reconciler" "^0.26.7" + "@types/webxr" "*" + base64-js "^1.5.1" + buffer "^6.0.3" its-fine "^1.0.6" react-reconciler "^0.27.0" react-use-measure "^2.1.1" scheduler "^0.21.0" - suspend-react "^0.0.8" + suspend-react "^0.1.3" zustand "^3.7.1" "@sinclair/typebox@^0.27.8": @@ -1376,6 +1379,11 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== +"@tweenjs/tween.js@~23.1.2": + version "23.1.2" + resolved "https://registry.yarnpkg.com/@tweenjs/tween.js/-/tween.js-23.1.2.tgz#4e5357fd6742f5aa50447d3fa808aed4cda93ed7" + integrity sha512-kMCNaZCJugWI86xiEHaY338CU5JpD0B97p1j1IKNn/Zto8PgACjQx0UxbHjmOcLl/dDOBnItwD07KmCs75pxtQ== + "@types/body-parser@*": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" @@ -1524,12 +1532,21 @@ "@types/mime" "*" "@types/node" "*" -"@types/three@^0.144.0": - version "0.144.0" - resolved "https://registry.yarnpkg.com/@types/three/-/three-0.144.0.tgz#a154f40122dbc3668c5424a5373f3965c6564557" - integrity sha512-psvEs6q5rLN50jUYZ3D4pZMfxTbdt3A243blt0my7/NcL6chaCZpHe2csbCtx0SOD9fI/XnF3wnVUAYZGqCSYg== +"@types/stats.js@*": + version "0.17.3" + resolved "https://registry.yarnpkg.com/@types/stats.js/-/stats.js-0.17.3.tgz#705446e12ce0fad618557dd88236f51148b7a935" + integrity sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ== + +"@types/three@^0.166.0": + version "0.166.0" + resolved "https://registry.yarnpkg.com/@types/three/-/three-0.166.0.tgz#a220b9ffecb7b650e2fdc6ef982285e651ec5dbe" + integrity sha512-FHMnpcdhdbdOOIYbfkTkUVpYMW53odxbTRwd0/xJpYnTzEsjnVnondGAvHZb4z06UW0vo6WPVuvH0/9qrxKx7g== dependencies: + "@tweenjs/tween.js" "~23.1.2" + "@types/stats.js" "*" "@types/webxr" "*" + fflate "~0.8.2" + meshoptimizer "~0.18.1" "@types/webxr@*": version "0.5.1" @@ -1832,7 +1849,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1: +base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -1932,6 +1949,14 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -2961,6 +2986,11 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fflate@~0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -3441,7 +3471,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -4135,6 +4165,11 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +meshoptimizer@~0.18.1: + version "0.18.1" + resolved "https://registry.yarnpkg.com/meshoptimizer/-/meshoptimizer-0.18.1.tgz#cdb90907f30a7b5b1190facd3b7ee6b7087797d8" + integrity sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -5890,10 +5925,10 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -suspend-react@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/suspend-react/-/suspend-react-0.0.8.tgz#b0740c1386b4eb652f17affe4339915ee268bd31" - integrity sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg== +suspend-react@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/suspend-react/-/suspend-react-0.1.3.tgz#a52f49d21cfae9a2fb70bd0c68413d3f9d90768e" + integrity sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ== tar-fs@^2.0.0: version "2.1.1" @@ -5962,10 +5997,10 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -three@^0.144.0: - version "0.144.0" - resolved "https://registry.yarnpkg.com/three/-/three-0.144.0.tgz#2818517169f8ff94eea5f664f6ff1fcdcd436cc8" - integrity sha512-R8AXPuqfjfRJKkYoTQcTK7A6i3AdO9++2n8ubya/GTU+fEHhYKu1ZooRSCPkx69jbnzT7dD/xEo6eROQTt2lJw== +three@^0.166.1: + version "0.166.1" + resolved "https://registry.yarnpkg.com/three/-/three-0.166.1.tgz#322cfc48fff4e751cd47d61fd1558c387d098d7c" + integrity sha512-LtuafkKHHzm61AQA1be2MAYIw1IjmhOUxhBa0prrLpEMWbV7ijvxCRHjSgHPGp2493wLBzwKV46tA9nivLEgKg== through2@^2.0.0: version "2.0.5"