Ok,
the
first demo of this card game was rather simple, so we're going to add
a few features to this one to spice it up a bit.
The first thing to add is a bit of sound, of course, so whenever a new card is drawn on the screen, we'll play a simple .wav file, to give the user an audio cue. Since this will be played from a number of locations, I simply wrapped the playSound() function into my own sound_off() procedure.
The previous game demo was also a bit slow, since sleep() can only be called with a minimum value of one second, so I've written a simple delay loop procedure, which I call stall(), so I can get some shorter time delays into the game's behavior.
Next, the most important new feature, is, of course adding mouse input to the game, so that cards to be played can be selected by a simple mouse click, instead of the keyboard. This will be done in the on_bitmap() routine, which is called whenever the mouse is on our program's window, by an onMouse[] event handler.
Since this routine will be called whenever the mouse is moved over our game's window, the first thing we do is check if the left mouse button is pressed down. If it's not, the routine falls through, without doing all the other checks, as quickly as possible.
The next big 'stack' of if statements check to see where the mouse is, when it's been clicked, to determine which card the user has selected.
Here's an explanation of how
this is done.
The first things to keep in mind, are the dimensions
of the 'dealpix' pixmap, and it's location in the client area of our
program's window.
Since the pixmap is 131 by 98 pixels in size,
and it's top-left corner is always located in our client area at 80
by 140 pixels, it's bottom-right corner is located at 210 by 237
pixels.
These two locations on our
window, establish the 'total' zone we check for, when the left mouse
button is clicked.
To determine which of four cards is being
selected, keep in mind that the cards are overlapped so that only 20
pixels for each card are shown. This divides our 'total' area into 4
zones on the left side, from which we can calculate which card has
been clicked, leaving a remainder on the far right for the last card,
since it's shown fully.
While the above description shows how the bitmap is split up for mouse selection, you'll note that I've used greater-than, ( > ), and less-than, ( < ), ( ..and +1, -1 ) in my calculations. This was done to create a dead-zone within the bitmap that would *not* be sensitive to selection. The 3 pixels 'between' the cards, for example, won't react to a mouse click so the user can't select the wrong card 'accidentally".
-- card-trix-2.exw
--
include win32lib.ew
without warning
sequence deck, cards
deck = {1, 2, 3, 4, 5}
cards = {"Ten", "Jack", "Queen", "King", "Ace"}
integer ready, picked, myscore, yourscore, cheat
ready = 0
myscore = 0
yourscore = 0
cheat = 0
----
constant
msg0 = "...dealing cards.",
msg1 = "...draw a card ( 1 to 4 )",
msg2 = "You Lose : )",
msg3 = "You Win !!",
MyWin = create( Window, "Card-Trix-2", 0, 10, 10, 300, 320, 0 ),
yours = create( LText, "You", MyWin, 5, 10, 100, 20, 0 ),
mine = create( RText, "Me", MyWin, 180, 10, 100, 20, 0 ),
menuBar = create( Menu, "&Program", MyWin, 0, 0, 0, 0, 0 ),
menuCheat = create( MenuItem, "&Cheat", menuBar, 0, 0, 0, 0, 0 ),
menuOut = create( MenuItem, "E&xit", menuBar, 0, 0, 0, 0, 0 ),
statBar = create( StatusBar, "", MyWin, 0, 0, 0, 0, 0 ),
deckPix = create( Pixmap, "", 0, 0, 0, 421, 71, 0 ),
dealPix = create( Pixmap, "", 0, 0, 0, 131, 98, 0 ),
playPix = create( Pixmap, "", 0, 0, 0, 149, 98, 0 ),
myTimer = 1,
myTimer2 = 2
----
sequence temp_wins = { MyWin, yours, mine } -- fix a snag
for i=1 to length(temp_wins) do
setWindowBackColor( temp_wins[i], Green )
end for
setFont( yours, "System", 0, 0 )
setFont( mine, "System", 0, 0 )
setPixmap( deckPix, loadBitmapFromFile( ".\\Bin\\cardtrix.bmp" ) )
----
function shuffle( sequence s )
integer r
object temp
for i = length( s ) to 1 by -1 do
r = rand( i )
temp = s[r]
s[r] = s[i]
s[i] = temp
end for
return s
end function
----
procedure green( )
setPenColor( dealPix, Green )
drawRectangle( dealPix, 1, 0, 0, 131, 98 )
copyBlt( MyWin, 80, 140, dealPix )
setPenColor( playPix, Green )
drawRectangle( playPix, 1, 0, 0, 149, 98 )
copyBlt( MyWin, 70, 35, playPix )
end procedure
----
-- play pop.wav every time a card is drawn
procedure sound_off( )
object jk
jk = playSound( ".\\Bin\\pop.wav" )
end procedure
----
function anything_but( integer x )
integer r
r = rand( 3 )
if r >= x then r += 1 end if
return r
end function
----
procedure play( integer picked )
integer pos, rnd, you, me
if ready = 1 then
ready = 0
setText( statBar, "...you drew the " & cards[deck[picked]] )
you = deck[picked]
pos = deck[picked]*70
bitBlt( playPix, 0, 0, deckPix, pos, 0, 71, 98, SrcCopy )
copyBlt( MyWin, 70, 35, playPix )
sound_off( )
sleep( 1 )
rnd = anything_but( picked )
me = deck[rnd]
pos = deck[rnd]*70
bitBlt( playPix, 78, 0, deckPix, pos, 0, 71, 98, SrcCopy )
copyBlt( MyWin, 70, 35, playPix )
sound_off( )
sleep( 2 )
if me > you then
setText( statBar, msg2 )
myscore += 1
setText( mine, "Me " & sprintf( "%d", myscore ) )
else
setText( statBar, msg3 )
yourscore += 1
setText( yours, "You " & sprintf( "%d", yourscore ) )
end if
sleep( 3 )
green( )
deck = shuffle( deck )
setTimer( MyWin, myTimer, 500 )
end if
end procedure
----
procedure start_up( atom self, atom event, sequence params )
green( )
deck = shuffle( deck )
setText( statBar, msg0 )
setTimer( MyWin, myTimer, 500 )
end procedure
setHandler( MyWin, w32HOpen, routine_id( "start_up" ) )
----
-- delay routine
procedure stall( atom x )
atom delay
delay=time( )
while time( ) < delay + x do
end while
end procedure
----
procedure do_deal( atom self, atom event, sequence params )
atom timerId = params[1]
if timerId = 1 then
killTimer( MyWin, myTimer )
setTimer( MyWin, myTimer2, 2500 )
setText( statBar, msg0 )
if cheat = 0 then
for i = 0 to 3 do
-- give visual cue, as well as sound
bitBlt( dealPix, i*20, 0, deckPix, 0, 0, 71, 98, SrcCopy ) --**
copyBlt( MyWin, 80, 140, dealPix ) --**
bitBlt( dealPix, i*20, 0, deckPix, 0, 0, 71, 98, PatInvert )
copyBlt( MyWin, 80, 140, dealPix )
sound_off( )
stall( .1 )
bitBlt( dealPix, i*20, 0, deckPix, 0, 0, 71, 98, SrcCopy )
copyBlt( MyWin, 80, 140, dealPix )
stall( .4 )
end for
else
-- don't bother with visual cue here, but,
-- show first card to 'cheat'...
bitBlt( dealPix, 0, 0, deckPix, deck[1]*70, 0, 71, 98, SrcCopy )
copyBlt( MyWin, 80, 140, dealPix )
stall( .5 )
for i = 0 to 3 do
sound_off( )
bitBlt( dealPix, i*20, 0, deckPix, 0, 0, 71, 98, SrcCopy )
copyBlt( MyWin, 80, 140, dealPix )
stall( .5 )
end for
end if
elsif timerId = 2 then
killTimer( MyWin, myTimer2 )
ready = 1
setText( statBar, msg1 )
end if
end procedure
setHandler( MyWin, w32HTimer, routine_id( "do_deal" ) )
----
procedure kill_timers( )
killTimer( MyWin, myTimer )
killTimer( MyWin, myTimer2 )
end procedure
----
procedure cheat_deal( atom self, atom event, sequence params )
kill_timers( )
ready=0
if cheat = 0 then cheat = 1 else cheat = 0 end if
green( )
do_deal( 0, 0, {1} )
end procedure
setHandler( menuCheat, w32HClick, routine_id( "cheat_deal" ) )
----
procedure pick( atom self, atom event, sequence params )
atom key = params[1]
if key > 48 and key < 53 then
picked = key - 48
play( picked )
end if
end procedure
setHandler( MyWin, w32HKeyDown, routine_id( "pick" ) )
----
procedure on_bitmap( atom self, atom event, sequence params )
atom x = params[2]
atom y = params[3]
integer picked
if event = LeftDown then
if x > 80 then -- left x co-ordinate of 'total'
if x < 210 then -- right x co-ordinate of 'total'
if y > 141 and y < 237 then -- y co-ordinates of 'total'
picked = 0
for i = 80 to 140 by 20 do -- 'overlapped' zones
-- leave a 'dead' zone between cards !
if x > i+1 and x < i + ( 20 - 1 ) then
picked = floor( ( i - 80 ) / 20 ) + 1
end if
end for
if x > 140 and x < 210 -- 'remainder' of last card
then picked = 4
end if
if picked != 0 and ready = 1 then
play( picked )
end if
end if
end if
end if
end if
end procedure
setHandler( MyWin, w32HMouse, routine_id( "on_bitmap" ) )
----
procedure re_paint( atom self, atom event, sequence params )
copyBlt( MyWin, 80, 140, dealPix )
copyBlt( MyWin, 70, 35, playPix )
end procedure
setHandler( MyWin, w32HPaint, routine_id( "re_paint" ) )
----
procedure quit( atom self, atom event, sequence params )
closeWindow( MyWin )
end procedure
setHandler( menuOut, w32HClick, routine_id( "quit" ) )
----
WinMain( MyWin, Normal )
----I've used a little bitBlt() trick within the do_deal() routine to momentarily change the color of the dealt card, by using 'PatInvert' as the last bitBlt() parameter.
But, we're not done yet !
Our program is still not very responsive to the user. Clicking on
the menu while cards are being drawn, for example, does nothing until
the drawing routine is finished, so there's more work to do ;-)
..end of lesson.