Created
December 5, 2021 09:35
-
-
Save GWRon/d518fa3c86ff45b604366de24b60bbdf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
' J I G S A W - G A M E E X A M P L E | |
' | |
' Author Midimaster www.midimaster.de | |
' | |
' Copyright: Public Domain | |
' Version 1.01 | |
' last change: now for BlitzMax NG and BlitzMax 1.51 | |
' | |
' your own pngs: | |
' a transparent "jigmiddle.png" with a smaller black rectangle in the exact center, | |
' a transparent "jignose.png" with one example for the black jigsaw nose looking to the right | |
' the transparent border around the rectangle needs to be exact the size to take the nose | |
' both pngs and the inner rectangle need to be quadratic with a odd side length | |
' the "foto.png" size needs to be a multiple of the inner rectangle size. | |
SuperStrict | |
Framework Brl.StandardIO | |
Import Random.Xoshiro | |
Import Brl.Retro | |
Import Brl.LinkedList | |
Import Brl.GLMax2D | |
Import Brl.PNGLoader | |
Import "dig.base.gfx.imagehelper.bmx" | |
Global Nr%=0, Move% | |
'Delevopers Flags: | |
Const SORTED% = 0 ' set to 1 to get sorted JigSaw pieces during Development/Debugging | |
Const ONLY_MASK% = 0 ' set to 1 to see only JigSaw piece masks during Development/Debugging | |
Const SHOW_TYP% = 0 ' set to 1 to see additional Typ information (like "FHHW" )next to the piece during Development/Debugging | |
Const LIGHT_OFF% = 0 ' set to 1 to switch off border lighting during Development/Debugging | |
Const ONLY_BLUE% = 0 ' set to 1 to switch off pixel picking from JigFoto during Development/Debugging | |
Const NO_SHADOW% = 0 ' set to 1 to hide shadow layer during Development/Debugging | |
Const SHOW_GRID% = 2 ' set to 1 to show placement grid for tiles and 2 to show puzzle outline | |
Const SHOW_TILE_ORIGIN% = 0 ' set to 1 to handle of the tiles | |
AppTitle="JigSaw example by midimaster.de" | |
Graphics 1400,900 | |
Global jigsaw:TJigsaw = new TJigsaw | |
jigSaw.Init() | |
SetClsColor 55,111,111 | |
Repeat | |
Cls | |
SetColor(255,255,255) | |
SetBlend(alphablend) | |
jigsaw.Update() | |
MouseAction() | |
jigsaw.Draw() | |
Flip(1) | |
Until AppTerminate() | |
Function MouseAction() | |
If MouseDown(1) | |
If move = 0 | |
local oldSelectedTile:TJigsawTile = jigsaw.selectedTile | |
jigsaw.SelectTile( jigSaw.GetTileAtXY(MouseX() - jigsaw.x, MouseY() - jigsaw.y) ) | |
If jigsaw.selectedTile | |
If jigsaw.selectedTile <> oldSelectedTile | |
jigsaw.DragTile(jigsaw.selectedTile) | |
jigsaw.HoverTile(jigsaw.selectedTile) | |
Endif | |
move = 1 | |
MouseXSpeed() | |
MouseYSpeed() | |
EndIf | |
ElseIf move = 1 | |
jigsaw.selectedTile.x :+ MouseXSpeed() | |
jigsaw.selectedTile.y :+ MouseYSpeed() | |
EndIf | |
ElseIf MouseDown(2) | |
If move = 0 | |
local oldSelectedTile:TJigsawTile = jigsaw.selectedTile | |
jigsaw.SelectTile(jigsaw.GetTileAtXY(MouseX() - jigsaw.x, MouseY() - jigsaw.y) ) | |
If jigsaw.selectedTile | |
If jigsaw.selectedTile <> oldSelectedTile | |
jigsaw.DragTile(jigsaw.selectedTile) | |
Endif | |
move = 2 | |
MouseXSpeed() | |
MouseYSpeed() | |
EndIf | |
Else | |
jigsaw.selectedTile.rotation :+ MouseXSpeed() | |
EndIf | |
ElseIf move > 0 | |
if jigsaw.TryToPlaceTile(jigsaw.selectedTile) | |
jigsaw.selectedTile = Null | |
Endif | |
move = 0 | |
Else | |
jigsaw.DropTile(jigsaw.selectedTile) | |
EndIf | |
End Function | |
Type TJigsawTileConfig | |
Field size:Int | |
Field baseSize:Int | |
Field borderSize:Int | |
Field basePixShape:TPixmap | |
Field basePixNoseShape:TPixmap | |
End Type | |
Type TJigsawTile | |
Field sideTypes:Int[] | |
Field sideOffsets:Int[] | |
Field x:Int, y:Int | |
Field rotation:Int | |
Field column:Int, row:Int | |
Field tileConfig:TJigsawTileConfig | |
Field placed:Int | |
Field selected:Int | |
Field pixTile:TPixmap, pixShape:TPixmap | |
Field imgTile:TImage, imgShadow:TImage, imgOutline:TImage, imgGridBackground:TImage | |
Const outlineOffset:Int = 0 | |
Const shadowBlurStrength:Float = 0.25 | |
Const shadowAlpha:Float = 0.25 | |
Const shadowOffset:Int = 4 | |
Const draggedShadowOffset:Int = 8 | |
Const draggedShadowAlpha:Float = 0.4 | |
Const selectedOffset:Int = -2 | |
Method Init:TJigsawTile(jigsawPicture:TPixmap, tileConfig:TJigsawTileConfig, sideTypes:Int[], sideOffsets:Int[], column:Int, row:Int) | |
self.column = column | |
self.row = row | |
self.sideTypes = sideTypes | |
self.sideOffsets = sideOffsets | |
self.tileConfig = tileConfig | |
self._GenerateShape() | |
self._GenerateTileImage(jigsawPicture) | |
self._GenerateShadowImage() | |
self._GenerateOutlineImage() | |
self._GenerateGridBackgroundImage() | |
MidHandleImage(imgTile) | |
MidHandleImage(imgShadow) | |
MidHandleImage(imgOutline) | |
Return self | |
End Method | |
Method DrawShadow(offsetX:Int, offsetY:Int) | |
If NO_SHADOW Then Return | |
Local oldA:Float = GetAlpha() | |
SetRotation(rotation) | |
If selected | |
SetAlpha(draggedShadowAlpha * oldA) | |
DrawImage(imgShadow, x + offsetX + draggedShadowOffset + selectedOffset, y + offsetY + draggedShadowOffset + selectedOffset) | |
Else | |
SetAlpha(shadowAlpha * oldA) | |
DrawImage(imgShadow, x + offsetX + shadowOffset, y + offsetY + shadowOffset) | |
EndIf | |
SetRotation(0) | |
SetAlpha(oldA) | |
End Method | |
Method DrawOutline(offsetX:Int, offsetY:Int) | |
SetRotation(rotation) | |
if selected | |
DrawImage(imgOutline, x + offsetX + outlineOffset + selectedOffset, y + offsetY + outlineOffset + selectedOffset) | |
else | |
DrawImage(imgOutline, x + offsetX + outlineOffset, y + offsetY + outlineOffset) | |
endif | |
SetRotation(0) | |
End Method | |
Method Draw(offsetX:Int, offsetY:Int) | |
SetRotation(rotation) | |
if selected | |
DrawImage(imgTile, x + offsetX + selectedOffset, y + offsetY + selectedOffset) | |
else | |
DrawImage(imgTile, x + offsetX, y + offsetY) | |
endif | |
SetRotation(0) | |
If SHOW_TYP = 1 | |
local t:String | |
For local i:int = EachIn sideTypes | |
if i = TJigsaw.SIDE_HOLE then t :+ "H" | |
if i = TJigsaw.SIDE_NOSE then t :+ "N" | |
if i = TJigsaw.SIDE_EMPTY then t :+ "F" | |
Next | |
DrawText(t, x + offsetX + 70, y + offsetY + 70) | |
EndIf | |
if SHOW_TILE_ORIGIN | |
DrawOval(x + offsetX - 5, y + offsetY - 5, 10, 10) | |
Endif | |
End Method | |
Method _GenerateShape() | |
pixShape = tileConfig.basePixShape.Copy() | |
Local noseSize:Int = tileConfig.basePixNoseShape.width | |
Select sideTypes[0] | |
Case TJigsaw.SIDE_HOLE | |
_CutMask(pixShape, tileConfig.basePixNoseShape, TJigsaw.SIDE_TOP, tileConfig.borderSize + sideOffsets[0], tileConfig.borderSize) | |
Case TJigsaw.SIDE_NOSE | |
_AddMask(pixShape, tileConfig.basePixNoseShape, TJigsaw.SIDE_TOP, tileConfig.borderSize + sideOffsets[0], tileConfig.borderSize - 1) | |
End Select | |
Select sideTypes[1] | |
Case TJigsaw.SIDE_HOLE | |
_CutMask(pixShape, tileConfig.basePixNoseShape, TJigsaw.SIDE_RIGHT, tileConfig.borderSize + tileConfig.baseSize - 1, tileConfig.borderSize + sideOffsets[1]) | |
Case TJigsaw.SIDE_NOSE | |
_AddMask(pixShape, tileConfig.basePixNoseShape, TJigsaw.SIDE_RIGHT, tileConfig.basesize + tileConfig.borderSize, tileConfig.borderSize + sideOffsets[1]) | |
End Select | |
Select sideTypes[2] | |
Case TJigsaw.SIDE_HOLE | |
_CutMask(pixShape, tileConfig.basePixNoseShape, TJigsaw.SIDE_BOTTOM, tileConfig.borderSize + sideOffsets[2], tileConfig.baseSize + tileConfig.borderSize - 1) | |
Case TJigsaw.SIDE_NOSE | |
_AddMask(pixShape, tileConfig.basePixNoseShape, TJigsaw.SIDE_BOTTOM, tileConfig.borderSize + sideOffsets[2], tileConfig.baseSize + tileConfig.borderSize) | |
End Select | |
Select sideTypes[3] | |
Case TJigsaw.SIDE_HOLE | |
_CutMask(pixShape, tileConfig.basePixNoseShape, TJigsaw.SIDE_LEFT, tileConfig.borderSize, tileConfig.borderSize + sideOffsets[3]) | |
Case TJigsaw.SIDE_NOSE | |
_AddMask(pixShape, tileConfig.basePixNoseShape, TJigsaw.SIDE_LEFT, tileConfig.borderSize - 1, tileConfig.borderSize + sideOffsets[3]) | |
End Select | |
End Method | |
Method _GenerateShadowImage() | |
'shadow must be a bit bigger | |
Local shadowPix:TPixmap = CreatePixmap(pixShape.width + 10, pixShape.height + 10, pixShape.format) | |
shadowPix.ClearPixels(0) | |
shadowPix.Paste(pixShape, 5, 5) | |
blurPixmap(shadowPix, shadowBlurStrength, $ffffff00) | |
imgShadow = LoadImage(shadowPix, FILTEREDIMAGE) | |
End Method | |
Method _GenerateOutlineImage() | |
'outline must be a bit bigger | |
imgOutline = ConvertToOutLine(pixShape, 3, 0.75, $ffffffff, 4) | |
Local outlinePix:TPixmap = LockImage(imgOutline) | |
blurPixmap(outlinePix, 0.7, $FF000000) | |
imgOutline = LoadImage(outlinePix, FILTEREDIMAGE) | |
End Method | |
Method _GenerateGridBackgroundImage() | |
'outline must be a bit bigger | |
imgGridBackground = ConvertToOutLine(pixShape, 2, 0.8, $66ffffff, 4) | |
Local outlinePix:TPixmap = LockImage(imgGridBackground) | |
blurPixmap(outlinePix, 0.7, $00000000) | |
imgGridBackground = LoadImage(outlinePix, FILTEREDIMAGE) | |
End Method | |
Method _GenerateTileImage(jigsawTexture:TPixmap) | |
pixTile = CreatePixmap(pixShape.width, pixShape.height, PF_RGBA8888) | |
pixTile.ClearPixels(0) | |
If ONLY_MASK = 0 | |
For Local pixX:Int = 0 Until pixTile.width | |
For Local pixY:Int = 0 Until pixTile.height | |
local maskAlpha:Int = (pixShape.ReadPixel(pixX, pixY) Shr 24) & $ff | |
'skip invisible | |
If maskAlpha <= 0 Then Continue | |
Local sourceColor:Int | |
If ONLY_BLUE = 0 | |
sourceColor = jigsawTexture.ReadPixel(pixX + (column-1) * tileConfig.baseSize - tileConfig.borderSize, pixY + (row-1) * tileConfig.baseSize - tileConfig.borderSize) | |
Else | |
sourceColor = $ff7777ff | |
EndIf | |
'remove source alpha and add mask alpha | |
pixTile.WritePixel(pixX, pixY, (sourceColor & $00FFFFFF) + (maskAlpha * $1000000) ) | |
Next | |
Next | |
_AddLightEffect(pixTile, pixShape) | |
EndIf | |
imgTile = LoadImage(pixTile, FILTEREDIMAGE) | |
End Method | |
Function _AddLightEffect(pix:TPixmap, mask:TPixmap, lightDirection:int = 0) | |
If LIGHT_OFF=1 Return | |
Local now:Int=0 | |
Local c:Int | |
'scan along pixel lines and light up first opacque pixel and | |
'darken the ones before the first transparent one, rinse repeat | |
For Local pixX:Int = 0 Until pix.width | |
now = 0 | |
For Local pixY:Int = 0 Until pix.height | |
c = mask.ReadPixel(pixX, pixY) | |
If now = 0 and c <> 0 | |
pix.WritePixel(pixX, pixY, RiseColor(1.3, pix.ReadPixel(pixX, pixY))) | |
if pixY + 1 < pix.height | |
pix.WritePixel(pixX, pixY + 1, RiseColor(1.4, pix.ReadPixel(pixX, pixY + 1))) | |
EndIf | |
now = 1 | |
ElseIf now = 1 and c = 0 | |
if pixY - 2 >= 0 | |
pix.WritePixel(pixX, pixY - 2, RiseColor(0.7, pix.ReadPixel(pixX, pixY - 2))) | |
EndIf | |
If pixY - 1 >= 0 | |
pix.WritePixel(pixX, pixY - 1, RiseColor(0.8, pix.ReadPixel(pixX, pixY - 1))) | |
EndIf | |
now = 0 | |
EndIf | |
Next | |
Next | |
For Local pixY:Int = 0 Until pix.height | |
now = 0 | |
For Local pixX:Int = 0 Until pix.width | |
c = mask.ReadPixel(pixX, pixY) | |
If now = 0 and c <> 0 | |
pix.WritePixel(pixX, pixY, RiseColor(1.4, pix.ReadPixel(pixX, pixY))) | |
If pixX + 1 < pix.width | |
pix.WritePixel(pixX + 1, pixY, RiseColor(1.4, pix.ReadPixel(pixX + 1, pixY))) | |
EndIf | |
now = 1 | |
ElseIf now = 1 and c = 0 | |
If pixX - 2 >= 0 | |
pix.WritePixel(pixX - 2, pixY, RiseColor(0.7, pix.ReadPixel(pixX - 2, pixY))) | |
EndIf | |
If pixX - 1 >= 0 | |
pix.WritePixel(pixX - 1, pixY, RiseColor(0.7, pix.ReadPixel(pixX - 1, pixY))) | |
EndIf | |
now = 0 | |
EndIf | |
Next | |
Next | |
'yep, function in a function! | |
Function RiseColor:Int(f:Float, color:Int) | |
Local r:Int = (color & $00ff0000) / $10000 | |
Local g:Int = (color & $0000ff00) / $100 | |
Local b:Int = (color & $000000ff) | |
r = int(Min(r*f, 255)) * $10000 | |
g = int(Min(g*f, 255)) * $100 | |
b = int(Min(b*f, 255)) | |
Return (color & $ff000000) | r | g | b | |
End Function | |
End Function | |
Function _AddMask(base:TPixmap, mask:TPixmap, side:Int, offsetX:Int, offsetY:Int) | |
if base.width < mask.width + offsetX Then Throw "TJigsawTile - mask too big (width)" | |
if base.height < mask.height + offsetY Then Throw "TJigsawTile - mask too big (height)" | |
For Local maskX:Int = 0 Until mask.width | |
For Local maskY:Int = 0 Until mask.height | |
Local maskColor:Int = mask.ReadPixel(maskX, maskY) | |
If maskColor = 0 Then continue | |
Select side | |
Case TJigsaw.SIDE_RIGHT | |
base.WritePixel(offsetX + maskX, offsetY + maskY, maskColor) | |
Case TJigsaw.SIDE_LEFT | |
base.WritePixel(offsetX - maskX, offsetY + maskY, maskColor) | |
Case TJigsaw.SIDE_BOTTOM | |
base.WritePixel(offsetX + maskY, offsetY + maskX, maskColor) | |
Case TJigsaw.SIDE_TOP | |
base.WritePixel(offsetX + maskY, offsetY - maskX, maskColor) | |
End Select | |
Next | |
Next | |
End Function | |
Function _CutMask(base:TPixmap, mask:TPixmap, side:Int, offsetX:Int, offsetY:Int) | |
if base.width < mask.width + offsetX Then Throw "TJigsawTile - mask too big (width)" | |
if base.height < mask.height + offsetY Then Throw "TJigsawTile - mask too big (height)" | |
For Local maskX:Int = 0 Until mask.width | |
For Local maskY:Int = 0 Until mask.height | |
'for cuts/holes we reduce alpha of the pixmap's pixel | |
'by what is left (so NOSE alpha 255 cuts 100%, 0 cuts 0%) | |
Local maskColor:Int = mask.ReadPixel(maskX, maskY) | |
If maskColor = 0 Then continue | |
Local maskAlpha:Int = (maskColor Shr 24) & $ff | |
Select side | |
Case TJigsaw.SIDE_RIGHT | |
'remove original alpha and add back "alpha mix" | |
base.WritePixel(offsetX - maskX, offsetY + maskY, (base.ReadPixel(offsetX - maskX, offsetY + maskY) & $00FFFFFF) + (255 - maskAlpha) * $1000000) | |
Case TJigsaw.SIDE_LEFT | |
'remove original alpha and add back "alpha mix" | |
base.WritePixel(offsetX + maskX, offsetY + maskY, (base.ReadPixel(offsetX + maskX, offsetY + maskY) & $00FFFFFF) + (255 - maskAlpha) * $1000000) | |
Case TJigsaw.SIDE_BOTTOM | |
'remove original alpha and add back "alpha mix" | |
base.WritePixel(offsetX + maskY, offsetY - maskX, (base.ReadPixel(offsetX + maskY, offsetY - maskX) & $00FFFFFF) + (255 - maskAlpha) * $1000000) | |
Case TJigsaw.SIDE_TOP | |
'remove original alpha and add back "alpha mix" | |
base.WritePixel(offsetX + maskY, offsetY + maskX, (base.ReadPixel(offsetX + maskY, offsetY + maskX) & $00FFFFFF) + (255 - maskAlpha) * $1000000) | |
End Select | |
Next | |
Next | |
End Function | |
End Type | |
Type TJigsaw | |
'tiles are ordered by their creation order - or manual drag/drop | |
Field tiles:TList = New TList | |
Field tilesReversed:TList = New TList | |
Field Columns%, Rows% | |
'position of jigsaw on screen | |
Field x:Int, y:Int | |
Field w:Int=-1, h:Int=-1 | |
'offset of image from jigsaw origin | |
Field imageOffsetX:int=100, imageOffsetY:Int=100 | |
Field selectedTile:TJigsawTile | |
Field hoveredTile:TJigsawTile | |
Field tileConfig:TJigsawTileConfig | |
Field jigsawPicture:TPixmap | |
Const SIDE_EMPTY:Int = 0 | |
Const SIDE_NOSE:Int = 1 | |
Const SIDE_HOLE:Int = 2 | |
Const SIDE_LEFT:Int = 4 | |
Const SIDE_TOP:Int = 8 | |
Const SIDE_RIGHT:Int = 16 | |
Const SIDE_BOTTOM:Int = 32 | |
Method Init() | |
tileConfig = new TJigsawTileConfig | |
tileConfig.basePixShape = LoadPixmap("jigMiddle.png") | |
tileConfig.basePixNoseShape = LoadPixmap("jigNOSE.png") | |
jigsawPicture = LoadPixmap("jigfoto.png") | |
if not tileConfig.basePixShape then throw "jigMiddle.png not found" | |
if not tileConfig.basePixNoseShape then throw "jigNOSE.png not found" | |
if not jigsawPicture then throw "jigfoto.png not found" | |
CalculateSizes() | |
GenerateTiles() | |
If SORTED=0 | |
ShuffleTiles() | |
Else | |
For Local tile:TJigsawTile = eachin tiles | |
tile.rotation = 0 | |
tile.x = (tile.column-1)*(tileConfig.size-20) + 100 | |
tile.y = (tile.row-1)*(tileConfig.size-20) + 100 | |
Next | |
EndIf | |
End Method | |
Method CalculateSizes() | |
Print "GenerateTiles()" | |
tileConfig.size = Min(tileConfig.basePixShape.width, tileConfig.basePixShape.height) | |
tileConfig.baseSize = _FindTileBaseWidth(tileConfig.basePixShape) | |
tileConfig.borderSize = (tileConfig.size - tileConfig.baseSize) / 2 | |
Print " size=" + tileConfig.size + " (base=" + tileConfig.baseSize + " borders=" + tileConfig.borderSize + ")" | |
'if image is bigger than puzzle size (eg some pixels too wide) | |
'we simply ignore the "rest" | |
'but if too small to fit at least once ... we fail | |
columns = jigsawPicture.width / tileConfig.baseSize | |
rows = jigsawPicture.height / tileConfig.baseSize | |
Print " columns=" + columns + " rows=" + rows | |
If columns = 0 or rows = 0 | |
RuntimeError "ERROR: picture too small (" + jigsawPicture.width + "x" + jigsawPicture.height + ", minimum required: " + tileConfig.baseSize + "x" + tileConfig.baseSize + ")" | |
EndIf | |
End Method | |
'scans y-center from left to right to find first opacque point | |
'assume inner part is "centered" (left space = right space) | |
Function _FindTileBaseWidth:Int(pix:TPixmap) | |
For Local x:Int = 0 until pix.width | |
If pix.ReadPixel(x, pix.height/2)<>0 | |
Return pix.width - 2 * x | |
EndIf | |
Next | |
RuntimeError "_FindTileBaseWidth: Failed to find non-transparent point on vertical center line" | |
End Function | |
Method GenerateTiles() | |
Print "GenerateTiles()" | |
SeedRnd MilliSecs() | |
For Local row:Int = 1 To rows | |
For Local col:Int = 1 To columns | |
local sideTypes:Int[] = new Int[4] | |
local sideOffsets:Int[] = new Int[4] | |
'top | |
'first row? | |
If row = 1 | |
sideTypes[0] = SIDE_EMPTY | |
Else | |
Local neighbourTop:TJigsawTile = GetTile(col,row-1) | |
If neighbourTop | |
If neighbourTop.sideTypes[2] = SIDE_HOLE | |
sideTypes[0] = SIDE_NOSE | |
Else | |
sideTypes[0] = SIDE_HOLE | |
EndIf | |
sideOffsets[0] = neighbourTop.sideOffsets[2] | |
EndIf | |
EndIf | |
'right | |
'last column? | |
If col = columns | |
sideTypes[1] = SIDE_EMPTY | |
Else | |
Select Rand(0,1) | |
Case 0 | |
sideTypes[1] = SIDE_HOLE | |
Case 1 | |
sideTypes[1] = SIDE_NOSE | |
End Select | |
'TODO: anders definieren | |
sideOffsets[1] = Rand(30, 65) | |
EndIf | |
'bottom | |
'last row? | |
If row = rows | |
sideTypes[2] = SIDE_EMPTY | |
Else | |
Select Rand(0,1) | |
Case 0 | |
sideTypes[2] = SIDE_HOLE | |
Case 1 | |
sideTypes[2] = SIDE_NOSE | |
End Select | |
'TODO: anders definieren | |
sideOffsets[2] = Rand(25, 70) | |
EndIf | |
'left | |
'first column? | |
If col = 1 | |
sideTypes[3] = SIDE_EMPTY | |
Else | |
Local neighbourLeft:TJigsawTile = GetTile(col - 1,row) | |
If neighbourLeft | |
If neighbourLeft.sideTypes[1] = SIDE_HOLE | |
sideTypes[3] = SIDE_NOSE | |
Else | |
sideTypes[3] = SIDE_HOLE | |
EndIf | |
sideOffsets[3] = neighbourLeft.sideOffsets[1] | |
EndIf | |
EndIf | |
Local tile:TJigsawTile = New TJigsawTile.Init(jigsawPicture, tileConfig, sideTypes, sideOffsets, col, row) | |
tiles.AddFirst(tile) | |
tilesReversed.AddLast(tile) | |
Next | |
Next | |
End Method | |
Method ShuffleTiles:int() | |
For local tile:TJigsawTile = EachIn tiles | |
tile.rotation = Rand(0,359) | |
tile.x = Rand(GraphicsWidth()-300) + 200 | |
tile.y = Rand(GraphicsHeight()-300) + 200 | |
Next | |
End Method | |
Method GetTile:TJigsawTile(column:Int, row:Int) | |
For Local t:TJigsawTile = EachIn tiles | |
If t.column = column And t.row = row | |
Return t | |
EndIf | |
Next | |
Return Null | |
End Method | |
Method IsTileOverTargetCell:Int(tile:TJigsawTile) | |
'print "IsTileOverTargetCell: " + " x="+tile.x +" y="+tile.y | |
'print "IsTileOverTargetCell: " + " currCol="+((tile.x - imageOffsetX + tileConfig.baseSize/2) / tileConfig.baseSize + 1)+" tarCol="+tile.column | |
'print "IsTileOverTargetCell: " + " currRow="+((tile.y - imageOffsetY + tileConfig.baseSize/2) / tileConfig.baseSize + 1)+" tarRow="+tile.row | |
Return ((tile.x - imageOffsetX + tileConfig.baseSize/2) / tileConfig.baseSize) + 1 = tile.column And ((tile.y - imageOffsetY + tileConfig.baseSize/2) / tileConfig.baseSize) + 1 = tile.row | |
End Method | |
Method PlaceTileOnTargetCell(tile:TJigsawTile) | |
tile.placed = True | |
tile.rotation = 0 | |
tile.x = (tile.column - 1) * tileConfig.baseSize + imageOffsetX | |
tile.y = (tile.row - 1) * tileConfig.baseSize + imageOffsetY | |
tile.placed = True | |
End Method | |
Method TryToPlaceTile:Int(tile:TJigsawTile) | |
if not tile then Return False | |
If IsTileOverTargetCell(tile) | |
'expect tile to be rotated almost into original rotation | |
tile.rotation = (tile.rotation + 360) Mod 360 | |
If tile.rotation < 10 Or tile.rotation > 350 | |
PlaceTileOnTargetCell(tile) | |
DropTile(tile) | |
tiles.Remove(tile) | |
tiles.AddFirst(tile) | |
tilesReversed = tiles.Reversed() | |
Return True | |
EndIf | |
EndIf | |
Return False | |
End Method | |
Method Update() | |
if not selectedTile | |
hoveredTile = jigsaw.GetTileAtXY(MouseX() - x, MouseY() - y) | |
endif | |
End Method | |
Method Draw() | |
SetColor 50,50,50 | |
if w=-1 and h=-1 | |
DrawRect(x, y, GraphicsWidth() - x, GraphicsHeight() - y) | |
else | |
DrawRect(x, y, w, h) | |
endif | |
SetColor 152, 122, 35 | |
DrawRect(imageOffsetX - tileConfig.baseSize/2 - 3, imageOffsetY - tileConfig.baseSize/2 - 3, tileConfig.baseSize * columns + 6, tileConfig.baseSize * rows + 6) | |
SetColor 222,155,55 | |
DrawRect(imageOffsetX - tileConfig.baseSize/2 + 3,imageOffsetY - tileConfig.baseSize/2 + 3, tileConfig.baseSize * columns - 6, tileConfig.baseSize * rows - 6) | |
SetColor 255,255,255 | |
'puzzle grid | |
if SHOW_GRID = 1 | |
For local r:int = 0 to rows ' from 0 so it contains "begin" too | |
DrawLine(imageOffsetX, imageOffsetY + r*tileConfig.baseSize, imageOffsetX + columns*tileConfig.baseSize, imageOffsetY + r*tileConfig.baseSize) | |
Next | |
For local c:int = 0 to columns | |
DrawLine(imageOffsetX + c*tileConfig.baseSize, imageOffsetY, imageOffsetX + c*tileConfig.baseSize, imageOffsetY + rows*tileConfig.baseSize) | |
Next | |
Elseif SHOW_GRID = 2 | |
For local t:TJigsawTile = EachIn tiles | |
DrawImage(t.imgGridBackground, imageOffsetX + (t.column-1)*tileConfig.baseSize - tileConfig.baseSize/2 - tileConfig.borderSize - 5, imageOffsetY + (t.row-1)*tileConfig.baseSize - tileConfig.baseSize/2 - tileConfig.borderSize - 5) | |
Next | |
EndIf | |
DrawTiles() | |
End Method | |
Method DrawTiles() | |
'draw layer of base tiles (so exclude top most one) | |
For Local t:TJigsawTile= EachIn tiles | |
if t <> selectedTile Then t.DrawShadow(x, y) | |
Next | |
'draw tile itself | |
For Local t:TJigsawTile = EachIn tiles | |
If t = selectedTile Then t.DrawShadow(x, y) | |
t.Draw(x, y) | |
if hoveredTile = t | |
SetBlend LIGHTBLEND | |
SetAlpha Float(0.1 + 0.1 * Sin(Millisecs() * 0.2)) | |
t.Draw(x, y) | |
SetBlend ALPHABLEND | |
SetAlpha 1.0 | |
EndIf | |
if hoveredTile = t | |
SetAlpha 0.9 | |
t.DrawOutline(x, y) | |
SetAlpha 1.0 | |
EndIf | |
'If t = hoveredTile Then t.DrawOutline(x, y) | |
Next | |
'draw hover on top of all | |
if hoveredTile | |
SetAlpha 0.4 | |
hoveredTile.DrawOutline(x, y) | |
SetAlpha 1.0 | |
EndIf | |
End Method | |
Method HoverTile:Int(t:TJigsawTile) | |
If t <> hoveredTile | |
hoveredTile = t | |
Return True | |
endif | |
Return False | |
End Method | |
Method SelectTile:Int(t:TJigsawTile) | |
print "select" | |
if selectedTile then selectedTile.selected = False | |
If t <> selectedTile | |
if t then t.selected = True | |
selectedTile = t | |
Return True | |
endif | |
Return False | |
End Method | |
Method DropTile(t:TJigsawTile) | |
if t | |
if t = selectedTile then selectedTile = Null | |
t.selected = False | |
endif | |
End Method | |
Method DragTile(t:TJigsawTile) | |
'move it on top | |
tiles.Remove(t) | |
tiles.AddLast(t) | |
tilesReversed = tiles.Reversed() | |
End Method | |
Method GetTileAtXY:TJigsawTile(x:Int, y:Int, lookupRange:int = -1) | |
if lookupRange = -1 then lookupRange = tileConfig.baseSize / 2 | |
For Local t:TJigsawTile = EachIn tilesReversed | |
if t.placed then continue | |
If Abs(x - t.x) < lookupRange | |
If Abs(y - t.y) < lookupRange | |
Return t | |
EndIf | |
EndIf | |
Next | |
Return Null | |
End Method | |
End Type |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment