1- import { Player , StructureRotation , StructureSaveMode , system , world } from '@minecraft/server'
1+ import { EntityTypes , Player , StructureRotation , StructureSaveMode , system , world } from '@minecraft/server'
22import { MinecraftBlockTypes } from '@minecraft/vanilla-data'
33import {
44 ActionForm ,
@@ -13,14 +13,17 @@ import {
1313} from 'lib'
1414import { StructureDungeonsId , StructureFile , structureFiles } from 'lib/assets/structures'
1515import { i18n , noI18n } from 'lib/i18n/text'
16+ import { anyPlayerNear } from 'lib/player-move'
1617import { Area } from 'lib/region/areas/area'
1718import { SphereArea } from 'lib/region/areas/sphere'
1819import { Region , RegionCreationOptions , RegionPermissions } from 'lib/region/kinds/region'
20+ import { createLogger } from 'lib/utils/logger'
1921import { structureLikeRotate , structureLikeRotateRelative , toAbsolute , toRelative } from 'lib/utils/structure'
2022import { Dungeon } from './loot'
2123
2224export interface DungeonRegionDatabase extends JsonObject {
2325 chests : Record < string , number | null >
26+ spawnerCooldowns : Record < string , number >
2427 structureId : string
2528 rotation : StructureRotation
2629 terrainStructureId : string
@@ -39,9 +42,20 @@ export interface DungeonChest {
3942 location : Vector3
4043}
4144
45+ export interface DungeonSpawner {
46+ id : string
47+ restoreTime : number
48+ location : Vector3
49+ entities : { typeId : string ; amount : number } [ ]
50+ }
51+
4252export class DungeonRegion extends Region {
53+ static logger = createLogger ( 'DungeonRegion' )
54+
4355 static dungeons : DungeonRegion [ ] = [ ]
4456
57+ static oldChestLogPositions = new Set < string > ( )
58+
4559 static {
4660 system . runInterval (
4761 ( ) => {
@@ -53,19 +67,34 @@ export class DungeonRegion extends Region {
5367 }
5468 }
5569
70+ for ( const spawner of dungeon . spawners ) {
71+ const cooldown = dungeon . ldb . spawnerCooldowns [ spawner . id ]
72+ if ( ! cooldown || Cooldown . isExpired ( cooldown , spawner . restoreTime ) ) {
73+ dungeon . spawn ( spawner )
74+ }
75+ }
76+
77+ // Old chest
5678 for ( const chest of Object . keys ( dungeon . ldb . chests ) ) {
5779 const vector = Vec . parse ( chest )
5880 if ( ! vector ) continue
5981
6082 if ( dungeon . chests . find ( e => Vec . equals ( e . location , vector ) ) ) continue
6183
62- // Old chest
63- console . log ( 'Found old chest' , Vec . string ( vector , true ) )
64- try {
65- dungeon . dimension . setBlockType ( vector , MinecraftBlockTypes . Air )
66- Reflect . deleteProperty ( dungeon . ldb . chests , chest )
67- dungeon . save ( )
68- } catch ( e ) { }
84+ const oldChestPosition = Vec . string ( vector , true )
85+ if ( ! this . oldChestLogPositions . has ( oldChestPosition ) ) {
86+ this . logger . info ( 'Found old chest' , oldChestPosition , 'removing once there is any player nearby...' )
87+ this . oldChestLogPositions . add ( oldChestPosition )
88+ }
89+
90+ if ( anyPlayerNear ( vector , dungeon . dimensionType , 20 ) ) {
91+ try {
92+ this . logger . info ( 'Trying to remove old chest' , oldChestPosition )
93+ dungeon . dimension . setBlockType ( vector , MinecraftBlockTypes . Air )
94+ Reflect . deleteProperty ( dungeon . ldb . chests , chest )
95+ dungeon . save ( )
96+ } catch ( e ) { }
97+ }
6998 }
7099 }
71100 } ,
@@ -82,6 +111,7 @@ export class DungeonRegion extends Region {
82111
83112 ldb : DungeonRegionDatabase = {
84113 chests : { } ,
114+ spawnerCooldowns : { } ,
85115 structureId : '' ,
86116 terrainStructureId : '' ,
87117 terrainStructurePosition : { x : 0 , z : 0 , y : 0 } ,
@@ -96,14 +126,24 @@ export class DungeonRegion extends Region {
96126
97127 protected configureDungeon ( ) : void {
98128 if ( ! this . structureFile ) return
99- const { chestPositions, enderChestPositions } = this . structureFile
129+ const { chestPositions, enderChestPositions, shulkers } = this . structureFile
100130 const toRotated = ( f : Vector3 [ ] ) => this . rotate ( f . map ( e => this . fromRelativeToAbsolute ( e ) ) )
101131
102132 const loot = Dungeon . loot [ this . structureId ] ?? Dungeon . defaultLoot
103133 for ( const f of toRotated ( chestPositions ) ) this . createChest ( f , loot )
104134
105135 const powerfullLoot = Dungeon . powerfullLoot [ this . structureId ] ?? Dungeon . defaultLoot
106136 for ( const f of toRotated ( enderChestPositions ) ) this . createChest ( f , powerfullLoot )
137+
138+ for ( const { loc, inv } of shulkers ) {
139+ const [ rotated ] = this . rotate ( [ this . fromRelativeToAbsolute ( loc ) ] )
140+ if ( ! rotated ) {
141+ console . warn ( 'Not rotated' )
142+ continue
143+ }
144+
145+ this . createSpawnerFromShulkerInventory ( inv , rotated )
146+ }
107147 }
108148
109149 private rotate ( vectors : Vector3 [ ] ) {
@@ -144,7 +184,7 @@ export class DungeonRegion extends Region {
144184 }
145185
146186 customFormButtons ( form : ActionForm , player : Player ) : void {
147- form . button ( noI18n `Установить структуру` , ( ) => {
187+ form . button ( noI18n `Снова установить структуру` , ( ) => {
148188 this . placeStructure ( )
149189 } )
150190 }
@@ -223,7 +263,65 @@ export class DungeonRegion extends Region {
223263 allowedAllItem : true ,
224264 }
225265
226- chests : DungeonChest [ ] = [ ]
266+ private spawners : DungeonSpawner [ ] = [ ]
267+
268+ protected createSpawnerFromShulkerInventory (
269+ inv : { amount : number ; typeId : string } [ ] ,
270+ rotated : Vector3 ,
271+ restoreTime ?: number ,
272+ ) {
273+ const entities : DungeonSpawner [ 'entities' ] = [ ]
274+ for ( const value of inv ) {
275+ const entityTypeId = / ^ (?: m i n e c r a f t : ) ( .+ ) _ s p a w n _ e g g $ / . exec ( value . typeId ) ?. [ 1 ]
276+ if ( ! entityTypeId ) {
277+ console . warn ( 'No entity type id for' , value . typeId )
278+ continue
279+ }
280+
281+ entities . push ( { typeId : entityTypeId , amount : value . amount } )
282+ }
283+ this . createSpawner ( rotated , entities , restoreTime )
284+ }
285+
286+ protected createSpawner ( location : Vector3 , entities : DungeonSpawner [ 'entities' ] , restoreTime = ms . from ( 'min' , 20 ) ) {
287+ this . spawners . push ( {
288+ id : Vec . string ( location ) ,
289+ location,
290+ entities,
291+ restoreTime,
292+ } )
293+ }
294+
295+ private static invalidSpawnerTypeIds = new Set < string > ( )
296+
297+ private spawn ( spawner : DungeonSpawner ) {
298+ if ( ! anyPlayerNear ( spawner . location , this . dimensionType , 20 ) ) return
299+
300+ for ( const { typeId, amount } of spawner . entities ) {
301+ const type = EntityTypes . get < string > ( typeId )
302+ if ( ! type ) {
303+ if ( ! DungeonRegion . invalidSpawnerTypeIds . has ( typeId ) ) {
304+ console . warn ( 'Dungeon spawner invalid typeId' , typeId , spawner )
305+ DungeonRegion . invalidSpawnerTypeIds . add ( typeId )
306+ }
307+ continue
308+ }
309+
310+ const entities = this . dimension . getEntities ( { location : spawner . location , maxDistance : 20 } )
311+ if ( entities . length < amount ) {
312+ const toSpawn = amount - entities . length
313+ for ( let i = 0 ; i < toSpawn ; i ++ ) {
314+ this . dimension . spawnEntity ( type , spawner . location )
315+ }
316+ }
317+ }
318+ }
319+
320+ private chests : DungeonChest [ ] = [ ]
321+
322+ protected removeChest ( location : Vector3 ) {
323+ this . chests = this . chests . filter ( e => ! Vec . equals ( e . location , location ) )
324+ }
227325
228326 protected createChest ( location : Vector3 , loot : LootTable , restoreTime = ms . from ( 'min' , 20 ) ) {
229327 // console.log(
0 commit comments