@@ -30,6 +30,7 @@ import (
3030 "github.com/siohaza/fosilo/pkg/config"
3131 "github.com/siohaza/fosilo/pkg/lua"
3232 "github.com/siohaza/fosilo/pkg/vxl"
33+ "github.com/siohaza/fosilo/pkg/vxlgen"
3334
3435 "github.com/codecat/go-enet"
3536)
@@ -147,6 +148,7 @@ func (s *Server) Start() error {
147148 return fmt .Errorf ("failed to load Lua gamemode: %w" , err )
148149 }
149150 s .gameMode = luaMode
151+ s .callbacks .Register (luaMode )
150152 s .logger .Info ("loaded Lua game mode" , "path" , luaGamemodePath , "mode" , s .gameMode .Name ())
151153
152154 if s .luaCommands != nil {
@@ -395,6 +397,47 @@ func (s *Server) prepareMapResources(mapSpec string) (*vxl.Map, *config.MapConfi
395397 return vxlMap , mapCfg , display , "classicgen" , nil
396398 }
397399
400+ if strings .EqualFold (base , "vxlgen" ) {
401+ seed , display := s .resolveVxlgenSeed (param )
402+ result := vxlgen .Generate (vxlgen.Config {Seed : seed })
403+ vxlMap := result .Map
404+
405+ mapCfg := config .DefaultMapConfig ()
406+ mapCfg .Map .Author = "VxlGen"
407+ mapCfg .Map .Description = fmt .Sprintf ("Procedurally generated labyrinth (seed %d)" , seed )
408+
409+ blueZ := 63 - result .BlueSpawnArea .Max .Z
410+ greenZ := 63 - result .GreenSpawnArea .Max .Z
411+
412+ mapCfg .Tents .Team1 = config.TentArea {
413+ Start : [3 ]int {result .BlueSpawnArea .Min .X , result .BlueSpawnArea .Min .Y , max (0 , blueZ - 5 )},
414+ End : [3 ]int {result .BlueSpawnArea .Max .X , result .BlueSpawnArea .Max .Y , min (63 , blueZ + 10 )},
415+ }
416+ mapCfg .Tents .Team2 = config.TentArea {
417+ Start : [3 ]int {result .GreenSpawnArea .Min .X , result .GreenSpawnArea .Min .Y , max (0 , greenZ - 5 )},
418+ End : [3 ]int {result .GreenSpawnArea .Max .X , result .GreenSpawnArea .Max .Y , min (63 , greenZ + 10 )},
419+ }
420+
421+ mapCfg .SpawnPoints .Team1 = config.SpawnArea {
422+ Start : [3 ]int {result .BlueSpawnArea .Min .X , result .BlueSpawnArea .Min .Y , max (0 , blueZ - 5 )},
423+ End : [3 ]int {result .BlueSpawnArea .Max .X , result .BlueSpawnArea .Max .Y , min (63 , blueZ + 10 )},
424+ }
425+ mapCfg .SpawnPoints .Team2 = config.SpawnArea {
426+ Start : [3 ]int {result .GreenSpawnArea .Min .X , result .GreenSpawnArea .Min .Y , max (0 , greenZ - 5 )},
427+ End : [3 ]int {result .GreenSpawnArea .Max .X , result .GreenSpawnArea .Max .Y , min (63 , greenZ + 10 )},
428+ }
429+
430+ setVxlgenIntelPositions (vxlMap , mapCfg , result )
431+
432+ s .config .Server .TowerPosX = result .TowerPos .X
433+ s .config .Server .TowerPosY = result .TowerPos .Y
434+ s .config .Server .TowerCellsX = result .NumCells .X
435+ s .config .Server .TowerCellsY = result .NumCells .Y
436+ s .config .Server .TowerCellsZ = result .NumCells .Z
437+
438+ return vxlMap , mapCfg , display , "vxlgen" , nil
439+ }
440+
398441 mapPath := fmt .Sprintf ("maps/%s.vxl" , mapSpec )
399442
400443 mapData , err := os .ReadFile (mapPath )
@@ -449,8 +492,9 @@ func setClassicgenIntelPositions(vxlMap *vxl.Map, mapCfg *config.MapConfig) {
449492
450493 placeTent := func (area config.TentArea ) tentPlacement {
451494 x , y , top , slope := findTentPlacement (vxlMap , area )
452- flattenTentArea (vxlMap , x , y , top )
495+ flattenTentArea (vxlMap , x , y , top + 1 )
453496
497+ top = vxlMap .FindTopBlock (x , y )
454498 baseHeight := clampInt (top , 1 , vxlMap .Depth ()- 2 )
455499 heightOffset := 1 + min (slope , 2 )
456500
@@ -610,7 +654,8 @@ func flattenTentArea(vxlMap *vxl.Map, centerX, centerY, centerZ int) {
610654 depth := vxlMap .Depth ()
611655
612656 targetHeight := clampInt (centerZ , 1 , depth - 2 )
613- radius := 4
657+ radius := 6
658+ innerRadius := 3
614659
615660 for dx := - radius ; dx <= radius ; dx ++ {
616661 for dy := - radius ; dy <= radius ; dy ++ {
@@ -621,27 +666,32 @@ func flattenTentArea(vxlMap *vxl.Map, centerX, centerY, centerZ int) {
621666 cx := clampInt (centerX + dx , 0 , width - 1 )
622667 cy := clampInt (centerY + dy , 0 , height - 1 )
623668
669+ currentTop := vxlMap .FindTopBlock (cx , cy )
624670 distance := abs (dx ) + abs (dy )
671+
625672 desiredHeight := targetHeight
626- if distance >= radius - 1 {
627- desiredHeight = max (targetHeight - 1 , 1 )
673+ if distance > innerRadius {
674+ steps := distance - innerRadius
675+ if currentTop > targetHeight {
676+ desiredHeight = min (targetHeight + steps , currentTop )
677+ } else if currentTop < targetHeight {
678+ desiredHeight = max (targetHeight - steps , currentTop )
679+ }
628680 }
629681 desiredHeight = clampInt (desiredHeight , 1 , depth - 2 )
630682
631- currentTop := vxlMap .FindTopBlock (cx , cy )
632- if currentTop > desiredHeight {
633- for z := desiredHeight + 1 ; z <= currentTop ; z ++ {
683+ if currentTop < desiredHeight {
684+ for z := currentTop ; z < desiredHeight ; z ++ {
634685 vxlMap .SetAir (cx , cy , z )
635686 }
636- } else if currentTop < desiredHeight {
687+ } else if currentTop > desiredHeight {
637688 color := fallbackTerrainColor
638689 if currentTop >= 0 {
639690 if c := vxlMap .Get (cx , cy , currentTop ); c != 0 {
640691 color = c
641692 }
642693 }
643- start := max (currentTop + 1 , 0 )
644- for z := start ; z <= desiredHeight ; z ++ {
694+ for z := desiredHeight ; z < currentTop ; z ++ {
645695 vxlMap .Set (cx , cy , z , color )
646696 }
647697 }
@@ -674,6 +724,52 @@ func (s *Server) resolveClassicgenSeed(seedHint string) (uint32, string) {
674724 return seed , displayName
675725}
676726
727+ func (s * Server ) resolveVxlgenSeed (seedHint string ) (uint64 , string ) {
728+ var seed uint64
729+ if seedHint != "" {
730+ if v , err := strconv .ParseUint (seedHint , 0 , 64 ); err == nil {
731+ seed = v
732+ } else {
733+ seed = uint64 (crc32 .ChecksumIEEE ([]byte (seedHint )))
734+ }
735+ }
736+ if seed == 0 {
737+ seed = uint64 (time .Now ().UnixNano ())
738+ }
739+ displayName := fmt .Sprintf ("vxlgen (seed: %d)" , seed )
740+ return seed , displayName
741+ }
742+
743+ func setVxlgenIntelPositions (vxlMap * vxl.Map , mapCfg * config.MapConfig , result * vxlgen.Result ) {
744+ if vxlMap == nil || mapCfg == nil {
745+ return
746+ }
747+
748+ placeIntel := func (area vxlgen.Box3i ) tentPlacement {
749+ cx := (area .Min .X + area .Max .X ) / 2
750+ cy := (area .Min .Y + area .Max .Y ) / 2
751+ gz := 63 - (area .Min .Z + area .Max .Z )/ 2
752+ return tentPlacement {
753+ intel : [3 ]float64 {float64 (cx ) + 0.5 , float64 (cy ) + 0.5 , float64 (gz ) + 2.0 },
754+ base : [3 ]float64 {float64 (cx ) + 0.5 , float64 (cy ) + 0.5 , float64 (gz )},
755+ x : cx ,
756+ y : cy ,
757+ z : gz ,
758+ }
759+ }
760+
761+ team1 := placeIntel (result .BlueSpawnArea )
762+ team2 := placeIntel (result .GreenSpawnArea )
763+
764+ mapCfg .Intel .Team1Position = team1 .intel
765+ mapCfg .Intel .Team1Base = team1 .base
766+ mapCfg .Intel .Team2Position = team2 .intel
767+ mapCfg .Intel .Team2Base = team2 .base
768+
769+ mapCfg .SpawnPoints .Team1Points = team1 .spawnPoints (vxlMap )
770+ mapCfg .SpawnPoints .Team2Points = team2 .spawnPoints (vxlMap )
771+ }
772+
677773func (s * Server ) run () {
678774 ticker := time .NewTicker (s .tickRate )
679775 defer ticker .Stop ()
@@ -2162,18 +2258,9 @@ func (s *Server) respawnPlayer(playerID uint8) {
21622258 s .gameMode .OnPlayerSpawn (p )
21632259 s .callbacks .OnPlayerSpawn (p )
21642260
2165- p .RLock ()
2166- packet := protocol.PacketCreatePlayer {
2167- PacketID : uint8 (protocol .PacketTypeCreatePlayer ),
2168- PlayerID : p .ID ,
2169- Weapon : p .Weapon ,
2170- Team : toNetworkTeamID (p .Team ),
2171- X : spawnPos .X ,
2172- Y : spawnPos .Y ,
2173- Z : spawnPos .Z ,
2174- }
2175- copy (packet .Name [:], p .Name )
2261+ s .BroadcastCreatePlayer (p )
21762262
2263+ p .RLock ()
21772264 reloadPacket := protocol.PacketWeaponReload {
21782265 PacketID : uint8 (protocol .PacketTypeWeaponReload ),
21792266 PlayerID : p .ID ,
@@ -2182,12 +2269,29 @@ func (s *Server) respawnPlayer(playerID uint8) {
21822269 }
21832270 p .RUnlock ()
21842271
2185- s .broadcastPacket (& packet , true )
21862272 s .sendPacket (p , & reloadPacket , true )
21872273 s .broadcastShortPlayerData (p )
21882274 s .sendPlayerProperties (p )
21892275}
21902276
2277+ func (s * Server ) BroadcastCreatePlayer (p * player.Player ) {
2278+ p .RLock ()
2279+ pos := p .Position
2280+ packet := protocol.PacketCreatePlayer {
2281+ PacketID : uint8 (protocol .PacketTypeCreatePlayer ),
2282+ PlayerID : p .ID ,
2283+ Weapon : p .Weapon ,
2284+ Team : toNetworkTeamID (p .Team ),
2285+ X : pos .X ,
2286+ Y : pos .Y ,
2287+ Z : pos .Z ,
2288+ }
2289+ copy (packet .Name [:], p .Name )
2290+ p .RUnlock ()
2291+
2292+ s .broadcastPacket (& packet , true )
2293+ }
2294+
21912295func (s * Server ) damagePlayer (playerID uint8 , damage uint8 , source protocol.Vector3f , damageType protocol.HurtType ) bool {
21922296 p , ok := s .gameState .Players .Get (playerID )
21932297 if ! ok {
@@ -2537,6 +2641,10 @@ func (s *Server) explodeGrenade(grenade *gamestate.Grenade) {
25372641 }
25382642 s .broadcastPacket (& blockPacket , true )
25392643 }
2644+
2645+ if thrower , ok := s .gameState .Players .Get (grenade .PlayerID ); ok {
2646+ s .callbacks .OnGrenadeExplode (thrower , grenade .Position .X , grenade .Position .Y , grenade .Position .Z )
2647+ }
25402648}
25412649
25422650func marshalPacket (packet interface {}) ([]byte , error ) {
0 commit comments