' Particle Pinch ' ' Copyright 2005 Ian Cowburn ' ' $Id$ ' Strict Import noddybox.vector Import noddybox.bitmapfont Import noddybox.simplegui Import noddybox.algorithm Import "types.bmx" Import "level.bmx" Import "game.bmx" Function LevelDesigner() DoDesigner() End Function Private ' **** Types ' Type TDesObj Abstract Field selsize:Int Field removable:Int Function Create:TDesObj(x:Int, y:Int) Abstract Function CreateFromLevel:TDesObj(o:Object) Abstract Method Draw() Abstract Method DrawSelect() Abstract Method MouseOver:Int(x:Int, y:Int) Abstract Method Drag(x:Int, y:Int) Abstract Method Edit() Abstract Method Save(lev:TLevel) Abstract Method SetInfo(w:TLabel) Abstract Method Snap() Abstract Method DrawSelBox(x:Int, y:Int) Local x1:Int=x-selsize Local y1:Int=y-selsize Local x2:Int=x+selsize Local y2:Int=y+selsize SetColor(255,255,255) DrawLine(x1,y1,x2,y1) DrawLine(x2,y1,x2,y2) DrawLine(x2,y2,x1,y2) DrawLine(x1,y2,x1,y1) End Method Method InSelBox(px:Int, py:Int, x:Int, y:Int) Local x1:Int=x-selsize Local y1:Int=y-selsize Local x2:Int=x+selsize Local y2:Int=y+selsize Return px>=x1 And px<=x2 And py>=y1 And py<=y2 End Method Method DrawCoord(x:Int, y:Int) GameGFX.font.Draw(x+","+y,0,0) End Method Method DoubleString:String(v:Double) Local s:String=v If s.Find(".")<>-1 While s.length>1 And s[s.length-1]=Asc("0") s=s[..s.length-1] Wend While s.length>1 And s[s.length-1]=Asc(".") s=s[..s.length-1] Wend EndIf Return s End Method Method BoolString:String(b:Int) If b Return "Yes" Else Return "No " EndIf End Method Method CalcSnap:Int(p:Int) Return (p+Designer.GRID/2-1)/Designer.GRID*Designer.GRID-1 End Method End Type Type TDesGrav Extends TDesObj Field g:TGravPoint Function Create:TDesObj(x:Int, y:Int) Local o:TDesGrav=New TDesGrav o.g=New TGravPoint o.g.x=x o.g.y=y o.g.friendly=False o.g.mass=25 o.g.repel=False o.selsize=3 o.removable=True Return o End Function Function CreateFromLevel:TDesObj(o:Object) Local lp:TGravPoint=TGravPoint(o) Local no:TDesGrav=New TDesGrav no.g=New TGravPoint no.g.x=lp.x no.g.y=lp.y no.g.friendly=lp.friendly no.g.mass=lp.mass no.g.repel=lp.repel no.selsize=3 no.removable=True Return no End Function Method Draw() SetColor(255,255,255) If g.friendly DrawImage(GameGFX.collector,g.x,g.y) Else DrawImage(GameGFX.star,g.x,g.y) EndIf End Method Method DrawSelect() DrawSelBox(g.x,g.y) End Method Method MouseOver:Int(x:Int, y:Int) Return InSelBox(x,y,g.x,g.y) End Method Method Drag(x:Int, y:Int) DrawCoord(x,y) g.x=x g.y=y End Method Method Edit() Designer.md_friendly.checked = g.friendly Designer.md_invert.checked = g.repel Designer.md_mass.SetFloat(g.mass) If GUIDialog(Designer.mdialog,Designer.md_ok,Designer.md_cancel,GameGFX.pointer) g.friendly = Designer.md_friendly.checked g.repel = Designer.md_invert.checked g.mass = Designer.md_mass.text.ToDouble() EndIf End Method Method Save(lev:TLevel) lev.grav.AddLast(g) End Method Method SetInfo(w:TLabel) w.text="Friendly:" + BoolString(g.friendly) + " Inverse gravity:" + BoolString(g.repel) + " Mass: " + DoubleString(g.mass) End Method Method Snap() g.x=CalcSnap(g.x) g.y=CalcSnap(g.y) End Method End Type Type TDesShip Extends TDesObj Field x:Int Field y:Int Field a:Int Function Create:TDesShip(x:Int, y:Int) Local o:TDesShip=New TDesShip o.x=x o.y=y o.a=0 o.selsize=8 o.removable=False Return o End Function Function CreateFromLevel:TDesObj(o:Object) Local lp:TLevel=TLevel(o) Local no:TDesShip=New TDesShip no.x=lp.startx no.y=lp.starty no.a=lp.startang no.selsize=8 no.removable=False Return no End Function Method Draw() SetColor(255,255,255) SetRotation(a) DrawImage(GameGFX.ship,x,y) SetRotation(0) End Method Method DrawSelect() DrawSelBox(x,y) End Method Method MouseOver:Int(x:Int, y:Int) Return InSelBox(x,y,Self.x,Self.y) End Method Method Drag(x:Int, y:Int) DrawCoord(x,y) Self.x=x Self.y=y End Method Method Edit() Designer.sd_ang.SetFloat(a) If GUIDialog(Designer.sdialog,Designer.sd_ok,Designer.sd_cancel,GameGFX.pointer) a = Designer.sd_ang.text.ToInt() EndIf End Method Method Save(lev:TLevel) lev.startx=x lev.starty=y lev.startang=a End Method Method SetInfo(w:TLabel) w.text="Angle: " + a End Method Method Snap() x=CalcSnap(x) y=CalcSnap(y) End Method End Type Type TDesPoint Extends TDesObj Field l:TPointLine Field over_p1:Int Function Create:TDesObj(x:Int, y:Int) Local o:TDesPoint=New TDesPoint o.over_p1=False o.l=New TPointLine o.l.x1=x o.l.y1=y o.l.y2=y If xNull sel.SetInfo(Designer.info) sel.DrawSelect() EndIf If drag sel.Drag(x,y) EndIf If MouseDown(2) If sel<>Null If sel.removable Select GUIMenu("Object Menu",["Edit","Snap to grid","Delete"],x,y,GameGFX.pointer) Case 0 sel.Edit() Case 1 sel.Snap() Case 2 Designer.obj.Remove(sel) End Select Else Select GUIMenu("Object Menu",["Edit","Snap to grid"],x,y,GameGFX.pointer) Case 0 sel.Edit() Case 1 sel.Snap() End Select EndIf Else Select GUIMenu("Create Menu",["Create Gravity Point","Create Particle Line","Edit Level Settings"],x,y,GameGFX.pointer) Case 0 Designer.obj.AddLast(TDesGrav.Create(x,y)) Case 1 Designer.obj.AddLast(TDesPoint.Create(x,y)) Case 2 Designer.EditLevelSettings() End Select EndIf sel=Null EndIf If Not drag If MouseDown(1) And sel<>Null drag=True EndIf Else If Not MouseDown(1) drag=False EndIf EndIf SetColor(255,255,255) DrawImage(GameGFX.pointer,MouseX(),MouseY()) Flip Wend End Function ' **** Utils ' Function ListAt:TLink(l:TList, i:Int) Local tl:TLink=l.FirstLink() While i>0 And tl<>Null i:-1 tl=tl.NextLink() Wend Return tl End Function ' **** Callbacks ' Function HideCallback(w:TWidget) Local c:TCheckbox=TCheckbox(w) Designer.gui.SetEnable(Not c.checked) c.enabled=True End Function Function QuitCallback(w:TWidget) Designer.done=GUIYesNo("Quit back to the|main menu of Particle Pinch?",GameGFX.pointer) End Function Function TestCallback(w:TWidget) Designer.SaveLevel() Local g:TGame=TGame.Create(Designer.level) Local res:Int=TGame.LEVEL_NOTOVER While res<>TGame.LEVEL_FINISHED And res<>TGame.LEVEL_CANCELLED Cls res=g.Play() Flip Wend FlushKeys() End Function Function CheckCallback(w:TWidget) Designer.SaveLevel() Local s:String="The following problems were found:" Local ok:Int=True Local m:TList=CreateList() Local p:TList=CreateList() Designer.level.CreatePlayfield(m,p) If (m.Count()+1)>MAX_GRAV ok=False s:+"| * Too many masses (combining placed and ship)" EndIf Local friends:Int=False For Local gp:TMass=EachIn m If gp.friend friends=True EndIf Next If Not friends ok=False s:+"| * No collector masses (no particles can be captured by player)" EndIf If p.Count()>MAX_POINT ok=False s:+"| * Too many points (" + p.Count() + " -- maximum is " + MAX_POINT + ")" EndIf If p.Count()=0 ok=False s:+"| * No particles to collect" EndIf If Not ok GUINotify(s,GameGFX.pointer) Else GUINotify("Level is valid",GameGFX.pointer) EndIf End Function Function LoadCallback(w:TWidget) Try If GUIYesNo("Overwrite current level set?",GameGFX.pointer) Local Load:TLevelSet=TLevelSet.Load(Designer.fname_txt.text) Designer.levelset=Load Designer.levelindex=0 Designer.levnum.maxval=Designer.levelset.level.Count()-1 Designer.levnum.value=0 Designer.LoadLevel() EndIf Catch e:TLevelException GUINotify("Failed to load '" + Designer.fname_txt.text + "'||"+e.message,GameGFX.pointer) EndTry End Function Function SaveCallback(w:TWidget) Designer.SaveLevel() If Not Designer.levelset.Save(Designer.fname_txt.text) GUINotify("Failed to save '" + Designer.fname_txt.text + "'",GameGFX.pointer) Else GUINotify("'" + Designer.fname_txt.text + "' saved OK",GameGFX.pointer) EndIf End Function Function NewCallback(w:TWidget) If GUIYesNo("Lose current level set?",GameGFX.pointer) Designer.levelindex=0 Designer.levnum.maxval=0 Designer.levelset=New TLevelSet Designer.levelset.level.AddLast(New TLevel) Designer.fname_txt.text="Untitled.ppinch" Designer.LoadLevel() EndIf End Function Function LevelNumberCallback(w:TWidget) Local c:TNumberInt=TNumberInt(w) Designer.SaveLevel() Designer.levelindex=c.value Designer.LoadLevel() End Function Function AddLevelCallback(w:TWidget) Designer.levelset.level.AddLast(New TLevel) Designer.levnum.maxval=Designer.levelset.level.Count()-1 End Function Function InsertLevelCallback(w:TWidget) Designer.SaveLevel() Local l:TLink=ListAt(Designer.levelset.level,Designer.levelindex) Designer.levelset.level.InsertBeforeLink(New TLevel,l) Designer.levnum.maxval=Designer.levelset.level.Count()-1 Designer.LoadLevel() End Function Function DeleteLevelCallback(w:TWidget) If Designer.levelset.level.Count()<2 GUINotify("Must have at least one level!",GameGFX.pointer) Return EndIf If GUIYesNo("Delete this level?||"+Designer.levname_txt.text,GameGFX.pointer) ListAt(Designer.levelset.level,Designer.levelindex).Remove() If Designer.levelindex>=Designer.levelset.level.Count() Designer.levelindex:-1 Designer.levnum.value=Designer.levelindex EndIf Designer.levnum.maxval=Designer.levelset.level.Count()-1 Designer.LoadLevel() EndIf End Function