Sorry for the double post, but here is my entry:
----------------------------------
SOLUTION: Lock Picker
REMSTART
Lock Picking Program
Goals:
-No external Media
-Scalable DIfficulty
-# pins
-Precision Required
Created By: BN2 Productions
Started On: July 8th, 2012
Finished On: July 11th, 2012
REMEND
Randomize Timer()
SYNC ON : SYNC RATE 60
DISABLE ESCAPEKEY
REM Frequently Used Colors
DIM Colors(10)
REM Not Quite Black
Colors(0)=RGB(11,11,11)
REM Tumbler
Colors(1)=RGB(128,0,64)
REM Pin
Colors(2)=RGB(128,128,0)
REM Main Control Variable
MainState=0
REM Game Control Variable
GameState=0
DO
IF Inkey$()="`" then Break
REM Main Switchboard
SELECT MainState
CASE 0 : GOSUB S0_Startup : ENDCASE
CASE 1 : GOSUB S1_Setup : ENDCASE
CASE 2 : GOSUB S2_PrimaryLoop : ENDCASE
CASE Default : MainState=2 : ENDCASE
ENDSELECT
SYNC
LOOP
END
REM Intro SwitchBoard. Will Include menus/intro sequences, etc
S0_Startup:
REM Switch to Setup
SELECT InitState
CASE 0 : GOSUB S0_I0_IntroSeq : ENDCASE
CASE 1 : GOSUB S0_I1_Setup : ENDCASE
CASE 2 : GOSUB S0_I2_Menu : ENDCASE
CASE Default : InitState=0 : ENDCASE
ENDSELECT
RETURN
REM Main Menu Switchboard
S0_I2_Menu:
SELECT MainMenuState
CASE 0 : GOSUB S0_I2_MS0_Menu : ENDCASE
CASE 1 : GOSUB S0_I2_MS1_DiffMenu : ENDCASE
CASE Default : MainMenuState=0 : ENDCASE
ENDSELECT
RETURN
REM Difficulty SwitchBoard. This will divert to subroutines that change difficulty levels
S1_Difficulty:
SELECT Difficulty
CASE 0 : GOSUB S1_D0_Easy : ENDCASE
CASE 1 : GOSUB S1_D1_Medium : ENDCASE
CASE 2 : GOSUB S1_D2_Hard : ENDCASE
CASE 3 : GOSUB S1_D3_Impossible : ENDCASE
CASE Default : Gosub S1_D0_Easy : ENDCASE
ENDSELECT
RETURN
REM Primary Game Switchboard
S2_PrimaryLoop:
Select GameState
CASE 0 : GOSUB S2_G0_PickLock : ENDCASE
CASE 1 : GOSUB S2_G1_GameMenu : ENDCASE
CASE 2 : GOSUB S2_G2_Completed : ENDCASE
CASE 3 : GOSUB S2_G3_Dialog : ENDCASE
CASE Default : GameState=0 : ENDCASE
ENDSELECT
RETURN
REM Pause Menu SwitchBoard
REM Menu: Return TO Menu, New Lock, Cancel
REM Select was used for further expansion later (Options menu and others)
S2_G1_GameMenu:
SELECT MenuState
CASE 0 : GOSUB S2_G1_M0_Menu : ENDCASE
CASE Default : MenuState=0 : ENDCASE
ENDSELECT
RETURN
REM Dialog Box SwitchBoard
S2_G3_Dialog:
SELECT DialogStatus
CASE 1 : GOSUB S2_G3_D1_Jammed : ENDCASE
CASE 2 : GOSUB S2_G3_D2_Released : ENDCASE
Case Default : GameState=0 : ENDCASE
ENDSELECT
RETURN
REM This would be any kind of logos or intro vids or something. Not using anything currently
S0_I0_IntroSeq:
InitState=1
RETURN
REM Intro Menu. Options are: New Game->Difficulty Menu,Instructions,Exit
S0_I1_Setup:
REM Make the lock image
INK RGB(65,65,65),0
LockRadius=100
FOR r=0 to LockRadius
Ellipse 100,100,r,LockRadius
NEXT I
INK RGB(127,127,127),0
LockRadius=90
FOR r=0 to LockRadius
Ellipse 100,100,r,LockRadius
NEXT r
INK 0,0
BOX 100,20,120,100
BOX 80,100,100,180
FOR X=105 to 115
ColChange=X-105
ColChange#=((ColChange+.)/(10.))
ColChange#=150*Sin(ColChange#*360)
INK RGB(65+ColChange#,65+ColChange#,0),0
Box X,22,X+1,80
NEXT X
GET IMAGE 300,0,0,200,200
CLS
REM Make the option Selector
INK RGB(55,55,55),0
FOR X=1 to 20
Height=20-(2*X)
BOX X,X,X+1,X+Height
NEXT X
GET IMAGE 301,0,0,21,21
CLS
REM Make The Menu Image
INK RGB(200,200,200),0
BOX 0,0,150,140
SET TEXT SIZE 16
INK 0,0
CENTER TEXT 75,18,"New Game"
CENTER TEXT 75,58,"Instructions"
CENTER TEXT 75,98,"Exit"
GET IMAGE 302,0,0,151,201
cls
REM Make the difficulty Menu
INK RGB(200,200,200),0
Box 0,0,300,200
INK 0,0
SET TEXT SIZE 10
TEXT 5,5,"Select Difficulty:"
CENTER TEXT 150,180,"Press [ENTER] to continue"
SET TEXT SIZE 16
CENTER TEXT 60,30,"Easy"
CENTER TEXT 200,30,"Medium"
CENTER TEXT 60,130,"Hard"
CENTER TEXT 200,130,"Impossible"
GET IMAGE 303,0,0,300,200
cls
REM Make the difficulty selector ellipse
INK rgb(255,255,0),0
ellipse 61,21,60,20
ellipse 61,21,59,19
GET IMAGE 304,0,0,122,42
cls
INK RGB(255,255,255),0
InitState=2
RETURN
REM This is the Subroutine for the Initial Menu
S0_I2_MS0_Menu:
GOSUB S0_I2_MS0_TakeInput
GOSUB S0_I2_MS0_RenderMenu
RETURN
REM Draw the menu itself
S0_I2_MS0_RenderMenu:
SET TEXT SIZE 42
PASTE IMAGE 300,350,50
PASTE IMAGE 302,245,250
PASTE IMAGE 301,250,(250+19)+(40*MainSelected),0
CENTER TEXT 320,0,"Lock Picker!"
RETURN
REM Facilitate IO
S0_I2_MS0_TakeInput:
REM Choose based on the mouse position
MainSelected=(MouseY()-250)/40
IF MainSelected<0 then MainSelected=0
IF MainSelected>2 then MainSelected=2
REM IF the user selects something
IF MouseClick()=1 then MainMenuClick=1
IF MouseClick()=0 AND MainMenuClick=1
MainMenuClick=0
SELECT MainSelected
CASE 0 : MainMenuState=1 : ENDCASE
CASE 1 : GOSUB S0_I2_MS0_Instructions : ENDCASE
CASE 2 : END : ENDCASE
ENDSELECT
ENDIF
RETURN
REM Difficulty Select Menu
S0_I2_MS1_DiffMenu:
CLS
PASTE IMAGE 303,170,140
IF MouseClick()=1
Mx=MouseX()-170 : My=MouseY()-140
REM Check for easy
If Mx<150 AND My<100 then Difficulty=0
REM CHeck for Medium
IF Mx>150 AND My<100 then Difficulty=1
REM CHeck for Hard
IF Mx<150 AND My>100 then Difficulty=2
REM CHeck for Impossible
IF Mx>150 AND My>100 then Difficulty=3
ENDIF
IF Difficulty=0 then PASTE IMAGE 304,170,160,0
IF Difficulty=1 then PASTE IMAGE 304,310,160,0
IF Difficulty=2 then PASTE IMAGE 304,170,260,0
IF Difficulty=3 then PASTE IMAGE 304,310,260,0
REM Check for the Enter key being pressed(time to start)
IF ReturnKey()=1 then DiffMenu=1
REM IF the user pressed enter and is now not pressing it, begin the game
IF ReturnKey()=0 AND DiffMenu=1
DiffMenu=0
MainMenuState=0
InitState=0
MainState=1
ENDIF
RETURN
REM this is an instructions slide.
S0_I2_MS0_Instructions:
GOSUB S1_Setup
MainState=0
CLS
GOSUB S2_G0_RenderLock
SET TEXT SIZE 12
ty=130
Text 0,0,"Welcome to Lock Pick! The goal of the game is to pick the lock!"
TEXT 100,75,"Try to separate the pins and tumblers!"
TEXT 100,95,"The tumblers will remain on top of this line"
TEXT 100,115,"and the pins below, if a picking is successful."
text 0,300,"Watch your tension! If you apply too much, the lock will jam!"
text 0,320,"If you apply too little, the tumblers will fall back down!"
text 400,ty,"Press [SPACE] to push the pin up."
text 400,ty+20,"Hold it at the separating line for a"
TEXT 400,ty+40,"short time in order to cause the pin"
text 400,ty+60, "and tumbler to separate."
text 400,ty+100,"Use the Left and Right arrow"
text 400,ty+120,"to control tension. Your"
text 400,ty+140,"maximum and minimum tensions"
text 400,ty+160,"are marked on the bar by two lines."
sync
wait key
cls
RETURN
REM Setup. Generates images for lock and initializes variables
S1_Setup:
REM Control Data
MaxLocks=1
TumbleAttr=4
PinSpeed#=4.0
PinAccel#=1.2
TensionBalanceX#=0.
GOSUB S1_Difficulty
REM Initialize or Reset control variables
REM Locks#(Lock Num,0.NumTumbles/TumbleNumber,1.Size/2.Position/3.Completed/4.Velocity#
DIM Locks#(MaxLocks,MaxTumbles,TumbleAttr)
CompletedPins=0
REM Display "Floor"
Floor=240
REM Display Ceiling (where the barrel turns)
Ceiling=Floor-(70+Window+5)
REM Populate the Locks array with data
Lock=1
Locks#(Lock,0)=Rnd(MaxTumbles-MinTumbles-1)+MinTumbles
For I=1 to MaxTumbles
Locks#(Lock,I,1)=RND(50)+20
Locks#(Lock,I,2)=0
NEXT I
REM Generate the Tumbler image
INK Colors(1),0
Box 0,0,15,75
GET IMAGE 1,0,0,15,75
CLS
REM Generate Pin images
INK Colors(2),0
FOR I=1 to Locks#(Lock,0)
BOX 0,0,15,Locks#(Lock,I,1)
GET IMAGE I+1,0,0,15,Locks#(Lock,I,1)
CLS
NEXT I
REM Generate Indicator Image
INK 65535,0
Line 0,5,5,0
Line 5,0,10,5
Get Image 65535,0,0,10,5
CLS
ActivePin=Locks#(Lock,0)
REM Generate the Tension Balance
INK RGB(255,255,255),0
for I=1 to 10
BOX I-1,0,I,I
next I
for I=10 to 20
box I-1,0,I,20-I
next i
get image 65534,0,0,20,10
cls
FOR I=1 to 300
Scale#=(I+.)/300.
G#=(1-Cos(Scale#*360.0))*.5
G=G#*255
R=255-G
INK RGB(R,G,0),0
BOX I-1,0,I,20
NEXT I
get image 65533,0,0,300,20
REM Make Jammed Button
INK RGB(128,0,0),0
BOX 0,0,300,100
FOR I=1 to 10
INK RGB(128+(I*10),0,0),0
Width=300-(2*I)
Box I,I,I+Width,I+1
Box I,100-I,I+Width,101-I
NEXT I
FOR I=1 to 10
INK RGB(128+(I*10),0,0),0
Height=100-(2*I)
Box I,I,I+1,I+Height
Box 300-I,I,301-I,I+Height
NEXT I
INK RGB(255,0,0),0
BOX 10,10,290,90
SET TEXT SIZE 30
INK 0,0
CENTER TEXT 152,27,"JAMMED"
INK RGB(255,255,255),0
CENTER TEXT 150,25,"JAMMED"
Set Text Size 10
Center Text 150,65,"(Click to Release Tension)"
GET IMAGE 65532,0,0,300,100
cls
REM Make the Completion Picture
INK RGB(0,0,128),0
BOX 0,0,400,80
INK 0,0
BOX 1,1,399,79
INK RGB(255,255,255),0
SET TEXT SIZE 42
Center TEXT 200,0,"Lock Picked!"
SET TEXT SIZE 10
CENTER TEXT 200,60,"(Press [ENTER] to continue)"
GET IMAGE 65531,0,0,401,81
CLS
REM Pause Menu Images
INK RGB(0,0,128),0
BOX 0,0,200,300
FOR I=1 to 10
INK RGB(0,0,128+(I*10)),0
Width=200-(2*I)
Box I,I,I+Width,I+1
Box I,300-I,I+Width,301-I
NEXT I
FOR I=1 to 10
INK RGB(0,0,128+(I*10)),0
Height=300-(2*I)
Box I,I,I+1,I+Height
Box 200-I,I,201-I,I+Height
NEXT I
INK RGB(0,0,255),0
BOX 10,10,190,290
INK 0,0
SET TEXT SIZE 24
CENTER TEXT 102,12,"Menu"
SET TEXT SIZE 16
Center Text 102,77,"Return to Menu"
Center Text 102,127,"New Lock"
Center Text 102,177,"Cancel"
INK RGB(255,255,255),0
SET TEXT SIZE 24
CENTER TEXT 100,10,"Menu"
SET TEXT SIZE 16
Center Text 100,75,"Return to Menu"
Center Text 100,125,"New Lock"
Center Text 100,175,"Cancel"
GET IMAGE 100,0,0,200,300
INK RGB(255,255,0),0
Center Text 100,75,"Return to Menu"
GET IMAGE 101,0,0,200,300
Center Text 100,125,"New Lock"
INK RGB(255,255,255),0
Center Text 100,75,"Return to Menu"
GET IMAGE 102,0,0,200,300
Center Text 100,125,"New Lock"
INK RGB(255,255,0),0
Center Text 100,175,"Cancel"
GET IMAGE 103,0,0,200,300
CLS
REM Background
Distance=2*(Floor-Ceiling)
INK RGB(128,64,0),0
BOX 0,0,25*Locks#(Lock,0),479
INK RGB(127,127,127),0
BOX 0,Ceiling-80,25*Locks#(Lock,0),Ceiling
BOX 0,(Ceiling+Distance),25*Locks#(Lock,0),(Ceiling+Distance)+80
For I=1 to Distance
D#=(I+.)/(Distance+.)
Scale#=.5*(Cos(D#*360)+1)
Scale#=1-Scale#
INK RGB(50+(150*Scale#),50+(150*Scale#),50+(150*Scale#)),0
Box 0,(Ceiling+Distance)-I,25*Locks#(Lock,0),(Ceiling+Distance)+1-I
NEXT I
GET IMAGE 104,0,0,25*Locks#(Lock,0),480
REM Switch to Primary Loop
INK RGB(255,255,255),0
MainState=2
RETURN
REM Set the variables for the easy difficulty
S1_D0_Easy:
REM These Control Difficulty
MaxTumbles=5
MinTumbles=2
TimeToHold=300
Window=15
SlideSpeed=1
MaxTension=250
MinTension=50
RETURN
REM Set variables for Medium Difficulty
S1_D1_Medium:
REM These Control Difficulty
REM Maximum Number of tumblers
MaxTumbles=7
REM Minimum Number of tumblers
MinTumbles=3
REM How many miliseconds the user has to hold the pin in place
TimeToHold=500
REM How close the user has to be to the exact spot
Window=15
REM How quickly the Tension Meter indicator moves
SlideSpeed=1
REM The maxium allowable tension before jam
MaxTension=250
REM The minimum allowable tension before pin reset
MinTension=50
RETURN
S1_D2_Hard:
REM These Control Difficulty
MaxTumbles=10
MinTumbles=6
TimeToHold=300
Window=13
SlideSpeed=2
MaxTension=250
MinTension=50
RETURN
S1_D3_Impossible:
REM These Control Difficulty
MaxTumbles=20
MinTumbles=8
TimeToHold=400
Window=10
SlideSpeed=3
MaxTension=250
MinTension=50
RETURN
REM The actual game. Shows the lock, takes input, etc
S2_G0_PickLock:
Time=Timer()
REM Take User Input
GOSUB S2_G0_Controls
REM Handle Lock Mechanics
GOSUB S2_G0_Physics
REM Handle Tension Mechanics
GOSUB S2_G0_Tension
REM Display The Lock/UI/Gfx
GOSUB S2_G0_RenderLock
REM Check for completion
IF CompletedPins=Locks#(Lock,0)
GameState=2
ENDIF
RETURN
REM Draws everything needed
S2_G0_RenderLock:
CLS
REM Display the floor and the ceiling
PASTE IMAGE 104,20,0
REM Display the Pins and Tumblers
FOR X=1 to Locks#(Lock,0)
Size=Locks#(Lock,X,1)
Position=Locks#(Lock,X,2)
IF Locks#(Lock,X,3)=1
PASTE IMAGE 1,X*20,Ceiling-75
ELSE
PASTE IMAGE 1,X*20,Floor-Position-Size-75
ENDIF
PASTE IMAGE X+1,X*20,Floor-Position-Size+5
REM Draw the Indicator for the active Pin
IF X=ActivePin
PASTE IMAGE 65535,X*20,Floor-Position
ENDIF
NEXT X
REM Paste the Images for the Tension Balance
LINE MinTension,Floor+25,MinTension,Floor+55
LINE MaxTension,Floor+25,MaxTension,Floor+55
Paste Image 65534,TensionBalanceX#,Floor+20,0
PASTE IMAGE 65533,10,Floor+30
RETURN
REM Take User Input and act accordingly
S2_G0_Controls:
REM Manually control the tension balance
IF RightKey()=1 : Slide=1*SlideSPeed : ENDIF
IF LeftKey()=1 : Slide=-1*SlideSpeed : ENDIF
IF MouseClick()=1 OR SpaceKey()=1
IF Slide=0 then Slide=1
Locks#(Lock,ActivePin,4)=PinSpeed#
ENDIF
IF KeyState(1)=1
GameState=1
ENDIF
RETURN
S2_G0_Physics:
FOR Pin=1 to Locks#(Lock,0)
REM Control Falling of the pins
Locks#(Lock,Pin,4)=Locks#(Lock,Pin,4)-PinAccel#
Locks#(Lock,Pin,2)=Locks#(Lock,Pin,2)+Locks#(Lock,Pin,4)
REM Control Limits of the Pin Motion
IF Locks#(Lock,Pin,2)<0 then Locks#(Lock,Pin,2)=0
IF Locks#(Lock,Pin,3)=1
MaxPinHeight=Floor-Ceiling+Locks#(Lock,Pin,1)
ELSE
MaxPinHeight=Floor-Ceiling-10
ENDIF
IF Locks#(Lock,Pin,2)>MaxPinHeight then Locks#(Lock,Pin,2)=MaxPinHeight
REM Check if the active Pin is being analyzed
IF Pin=ActivePin
REM Check to see if the player has placed the pin at the appropriate location (within WINDOW of the split line)
RangeFlag=inRange(Locks#(Lock,Pin,2),Floor-Ceiling+Locks#(Lock,Pin,1),Window)
REM If within range for the first time, note the starting time
IF RangeFlag=1
IF StartTimer=0
StartTimer=Time
ENDIF
ELSE
REM If not within range, cancel the stored value
StartTimer=0
ENDIF
REM If the StartTimer is set (The player has the pin in the right spot) and has kept it there for the right amount of time
` IF Locks#(Lock,Pin,2)>=(Floor-Ceiling+Locks#(Lock,Pin,1)) AND (Time-StartTimer)>=TimeToHold AND StartTimer<>0
IF (Time-StartTimer)>=TimeToHold AND StartTimer<>0
REM Reset the Timer
StartTime=0
REM Set the pin to completed
Locks#(Lock,Pin,3)=1
REM Increase the completed pin counter (probably unnecessary)
INC CompletedPins
REM Move on to the next pin
dec ActivePin
ENDIF
ENDIF
NEXT Pin
RETURN
REM Controls the Tension balance
S2_G0_Tension:
REM Move the slider in the appropriate direction (Set within controls)
IF Slide<>0
INC TensionBalanceX#,Slide
IF TensionBalanceX#<0 then TensionBalanceX#=0
IF TensionBalanceX#>300 then TensionBalanceX#=300
ENDIF
REM If the player has applied too much tension
IF TensionBalanceX#>MaxTension
REM Go to the Dialog Switchboard
GameState=3
REM Go to Jammed sequence
DialogStatus=1
ENDIF
REM IF the player has not applied enough tension
IF TensionBalanceX#<MinTension
REM Go to the dialog switchboard
GameState=3
REM Go to released sequence
DialogStatus=2
ENDIF
RETURN
REM Once the lock is solved, do this and then go back to setup
S2_G2_Completed:
REM Paste the Success Image
PASTE IMAGE 65531,120,200
IF ReturnKey()=1
REM Reset the State. Gamestate=>LockPick and MainState=>Setup
GameState=0
MainState=1
ENDIF
RETURN
REM When the Player Applies too much tension, jam the pins forcing them to release tension.
S2_G3_D1_Jammed:
PASTE IMAGE 65532,170,190
IF MouseClick()=1
GameState=0
TensionBalanceX#=0.
ENDIF
RETURN
REM When the player doesn't apply enough tension
S2_G3_D2_Released:
FOR I=1 to Locks#(Lock,0)
Locks#(Lock,I,3)=0
NEXT I
GameState=0
ActivePin=Locks#(Lock,0)
CompletedPins=0
IF KeyState(1)=1
GameState=1
ENDIF
RETURN
REM Main Game Menu
S2_G1_M0_Menu:
REM Position Variables
MenuY=90
MenuX=220
REM Determine Which option the mouse is hovering over and Paste the appropriate image
Selected=((MouseY()-(MenuY+75))/50)+1
IF Selected<0 then Selected=0
IF Selected>3 then Selected=3
PASTE IMAGE 100+selected,MenuX,MenuY
REM Check for mouse click
IF MouseClick()=1
ClickedOn=Selected
MouseDown=1
ENDIF
REM IF the mouse was clicked and is no longer being clicked, determine what to do
IF MouseClick()=0 AND MouseDown=1
MouseDown=0
SELECT ClickedON
REM Return to Menu
CASE 1 : GOSUB S2_G1_M0_C1_Restart : ENDCASE
REM New Lock
CASE 2 : GOSUB S2_G1_M0_C2_NewLock : ENDCASE
REM Cancel
CASE 3 : GOSUB S2_G1_M0_C3_Cancel : ENDCASE
CASE Default : SYNC : ENDCASE
ENDSELECT
ClickedOn=0
ENDIF
RETURN
REM Restart Game ->This goes back to the start menu
S2_G1_M0_C1_Restart:
GameState=0
MainState=0
InitState=2
CLS
RETURN
REM Generate a New Lock of the same difficulty
S2_G1_M0_C2_NewLock:
CLS
GameState=0
MainState=1
RETURN
REM Return to the game
S2_G1_M0_C3_Cancel:
GameState=0
RETURN
REM This will check if Variable# is within Range of Comparator#
FUNCTION inRange(Variable#,Comparator#,Range)
RetVal=0
IF abs(Variable#-Comparator#)<Range
RetVal=1
ENDIF
ENDFUNCTION RetVal
REM This will check if a point (Px,Py) is within the box given by X1,Y1,X2,Y2
FUNCTION InBox(Px,Py,X1,Y1,X2,Y2)
RetVal=0
IF Px>=X1 AND Px<=X2
IF Py>=Y1 AND Py<=Y2
RetVal=1
ENDIF
ENDIF
ENDFUNCTION RetVal
----------------------------------
This is seriously one of the most beautifully formatted programs I have ever written (mostly because I lined up the SELECT cases). I was also trying out a new(ish) structure method that worked out quite well. The switchboards control what part of the program is done. I've done similar things, but never to the full extent like this one (where everything is controlled via switchboards rather than eventually splitting off).
Comments, concerns, and insults are appreciated.
[EDIT] Removed line 248:enable escapekey . It was in there for debugging purposes and I forgot to remove it.
Great Quote:
"Time...LINE??? Time isn't made out of lines...it is made out of circles. That is why clocks are round!" -Caboose