Page 1 of 1

Jenga's Nest - The infernal tower

Posted: Mon Apr 25, 2011 3:15 pm
by djes
A little 3D game based on Jenga and Stork's nest principles.
For two players. You put a tile with the mouse, three per level. The goal is to make the highest tower without making it fall. Use mouse wheel to rotate the tile. Camera control with cursor keys + PageUp/PageDown.

Have fun ! :)

Code: Select all

;*********************************
; Jenga's Nest
; by djes (djes@free.fr)
; 04/25/2011 : First release
; 03/05/2012 : Code update
; Thanks to new PB 4.6 3D commands
; Feel free to enhance !

Enumeration 
  #GROUND
  #VIRTUALPIECE
  #TILEPL1MAT
  #TILEPL2MAT
EndEnumeration
;Need to be at the last position
Enumeration #PB_Compiler_EnumerationValue
  #TILE
EndEnumeration

; Window size
#SCREENWIDTH = 600
#SCREENHEIGHT = 600

Structure Piece
  EntityNB.i
  x.d
  y.d
  z.d
  Yaw.d
EndStructure

Global NewList Pieces.Piece()
Global InGame = #True

Procedure CreatePiece(u, mat, x.d, y.d, z.d, Yaw.d)
  CreateEntity(#TILE + u, MeshID(#TILE), MaterialID(mat))
  ScaleEntity(#TILE + u, 9, 1.485, 3)
  EntityLocate(#TILE + u, x, y, z)
  RotateEntity(#TILE + u, 0, Yaw, 0)
  EntityPhysicBody(#TILE + u, #PB_Entity_BoxBody, 250, 0, 2)
  AddElement(Pieces())
  Pieces()\EntityNb = #TILE + u
  ;   Pieces()\x = x
  ;   Pieces()\y = y
  ;   Pieces()\z = z
  ;   Pieces()\Yaw = Yaw
EndProcedure

Procedure TheEnd(msg.s)
  EntityLocate(#VIRTUALPIECE, 200, 200, 0)
  EntityPhysicBody(#VIRTUALPIECE, #PB_Entity_None, 250, 0, 2)   
  ReleaseMouse(#True)
  MessageRequester("Infos", Msg)
  InGame = #False  
EndProcedure

;- Initialization
InitEngine3D()
InitSprite()
InitKeyboard()
InitMouse()

;- Window
OpenWindow(0, 0, 0, #SCREENWIDTH, #SCREENHEIGHT, "Jenga's Nest by djes (http://djes.free.fr)", #PB_Window_ScreenCentered|#PB_Window_SystemMenu|#PB_Window_MinimizeGadget)
OpenWindowedScreen(WindowID(0), 0, 0, #SCREENWIDTH,#SCREENHEIGHT, 0, 0, 0,#PB_Screen_SmartSynchronization)

EnableWorldPhysics(#True)
EnableWorldCollisions(#True)

;-Textures
CreateTexture(0, 128, 128) 
StartDrawing(TextureOutput(0)) 
Box(0, 0, 128, 128, $FFFFFF)
StopDrawing()

;-Material
CreateMaterial(#GROUND, TextureID(0))
MaterialAmbientColor(#GROUND, RGB($FF, $FF, $FF))
CreateMaterial(#TILEPL1MAT, TextureID(0))
MaterialAmbientColor(#TILEPL1MAT, RGB(0, $FF, $55))
CreateMaterial(#TILEPL2MAT, TextureID(0))
MaterialAmbientColor(#TILEPL2MAT, RGB($FF, $55, 0))
CreateMaterial(#VIRTUALPIECE, TextureID(0))
MaterialAmbientColor(#VIRTUALPIECE, RGB(0, 0, $FF))
MaterialBlendingMode(#VIRTUALPIECE, #PB_Material_Add)
CreateMaterial(#GROUND, TextureID(0))
MaterialAmbientColor(#GROUND, RGB($FF, $FF, $FF))

;- Meshes
CreateCube(#GROUND, 1)
CreateCube(#TILE, 1)

;- Entities
CreateEntity(#GROUND, MeshID(#GROUND), MaterialID(#GROUND))
ScaleEntity(#GROUND, 20, 1, 20)
EntityLocate(#GROUND, 0, -0.5, 0)
EntityPhysicBody(#GROUND, #PB_Entity_StaticBody, 250, 0, 2)   

CreateEntity(#VIRTUALPIECE, MeshID(#TILE), MaterialID(#VIRTUALPIECE))
ScaleEntity(#VIRTUALPIECE, 9, 1.485, 3)
EntityPhysicBody(#VIRTUALPIECE, #PB_Entity_BoxBody, 250, 0, 2)   

;- Camera
CreateCamera(0, 0, 0, 100, 100)
CameraLocate(0, -30, 1.485 * 20, -30)
CameraLookAt(0, 0, 1.485 * 5, 0)

;-Light 
AmbientColor(RGB(155, 105, 105)) 
CreateLight(0, RGB(155, 155, 105), -200, 300, 0) 
;CreateLight(1,RGB(255, 127, 0), -200, -200, 200)
;WorldShadows(#PB_Shadow_Additive)

XMouse.d = 0
ZMouse.d = 0
YawMouse.d = 0

XCam = 160 * Sin(PosCam)
ZCam = 160 * Sin(PosCam)

PieceNB = 0
LevelCount = 0
Level.d = 0
CurrentY.d = Level * 1.485 + 0.7425 ; Level * 1.485 + 0.7425
UserDrop.d = 0
AvgMove.d = 0

PlayerTurn = 0
CreateSprite(0, 256, 48, #PB_Sprite_Texture|#PB_Sprite_AlphaBlending)

;- Main loop
Repeat
  
  Event = WindowEvent() 
  
  ExamineKeyboard()
  ExamineMouse()
  
  ;- Camera Move
  If KeyboardPushed(#PB_Key_Up)       : MoveCamera(0,  0,    0,   -0.1) : EndIf 
  If KeyboardPushed(#PB_Key_Down)     : MoveCamera(0,  0,    0,    0.1) : EndIf 
  If KeyboardPushed(#PB_Key_Left)     : MoveCamera(0, -0.1,  0,    0)   : EndIf 
  If KeyboardPushed(#PB_Key_Right)    : MoveCamera(0,  0.1,  0,    0)   : EndIf 
  If KeyboardPushed(#PB_Key_PageUp)   : MoveCamera(0,  0,   -0.1,  0)   : EndIf 
  If KeyboardPushed(#PB_Key_PageDown) : MoveCamera(0,  0,    0.1,  0)   : EndIf 
  
  CameraLookAt(0, 0, CurrentY, 0)
  
  If InGame
    
    XMouse - MouseDeltaX() / 50
    ZMouse - MouseDeltaY() / 50 
    YawMouse + MouseWheel() * 10 
    
    If XMouse < -20 : XMouse = -20 : EndIf
    If XMouse >  20 : XMouse =  20 : EndIf
    If ZMouse < -20 : ZMouse = -20 : EndIf
    If ZMouse >  20 : ZMouse =  20 : EndIf
    
    If MouseButton(#PB_MouseButton_Right ) : UserDrop = 1 : Else : UserDrop = 0 : EndIf
    
    EntityLocate(#VIRTUALPIECE, XMouse, CurrentY + UserDrop, ZMouse)
    RotateEntity(#VIRTUALPIECE, 0, YawMouse, 0)
    
    ;- Tile Drop
    If MouseButton(#PB_MouseButton_Left )
      
      EntityLocate(#VIRTUALPIECE, 200, 200, 0)
      
      CreatePiece(PieceNB, #TILEPL1MAT +  PlayerTurn, XMouse, CurrentY + UserDrop, ZMouse, YawMouse)
      
      XMouse = 200
      ZMouse = 200
      
      ;User is dropping the tile
      If UserDrop <> 0
        Delay = 120
        Repeat
          RenderWorld()
          FlipBuffers()
          Delay - 1
        Until (Abs(EntityY(#TILE + PieceNB) - CurrentY) < 0.05) Or (Delay <= 0)
        ;Pieces()\y = EntityY(#TILE + PieceNB) 
      EndIf
      
      ;Next level
      LevelCount + 1     
      If LevelCount > 2
        LevelCount = 0
        
        ;Little camera slide
        i.f = 0
        Repeat
          i + 0.05
          RenderWorld()
          FlipBuffers()
          CameraLookAt(0, 0, Level * 1.485 + 0.7425 + i, 0)
        Until i >= 1.485
        
        ;Average position for the next level
        CurrentY = (EntityY(#TILE + PieceNB) + EntityY(#TILE + PieceNB - 1) + EntityY(#TILE + PieceNB - 2)) / 3 + 1.515  ;Level * 1.485 + 0.7425 ;       
        
        Level + 1
      EndIf
      
      PieceNB + 1  
      PlayerTurn = 1 - PlayerTurn
      
      Repeat
        ExamineMouse()
      Until MouseButton(#PB_MouseButton_Left) = #False
      
    EndIf
    
    ;-Fall Check

    ForEach Pieces() 
      ;Check if tile has felt from the socle
      If (EntityY(Pieces()\EntityNB) < 0)
        TheEnd("Player " + Str(PlayerTurn + 1) + " has won. Level : " + Str(Level))
        Break ;To exit the loop and let the move continue (better feeling)
      EndIf
    Next
    
    ExamineWorldCollisions()
    
    If level > 0
      ;Check if tiles>3 are touching the ground
      For u = 3  To PieceNB - 1
        If EntityCollide(#GROUND, #TILE + u)
          TheEnd("Player " + Str(PlayerTurn + 1) + " has won. Level : " + Str(Level))
          Break
        EndIf        
      Next
    EndIf
    
    ;---
    
    StartDrawing(SpriteOutput(0))
    DrawText(0,0, "Level " + Str(Level))
    DrawText(0,16, "Player " + Str(PlayerTurn + 1) + " turn")
    StopDrawing()      
    
  Else
    
    StartDrawing(SpriteOutput(0))
    DrawText(0,16, "Player " + Str(PlayerTurn + 1) + " has won")
    StopDrawing()      
    
  EndIf
  
  DisplaySprite(0, 0, 0)
  
  ; Render 
  FlipBuffers()
  RenderWorld()
  
Until KeyboardPushed(#PB_Key_Escape) Or Event = #PB_Event_CloseWindow

End

Re: Jenga's Nest - The infernal tower

Posted: Tue Apr 26, 2011 1:33 am
by Demivec
Is it intentional that the placement of blocks on higher levels doesn't match the level? This causes collisions that eventually cause unavoidable 'tower failure'. :)
Image

Re: Jenga's Nest - The infernal tower

Posted: Tue Apr 26, 2011 8:11 am
by djes
Happy that you saw that, I was believing no one tested! It seems that this "bug" is due to the 3d engine, and because I compute the next Y position in a fixed maneer. As the tiles are stacked and handled dynamically, if the tower start to lean, the sum of 'rotated' positions is higher. It seems also that the engine let a little space between the objects, they are not perfectly joint, as in the real life in fact!

If G-Rom is there I wonder how I could handle that. If I wanted for example to put the virtual next tile on the top, mading it fall until it collides... :?: :mrgreen:

Re: Jenga's Nest - The infernal tower

Posted: Tue Apr 26, 2011 8:26 am
by Demivec
djes wrote:If G-Rom is there I wonder how I could handle that. If I wanted for example to put the virtual next tile on the top, mading it fall until it collides... :?: :mrgreen:
I think dropping it from a small height would be a similar problem to put it too low. Instead of calculating the new position based on a constant can you instead obtain the location of the top of a block on the previously completed level?

Re: Jenga's Nest - The infernal tower

Posted: Tue Apr 26, 2011 8:50 am
by DarkDragon
Its weird.. here the debugger says:
Die angegebene #Texture wurde nicht initialisiert.
and if I let it run without debugger it will just show brown bricks and I don't know what to do there. It moves them in a weird way.

Re: Jenga's Nest - The infernal tower

Posted: Tue Apr 26, 2011 9:30 am
by djes
Demivec wrote:
djes wrote:If G-Rom is there I wonder how I could handle that. If I wanted for example to put the virtual next tile on the top, mading it fall until it collides... :?: :mrgreen:
I think dropping it from a small height would be a similar problem to put it too low. Instead of calculating the new position based on a constant can you instead obtain the location of the top of a block on the previously completed level?
It would be hard even if I have the position of the tiles and their size, as it would consist of simulating the physic collisions with several tiles as the engine is already doing :/

DarkDragon> Don't you have a driver problem? The game is made for PB 4.60, don't you have an old engine.dll somewhere (windows/system)?

Re: Jenga's Nest - The infernal tower

Posted: Tue Apr 26, 2011 1:56 pm
by DarkDragon
djes wrote:DarkDragon> Don't you have a driver problem? The game is made for PB 4.60, don't you have an old engine.dll somewhere (windows/system)?
No. Please save the data inside the temporary directory instead of the current directory, so windows 7 won't block it:

Code: Select all

Add3DArchive(GetTemporaryDirectory(),#PB_3DArchive_FileSystem)
CreateImage(0, 128, 128) 
StartDrawing(ImageOutput(0)) 
  Box(0, 0, 128, 128, $FFFFFF)
StopDrawing()
SaveImage(0,GetTemporaryDirectory() + "/temp.bmp")
FreeImage(0)
LoadTexture(0,GetTemporaryDirectory() + "/temp.bmp")
DeleteFile(GetTemporaryDirectory() + "/temp.bmp")
Then it works ... but I don't understand the game. After I've seen Demivec's picture I thought I know it, but in this game you'd try to remove the bricks instead of stacking them.

Re: Jenga's Nest - The infernal tower

Posted: Tue Apr 26, 2011 1:58 pm
by djes
It's why you had these bugs? :shock:
Thank you for the code :)

Re: Jenga's Nest - The infernal tower

Posted: Mon Mar 05, 2012 4:42 pm
by djes
Little update, see first post.