' Hardwire ' ' Copyright 2005 Ian Cowburn ' ' $Id$ ' Strict Import "types.bmx" Import "particle.bmx" Import "sounds.bmx" Type Pit Const WIDTH=10 Const HEIGHT=14 Function X:Int(p:Int) Return 230+p*32 End Function Function Y:Int(p:Int) Return 96+p*32 End Function End Type Type TWire Const CROSS:Int=0 Const LEFT_RIGHT:Int=1 Const TOP_BOTTOM:Int=2 Const TOP_LEFT:Int=3 Const TOP_RIGHT:Int=4 Const BOTTOM_LEFT:Int=5 Const BOTTOM_RIGHT:Int=6 Const DIR_NONE:Int=0 Const DIR_UP:Int=1 Const DIR_RIGHT:Int=2 Const DIR_DOWN:Int=3 Const DIR_LEFT:Int=4 Global rotright:Int[] Global rotleft:Int[] Global img:TImage[] Global dir:Int[][] Global dx:Int[] Global dy:Int[] Global dirname:String[] Global typename:String[] Field t:Int Function Init() rotright=[0,2,1,4,6,3,5] rotleft=[0,2,1,5,3,6,4] img=[GameGFX.cross,GameGFX.left_right,GameGFX.top_bottom,GameGFX.top_left,GameGFX.top_right,GameGFX.bottom_left,GameGFX.bottom_right] ' CROSS LEFT_RIGHT TOP_BOTTOM TOP_LEFT TOP_RIGHT BOTTOM_LEFT BOTTOM_RIGHT dir=[[DIR_UP,DIR_RIGHT,DIR_DOWN,DIR_LEFT], [DIR_NONE,DIR_RIGHT,DIR_NONE,DIR_LEFT], [DIR_UP,DIR_NONE,DIR_DOWN,DIR_NONE], [DIR_NONE,DIR_UP,DIR_LEFT,DIR_NONE], [DIR_NONE,DIR_NONE,DIR_RIGHT,DIR_UP], [DIR_LEFT,DIR_DOWN,DIR_NONE,DIR_NONE], [DIR_RIGHT,DIR_NONE,DIR_NONE,DIR_DOWN]] dx=[0,0,1,0,-1] dy=[0,-1,0,1,0] dirname=["NONE","UP","RIGHT","DOWN","LEFT"] typename=["CROSS","LEFT_RIGHT","TOP_BOTTOM","TOP_LEFT","TOP_RIGHT","BOTTOM_LEFT","BOTTOM_RIGHT"] End Function Method New() t=Rand(0,6) End Method Function Create:TWire(t:Int[]) Local o:TWire=New TWire o.t=t[Rand(0,t.length-1)] Return o End Function Method Image:TImage() Return img[t] End Method Method RotateLeft() t=rotleft[t] End Method Method RotateRight() t=rotright[t] End Method Method Traverse:Int(d:Int) If d=DIR_NONE Return DIR_NONE EndIf Return dir[t][d-1] End Method Function DirX:Int(d:Int) Return dx[d] End Function Function DirY:Int(d:Int) Return dy[d] End Function End Type Type TPiece Abstract Field x:Int Field y:Int Field ox:Int Field oy:Int Field map:TWire[4,4] Field rot:Int Field offx:Int[4] Field offy:Int[4] Function Create:TPiece() Local o:TPiece Select Rand(0,5) Case 0 o=TPiece(New TPiece_S_Left) Case 1 o=TPiece(New TPiece_S_Right) Case 2 o=TPiece(New TPiece_L_Left) Case 3 o=TPiece(New TPiece_L_Right) Case 4 o=TPiece(New TPiece_Square) Case 5 o=TPiece(New TPiece_Bar) End Select o.BaseInit() Return o End Function Method Init() Abstract Method BaseInit() Init() rot=0 ox=offx[rot] oy=offy[rot] End Method Method Draw() For Local px:Int=0 Until 4 For Local py:Int=0 Until 4 If map[px,py] Local gx:Int=Pit.X(x-ox+px) Local gy:Int=Pit.Y(y-oy+py) SetColor(255,255,255) DrawImage(GameGFX.tile,gx,gy) SetColor(128,128,128) DrawImage(map[px,py].Image(),gx,gy) EndIf Next Next End Method Method RotateLeft() If rot=0 rot=3 Else rot:-1 EndIf ox=offx[rot] oy=offy[rot] Local m:TWire[4,4] For Local px:Int=0 Until 4 For Local py:Int=0 Until 4 If map[px,py] map[px,py].RotateLeft() EndIf m[py,3-px]=map[px,py] Next Next map=m End Method Method RotateRight() rot=(rot+1) Mod 4 ox=offx[rot] oy=offy[rot] Local m:TWire[4,4] For Local px:Int=0 Until 4 For Local py:Int=0 Until 4 If map[px,py] map[px,py].RotateRight() EndIf m[3-py,px]=map[px,py] Next Next map=m End Method Method Collides:Int(gm:TGameMap) For Local px:Int=0 Until 4 For Local py:Int=0 Until 4 If map[px,py] Local gx:Int=x-ox+px Local gy:Int=y-oy+py If gx<0 Or gx>=Pit.WIDTH Or gy>=Pit.HEIGHT Return True EndIf If gy>=0 And gm.map[gx,gy] Return True EndIf EndIf Next Next Return False End Method Method AddToMap(gm:TGameMap) For Local px:Int=0 Until 4 For Local py:Int=0 Until 4 If map[px,py] Local gx:Int=x-ox+px Local gy:Int=y-oy+py If gy<0 gm.overflow=True Else gm.map[gx,gy]=map[px,py] Particles.AddDust(Pit.X(gx)+16,Pit.Y(gy)+32) EndIf EndIf Next Next Sound.Click() gm.CheckWires() End Method End Type Type TPiece_S_Right Extends TPiece Method Init() map[1,0]=TWire.Create([TWire.CROSS,TWire.BOTTOM_RIGHT]) map[2,0]=TWire.Create([TWire.CROSS,TWire.TOP_LEFT,TWire.BOTTOM_LEFT,TWire.LEFT_RIGHT]) map[0,1]=TWire.Create([TWire.CROSS,TWire.TOP_RIGHT,TWire.BOTTOM_RIGHT,TWire.LEFT_RIGHT]) map[1,1]=TWire.Create([TWire.CROSS,TWire.TOP_LEFT]) offx=[1,2,2,0] offy=[1,1,2,2] End Method End Type Type TPiece_S_Left Extends TPiece Method Init() map[0,0]=TWire.Create([TWire.CROSS,TWire.TOP_RIGHT,TWire.BOTTOM_RIGHT,TWire.LEFT_RIGHT]) map[1,0]=TWire.Create([TWire.CROSS,TWire.BOTTOM_LEFT]) map[1,1]=TWire.Create([TWire.CROSS,TWire.TOP_RIGHT]) map[2,1]=TWire.Create([TWire.CROSS,TWire.TOP_LEFT,TWire.BOTTOM_LEFT,TWire.LEFT_RIGHT]) offx=[1,2,2,0] offy=[1,1,2,2] End Method End Type Type TPiece_L_Right Extends TPiece Method Init() map[0,0]=TWire.Create([TWire.CROSS,TWire.BOTTOM_RIGHT]) map[1,0]=TWire.Create([TWire.CROSS,TWire.TOP_LEFT,TWire.BOTTOM_LEFT,TWire.LEFT_RIGHT]) map[0,1]=TWire.Create([TWire.CROSS,TWire.TOP_BOTTOM]) map[0,2]=TWire.Create([TWire.CROSS,TWire.TOP_LEFT,TWire.TOP_RIGHT,TWire.TOP_BOTTOM]) offx=[0,2,3,1] offy=[1,0,2,3] End Method End Type Type TPiece_L_Left Extends TPiece Method Init() map[0,0]=TWire.Create([TWire.CROSS,TWire.TOP_RIGHT,TWire.BOTTOM_RIGHT,TWire.LEFT_RIGHT]) map[1,0]=TWire.Create([TWire.CROSS,TWire.BOTTOM_LEFT]) map[1,1]=TWire.Create([TWire.CROSS,TWire.TOP_BOTTOM]) map[1,2]=TWire.Create([TWire.CROSS,TWire.TOP_LEFT,TWire.TOP_RIGHT,TWire.TOP_BOTTOM]) offx=[1,2,2,1] offy=[1,1,2,2] End Method End Type Type TPiece_Square Extends TPiece Method Init() map[0,0]=TWire.Create([TWire.CROSS]) map[1,0]=TWire.Create([TWire.CROSS]) map[0,1]=TWire.Create([TWire.CROSS]) map[1,1]=TWire.Create([TWire.CROSS]) offx=[0,2,2,0] offy=[1,1,3,3] End Method End Type Type TPiece_Bar Extends TPiece Method Init() map[1,0]=TWire.Create([TWire.CROSS,TWire.BOTTOM_LEFT,TWire.BOTTOM_RIGHT,TWire.TOP_BOTTOM]) map[1,1]=TWire.Create([TWire.CROSS,TWire.TOP_BOTTOM]) map[1,2]=TWire.Create([TWire.CROSS,TWire.TOP_BOTTOM]) map[1,3]=TWire.Create([TWire.CROSS,TWire.TOP_LEFT,TWire.TOP_RIGHT,TWire.TOP_BOTTOM]) offx=[1,1,2,1] offy=[1,1,1,2] End Method End Type Type TFallingBlock Field w:TWire Field x:Int Field y:Double Field yi:Double Function Create:TFallingBlock(x:Int, y:Double) Local o:TFallingBlock=New TFallingBlock o.w=New TWire o.x=x o.y=y o.yi=0 Return o End Function Method Update() y:+yi yi=Max(4.0,yi+0.1) End Method Method Draw() SetColor(255,255,255) DrawImage(GameGFX.tile,Pit.X(x),y) SetColor(128,128,128) DrawImage(w.Image(),Pit.X(x),y) End Method End Type Type TWireListEnt Field w:TWire Field x:Int Field y:Int Function Create:TWireListEnt(x:Int, y:Int, w:TWire) Local o:TWireListEnt=New TWireListEnt o.x=x o.y=y o.w=w Return o End Function End Type Type TWireList Field list:TList Field col:Int Method New() list=CreateList() col=128 End Method Method Add(x:Int, y:Int, w:TWire) list.AddLast(TWireListEnt.Create(x,y,w)) End Method Method Draw:Int() col=Max(255,col+5) SetColor(col,col,col) For Local e:TWireListEnt=EachIn list DrawImage(e.w.Image(),Pit.X(e.x),Pit.Y(e.y)) Next If col<255 Return True Else For Local e:TWireListEnt=EachIn list Particles.AddImage(e.w.Image(),Pit.X(e.x),Pit.Y(e.y)) Next Return False EndIf End Method End Type Type TGameMap Field map:TWire[Pit.WIDTH,Pit.HEIGHT] Field trode_col:Int Field trode_coli:Int Field overflow:Int Field top:Int[Pit.WIDTH] Field drop:TList Field cx:Int Field cy:Int Field path:TList Method New() trode_col=0 trode_coli=1 overflow=False drop=CreateList() path=CreateList() CalcTop() cx=0 cy=Pit.HEIGHT-2 End Method Method CalcTop() For Local x:Int=0 Until Pit.WIDTH top[x]=-1 For Local y:Int=Pit.Height-1 To 0 Step -1 If Not map[x,y] top[x]=y Exit EndIf Next Next End Method Method AddRow(y:Int=-32) If BlockInteract() Then Return For Local x:Int=0 Until Pit.WIDTH drop.AddLast(TFallingBlock.Create(x,y)) Next End Method Method BlockInteract:Int() Return overflow Or path.Count() End Method Method FindPath:Int(l:TWireList, x:Int, y:Int, dir:Int) If y<0 Return False EndIf If x<0 Or x=Pit.WIDTH Or y=Pit.HEIGHT Return True EndIf Local w:TWire=map[x,y] If Not w Return False EndIf dir=w.Traverse(dir) If dir=TWire.DIR_NONE Return False EndIf l.Add(x,y,w) Return FindPath(l,x+TWire.DirX(dir),y+TWire.DirY(dir),dir) End Method Method FindLoop:Int(l:TWireList, ox:Int, oy:Int, x:Int, y:Int, dir:Int) If y<0 Or x<0 Or x=Pit.WIDTH Or y=Pit.HEIGHT Return False EndIf Local w:TWire=map[x,y] If Not w Return False EndIf dir=w.Traverse(dir) If dir=TWire.DIR_NONE Return False EndIf l.Add(x,y,w) x:+TWire.DirX(dir) y:+TWire.DirY(dir) If x=ox And y=oy And dir=TWire.DIR_LEFT Return True EndIf Return FindLoop(l,ox,oy,x,y,dir) End Method Method CheckWires() For Local y:Int=0 Until Pit.HEIGHT Local l:TWireList=New TWireList If FindPath(l,0,y,TWire.DIR_RIGHT) path.AddLast(l) EndIf l=New TWireList If FindPath(l,Pit.WIDTH-1,y,TWire.DIR_LEFT) path.AddLast(l) EndIf Next For Local x:Int=0 Until Pit.WIDTH Local l:TWireList=New TWireList If FindPath(l,x,Pit.HEIGHT-1,TWire.DIR_UP) path.AddLast(l) EndIf Next For Local x:Int=0 Until Pit.WIDTH For Local y:Int=0 Until Pit.HEIGHT If map[x,y] Local l:TWireList=New TWireList Select map[x,y].t Case TWire.CROSS, TWire.LEFT_RIGHT, TWire.TOP_LEFT, TWire.BOTTOM_LEFT If FindLoop(l,x,y,x,y,TWire.DIR_LEFT) path.AddLast(l) EndIf End Select EndIf Next Next End Method Method CursorLeft() If BlockInteract() Then Return cx=Max(0,cx-1) End Method Method CursorRight() If BlockInteract() Then Return cx=Min(Pit.WIDTH-2,cx+1) End Method Method CursorUp() If BlockInteract() Then Return cy=Max(0,cy-1) End Method Method CursorDown() If BlockInteract() Then Return cy=Min(Pit.HEIGHT-2,cy+1) End Method Method Rotate() If BlockInteract() Then Return Local done:Int=False For Local x:Int=0 To 1 For Local y:Int=0 To 1 If map[cx+x,cy+y] map[cx+x,cy+y].RotateRight() done=True EndIf Next Next If done Sound.Click() EndIf CheckWires() End Method Method Draw:Int() Local score:Int=0 SetColor(255,255,255) DrawImage(GameGFX.pit_bottomleft,Pit.X(-1),Pit.Y(Pit.HEIGHT)) DrawImage(GameGFX.pit_bottomright,Pit.X(Pit.WIDTH),Pit.Y(Pit.HEIGHT)) DrawImage(GameGFX.pit_top,Pit.X(-1),Pit.Y(0)) DrawImage(GameGFX.pit_top,Pit.X(Pit.WIDTH),Pit.Y(0)) SetColor(trode_col,trode_col,0) DrawImage(GameGFX.trode_left,Pit.X(-1),Pit.Y(0)) DrawImage(GameGFX.trode_right,Pit.X(Pit.WIDTH),Pit.Y(0)) For Local f:Int=0 Until Pit.WIDTH SetColor(255,255,255) DrawImage(GameGFX.pit_bottom,Pit.X(f),Pit.Y(Pit.HEIGHT)) SetColor(trode_col,trode_col,0) DrawImage(GameGFX.trode_bottom,Pit.X(f),Pit.Y(Pit.HEIGHT)) Next For Local f:Int=1 Until Pit.HEIGHT SetColor(255,255,255) DrawImage(GameGFX.pit_side,Pit.X(-1),Pit.Y(f)) DrawImage(GameGFX.pit_side,Pit.X(Pit.WIDTH),Pit.Y(f)) SetColor(trode_col,trode_col,0) DrawImage(GameGFX.trode_left,Pit.X(-1),Pit.Y(f)) DrawImage(GameGFX.trode_right,Pit.X(Pit.WIDTH),Pit.Y(f)) Next For Local x:Int=0 Until Pit.WIDTH For Local y:Int=0 Until Pit.HEIGHT If map[x,y] SetColor(255,255,255) DrawImage(GameGFX.tile,Pit.X(x),Pit.Y(y)) SetColor(128,128,128) DrawImage(map[x,y].Image(),Pit.X(x),Pit.Y(y)) EndIf Next Next If BlockInteract() For Local b:TFallingBlock=EachIn drop b.Draw() Next Local l:TEasyLink=TEasyLink.Create(path) Local check:Int=False While l.Value() Local b:TWireList=TWireList(l.Value()) If b.Draw() l.MoveNext() Else If Not overflow score:+b.list.Count() For Local e:TWireListEnt=EachIn b.list For Local y:Int=e.y To 1 Step -1 map[e.x,y]=map[e.x,y-1] Next map[e.x,0]=Null Particles.AddDust(Pit.X(e.x)+16,Pit.Y(e.y)+32) check=True Next EndIf l.Remove() EndIf Wend If check Sound.Path() CalcTop() CheckWires() EndIf Else Local hit:Int=False Local l:TEasyLink=TEasyLink.Create(drop) While l.Value() Local b:TFallingBlock=TFallingBlock(l.Value()) Local t:Int=Pit.Y(top[b.x])'-ImageHeight(GameGFX.tile) b.Update() If b.y>t If top[b.x]=-1 overflow=True b.y=t l.MoveNext() Else hit=True map[b.x,top[b.x]]=b.w Particles.AddDust(Pit.X(b.x)+16,Pit.Y(top[b.x])+32) l.Remove() EndIf Else b.Draw() l.MoveNext() EndIf Wend If hit And Not overflow Sound.Click() CheckWires() CalcTop() EndIf EndIf trode_col:+trode_coli If (trode_col=255 And trode_coli>0) Or (trode_col=200 And trode_coli<0) trode_coli=-trode_coli EndIf Return score End Method End Type