429 lines
17 KiB
Rexx
Executable File
429 lines
17 KiB
Rexx
Executable File
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
|
|
/* Copyright (c) 2005-2014 Rexx Language Association. All rights reserved. */
|
|
/* */
|
|
/* This program and the accompanying materials are made available under */
|
|
/* the terms of the Common Public License v1.0 which accompanies this */
|
|
/* distribution. A copy is also available at the following address: */
|
|
/* https://www.oorexx.org/license.html */
|
|
/* */
|
|
/* Redistribution and use in source and binary forms, with or */
|
|
/* without modification, are permitted provided that the following */
|
|
/* conditions are met: */
|
|
/* */
|
|
/* Redistributions of source code must retain the above copyright */
|
|
/* notice, this list of conditions and the following disclaimer. */
|
|
/* Redistributions in binary form must reproduce the above copyright */
|
|
/* notice, this list of conditions and the following disclaimer in */
|
|
/* the documentation and/or other materials provided with the distribution. */
|
|
/* */
|
|
/* Neither the name of Rexx Language Association nor the names */
|
|
/* of its contributors may be used to endorse or promote products */
|
|
/* derived from this software without specific prior written permission. */
|
|
/* */
|
|
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
|
|
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
|
|
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
|
|
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
|
|
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
|
|
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
|
|
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
|
|
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
|
|
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
|
|
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
|
|
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* oobandit.rex An ooDialog example, the Jackpot Slot Machine.
|
|
*
|
|
* This example demonstrates one way of animating bitmaps, by drawing them on
|
|
* the face of a button. It also shows how to correctly use dialog units in
|
|
* a UserDialog to size and place the controls.
|
|
*
|
|
* Creating the dialog and its controls is done this way:
|
|
*
|
|
* The pixel size of the bitmaps is known. Although bitmaps can be stretched
|
|
* by the OS to fit a specific size, bitmaps look best displayed in their
|
|
* actual size. The pixel size of the bitmaps is first converted to the
|
|
* correct dialog unit size, correct for the actual dialog to be constructed.
|
|
*
|
|
* Then the size of the dialog and the size and placement of the dialog
|
|
* controls are calculated around the bitmap size.
|
|
*
|
|
* Note: this program uses the public routine, locate(), to get the full path
|
|
* name to the directory this source code file is located. In places, the
|
|
* variable holding this value has been callously abbreviated to 'sd' which
|
|
* stands for source directory.
|
|
*
|
|
*/
|
|
|
|
-- Use the global .constDir for symbolic IDs, and add IDs for this example.
|
|
.application~useGlobalConstDir('O')
|
|
.constDir[IDC_PB_STOP] = 1100
|
|
.constDir[IDC_STATIC_JACKPOT] = 1200
|
|
.constDir[IDC_PB_BMP_LEFT] = 1201
|
|
.constDir[IDC_PB_BMP_CENTER] = 1202
|
|
.constDir[IDC_PB_BMP_RIGHT] = 1203
|
|
.constDir[IDC_EDIT] = 120
|
|
.constDir[IDC_UD] = 1206
|
|
|
|
/* 1ms fast, 500ms slow, 200ms start, equals random every 25th */
|
|
d = .BanditDlg~new(1, 1000, 1000, 25)
|
|
d~execute("SHOWTOP")
|
|
|
|
return 0
|
|
|
|
/*---------------------------------- requires ------------------------*/
|
|
|
|
::requires "ooDialog.cls"
|
|
::requires "samplesSetup.rex"
|
|
|
|
/*---------------------------------- dialog class --------------------*/
|
|
|
|
::class 'BanditDlg' subclass UserDialog
|
|
|
|
::constant BITMAP_X 152
|
|
::constant BITMAP_Y 178
|
|
::constant FONT_NAME "MS Shell Dlg"
|
|
::constant FONT_SIZE 14
|
|
|
|
::constant MARGIN_X 10
|
|
::constant MARGIN_Y 5
|
|
::constant JACKPOT_LINE_Y 22
|
|
::constant TEXT_Y 12
|
|
|
|
::constant BUTTON_X 35
|
|
::constant BUTTON_Y 15
|
|
|
|
::method init
|
|
expose kind3 initialSpeed minSpeed maxSpeed maxCycle cycle equal misses initPot won bitMapSize dlgSize
|
|
use arg minSpeed, maxSpeed, initialSpeed, kind3
|
|
|
|
self~init:super()
|
|
|
|
-- Set the font the dialog will use when created. Without this step, dialog
|
|
-- units can not be calculated correctly.
|
|
self~setDlgFont(self~FONT_NAME, self~FONT_SIZE)
|
|
|
|
-- Set our various instance variables.
|
|
minSpeed = max(1,minSpeed)
|
|
maxCycle = 200; initPot = 1000; equal = 0; misses = 0; cycle = maxCycle; won = .false
|
|
|
|
-- Calculate the size of a bitmap in dialog units.
|
|
bitMapSize = .Size~new(self~BITMAP_X, self~BITMAP_Y)
|
|
self~pixel2dlgUnit(bitMapSize)
|
|
|
|
-- Calculate the size of this dialog based on the bitmap size.
|
|
dlgSize = self~calcSize(bitMapSize)
|
|
|
|
title = "Jackpot Slot Machine - Stop on 3 of a Kind and Win $$$"
|
|
self~initCode = self~createcenter(dlgSize~width, dlgSize~height, title)
|
|
|
|
::method defineDialog
|
|
expose bmp. initialSpeed dlgSize bitMapSize
|
|
|
|
sd = locate()
|
|
|
|
-- Load the bitmaps into memory.
|
|
bmp.1 = self~loadBitmap(sd"bmp\tiger.bmp")
|
|
bmp.2 = self~loadBitmap(sd"bmp\chihuahu.bmp")
|
|
bmp.3 = self~loadBitmap(sd"bmp\eleph2.bmp")
|
|
bmp.4 = self~loadBitmap(sd"bmp\horse.bmp")
|
|
bmp.5 = self~loadBitmap(sd"bmp\sealion.bmp")
|
|
bmp.6 = self~loadBitmap(sd"bmp\moose.bmp")
|
|
bmp.7 = self~loadBitmap(sd"bmp\rhinoce.bmp")
|
|
bmp.8 = self~loadBitmap(sd"bmp\goat.bmp")
|
|
bmp.0 = 8
|
|
|
|
-- Note that for a static text control, the CENTERIMAGE flag has the effect
|
|
-- of vertically centering the text within the control.
|
|
|
|
-- Create the jackpot line. First a frame around the whole thing.
|
|
x = self~MARGIN_X
|
|
y = self~MARGIN_Y
|
|
self~createBlackFrame(IDC_STATIC, x, y, dlgSize~width - (2 * self~MARGIN_X), self~JACKPOT_LINE_Y, "BORDER")
|
|
|
|
-- Static text on the right, centered over the 1st bitmap
|
|
txt = "Jackpot $$$"
|
|
txtSize = self~getTextSizeDU(txt)
|
|
x += trunc((bitMapSize~width / 2) - (txtSize~width / 2))
|
|
y += self~MARGIN_Y
|
|
self~createStaticText(IDC_STATIC, x, y, txtSize~width, self~TEXT_Y, "CENTER CENTERIMAGE", txt)
|
|
|
|
-- The jackpot number, could be up to 9 digits. Just a static control with a
|
|
-- fancy frame, centered over the middle bitmap
|
|
txt = "888888888"
|
|
txtSize = self~getTextSizeDU(txt)
|
|
x = trunc((dlgSize~width / 2) - ((txtSize~width + 6) / 2))
|
|
self~createBlackFrame(IDC_STATIC, x + 0, y - 2, txtSize~width + 6, self~TEXT_Y + 4, "BORDER")
|
|
self~createBlackFrame(IDC_STATIC, x + 1, y - 1, txtSize~width + 4, self~TEXT_Y + 2, "BORDER")
|
|
self~createStaticText(IDC_STATIC_JACKPOT, x + 3, y - 0, txtSize~width + 0, self~TEXT_Y + 0, "RIGHT CENTERIMAGE")
|
|
|
|
-- Static text on the left, centered over the 3rd bitmap.
|
|
txt = "$$$ Jackpot"
|
|
txtSize = self~getTextSizeDU(txt)
|
|
x = (bitMapSize~width * 2) + (3 * self~MARGIN_X) -- The left edge of the 3rd bitmap ...
|
|
x += trunc((bitMapSize~width / 2) - (txtSize~width / 2)) -- ... and center
|
|
self~createStaticText(IDC_STATIC, x, y, txtSize~width, self~TEXT_Y, "CENTER CENTERIMAGE", txt)
|
|
|
|
-- Now place the bitmaps
|
|
x = self~MARGIN_X
|
|
y = (2 * self~MARGIN_Y) + self~JACKPOT_LINE_Y
|
|
self~createBitmapButton(IDC_PB_BMP_LEFT, x, y, bitMapSize~width, bitMapSize~height, "INMEMORY USEPAL", , , bmp.1)
|
|
|
|
x += bitMapSize~width + self~MARGIN_X
|
|
self~createBitmapButton(IDC_PB_BMP_CENTER, x, y, bitMapSize~width, bitMapSize~height, "INMEMORY ", , , bmp.1)
|
|
|
|
x += bitMapSize~width + self~MARGIN_X
|
|
self~createBitmapButton(IDC_PB_BMP_RIGHT, x, y, bitMapSize~width, bitMapSize~height, "INMEMORY ", , , bmp.1)
|
|
|
|
-- Stop and cancel buttons, placed at left margin and under bitmaps
|
|
x = self~MARGIN_X
|
|
y += bitMapSize~height + self~MARGIN_Y
|
|
buttons = "&Stop" .constDir[IDC_PB_STOP] "onStop &Cancel" .constDir[IDCANCEL] "Cancel"
|
|
self~createPushButtonGroup(x, y, self~BUTTON_X, self~BUTTON_Y, buttons, .false, "DEFAULT")
|
|
|
|
-- A group box to hold the speed adjustment controls
|
|
txt = 'Speed (in ms) lower is faster'
|
|
x += bitMapSize~width + self~MARGIN_X
|
|
cy = dlgSize~height - y - self~MARGIN_Y
|
|
self~createGroupBox(IDC_STATIC, x, y, (bitMapSize~width * 2) + self~MARGIN_X, cy, , txt)
|
|
|
|
-- And finally the speed adjustment controls them selves. The top of a group box
|
|
-- is higher than the top line of the group box (to allow for text.) So in order
|
|
-- for the speed controls to look centered with the group box lines, we need to
|
|
-- calculate the center, adjust for the height of the controls, and then push it
|
|
-- "down a bit." I arbitrarily choose 3 as 'a bit.'
|
|
x += self~MARGIN_X
|
|
y += trunc((cy / 2) - (self~TEXT_Y / 2)) + 3
|
|
txt = ' Faster :'
|
|
txtSize = self~getTextSizeDU(txt)
|
|
self~createStaticText(IDC_STATIC, x, y, txtSize~width, self~TEXT_Y, 'RIGHT CENTERIMAGE', txt)
|
|
|
|
x += txtSize~width + 2
|
|
self~createEdit(IDC_EDIT, x , y, 35, self~TEXT_Y, "NUMBER")
|
|
|
|
x += 35
|
|
self~createUpDown(IDC_UD, x, y, 25, self~TEXT_Y, "RIGHT ARROWKEYS AUTOBUDDY BUDDYINT HORIZONTAL NOTHOUSANDS", 'speed')
|
|
|
|
x += 2
|
|
self~createStaticText(IDC_STATIC, x, y, 30, self~TEXT_Y, 'LEFT CENTERIMAGE', ': Slower')
|
|
|
|
-- Set the up down position to the initial speed.
|
|
self~speed = initialSpeed
|
|
|
|
::method initDialog
|
|
expose minSpeed maxSpeed notStopped jackPotCtrl speedCtrl
|
|
|
|
self~newUpDown(IDC_UD)~setRange(minSpeed, maxSpeed)
|
|
|
|
speedCtrl = self~newEdit(IDC_EDIT)
|
|
speedCtrl~setLimit(maxSpeed~length - 1)
|
|
ret = speedCtrl~connectCharEvent(onKey)
|
|
|
|
jackPotCtrl = self~newStatic(IDC_STATIC_JACKPOT)
|
|
|
|
notStopped = .true
|
|
self~disableControl(IDC_PB_STOP)
|
|
self~start("bandit")
|
|
|
|
-- An attempt to disallow cut and paste into the speed control.
|
|
::method onKey unguarded
|
|
expose speedCtrl
|
|
use arg key, shift, control, alt, info
|
|
|
|
s = speedCtrl~selection
|
|
|
|
if control then return .false -- Don't allow cut and paste
|
|
if key == 57 then return .false
|
|
else return .true
|
|
|
|
::method bandit unguarded
|
|
expose x y z bmp. kind3 cycle maxCycle equal misses notStopped won
|
|
|
|
rand = random(1, 8, time('S') * 7) /* init random */
|
|
ret = play("WHISTLE.WAV")
|
|
|
|
-- The user could have canceled while the whistle was playing..
|
|
if \self~isDialogActive then return 0
|
|
|
|
self~enableControl(IDC_PB_STOP)
|
|
|
|
do cycle = maxCycle by -1 to 1 until \self~isDialogActive
|
|
if self~checkSpeed = 0 then leave
|
|
|
|
sleep = format(max(1, min(100, self~speed / 2)), , 0)
|
|
do j = 1 to self~speed / sleep
|
|
if self~isDialogActive then call msSleep sleep
|
|
end
|
|
|
|
if \self~isDialogActive then return 0
|
|
guard on when notStopped -- Don't change the bitmaps out from under the user.
|
|
|
|
if random(1, kind3) = 3 then do
|
|
x = equal // 8 + 1; y = x; z = x; equal += 1
|
|
end
|
|
else do
|
|
x = random(1, 8); y = random(1, 8); z = random(1, 8)
|
|
end
|
|
|
|
-- It's an error to invoke changeBitmapButton if the underlying dialog
|
|
-- no longer exists. (Which may be if the user hit cancel.)
|
|
if \self~isDialogActive then return 0
|
|
|
|
self~changeBitmapButton(IDC_PB_BMP_LEFT, bmp.x,,,,"INMEMORY STRETCH")
|
|
self~changeBitmapButton(IDC_PB_BMP_CENTER, bmp.y,,,,"INMEMORY STRETCH")
|
|
self~changeBitmapButton(IDC_PB_BMP_RIGHT, bmp.z,,,,"INMEMORY STRETCH")
|
|
|
|
guard off
|
|
if \self~isDialogActive then return 0
|
|
end
|
|
|
|
if \won then do
|
|
self~disableControls
|
|
|
|
msg = 'Sorry, you did not get the jackpot in ' || .endOfLine || -
|
|
misses 'tries.' || .endOfLine~copies(2) || -
|
|
'There were' equal 'chances. ('equal' three' || .endOfLine || -
|
|
'of a kind were shown.)'
|
|
title = 'End of run'
|
|
ret = messageDialog(msg, self~hwnd, title, "OK", "INFORMATION")
|
|
end
|
|
|
|
-- Clean up is in our cancel method, so we invoke that rather than self~ok:super
|
|
if self~isDialogActive then return self~cancel
|
|
else return 1
|
|
|
|
::method disableControls private
|
|
self~disableControl(IDC_PB_STOP)
|
|
self~disableControl(IDCANCEL)
|
|
self~disableControl(IDC_EDIT)
|
|
self~disableControl(IDC_UD)
|
|
|
|
::method onStop
|
|
expose x y z misses initPot notStopped won jackpotCtrl
|
|
|
|
notStopped = .false -- Prevent the 'bandit' from changing the bitmaps
|
|
|
|
if ((x=y) & (y=z)) then do
|
|
-- All 3 bitmaps are the same, jackpot.
|
|
won = .true
|
|
ret = play("tada.wav")
|
|
self~setWindowTitle(self~get,"Congratulations !")
|
|
do i = 40 by 20 to 120
|
|
self~write(i*self~factorx,i*self~factory,"Congratulations...","Arial",14,'BOLD')
|
|
end
|
|
money = jackpotCtrl~getText
|
|
self~write(10*self~factorx+5,75*self~factory,"You won the jackpot:" money,"Arial",18,'BOLD')
|
|
do i=1 to min(money%500 + 1,10)
|
|
ret = play("jackpot.wav")
|
|
money = max(0,money - 500)
|
|
jackpotCtrl~setText(money)
|
|
end
|
|
jackpotCtrl~setText(0)
|
|
call msSleep 1000
|
|
return self~cancel
|
|
end
|
|
|
|
-- Not 3 of a kind
|
|
misses += 1
|
|
ret = play("nope.wav", "yes")
|
|
|
|
if ((x=y) | (y=z) | (x=z)) then do
|
|
ret = infoDialog("2 equal, not bad, try again... jackpot reduced 25%")
|
|
initPot = trunc(initPot * .75)
|
|
end
|
|
else do
|
|
ret = infoDialog("Not a chance, try again... jackpot is halfed!")
|
|
initPot = trunc(initPot * .5)
|
|
end
|
|
|
|
if initPot = 1 then ret = infoDialog("One more chance to hit the jackpot....")
|
|
self~checkSpeed
|
|
notStopped = .true -- Unblock the 'bandit'
|
|
return 0
|
|
|
|
::method checkSpeed
|
|
expose minSpeed maxSpeed cycle initPot jackpotCtrl
|
|
|
|
if \self~isDialogActive then return 0
|
|
|
|
self~getDataAttribute('speed')
|
|
|
|
-- Although the edit control is numbers only, and the up down control won't
|
|
-- allow the user to spin outside of the range, or use the arrow keys to move
|
|
-- outside of the range, it is possible for the user to delete all the
|
|
-- numbers in the edit control, or to type in numbers larger than the
|
|
-- maximum. In which case the up down control seems to return the empty
|
|
-- string for its position ??
|
|
if self~speed == "" then do
|
|
say 'Up down position: ' self~newUpDown(IDC_UD)~getPosition
|
|
say 'Edit control text:' self~newEdit(IDC_EDIT)~getText
|
|
end
|
|
|
|
money = trunc(cycle * initPot / self~speed)
|
|
jackpotCtrl~setText(money)
|
|
|
|
return money
|
|
|
|
|
|
-- You can not remove the bitmap handles while the dialog is still displayed
|
|
-- on the screen. As long as the dialog is showing, the os will try to
|
|
-- repaint the dialog when needed. If the bitmaps are destroyed, the
|
|
-- program will crash. The leaving method is provided for just this purpose,
|
|
-- it allows you to clean up resources.
|
|
::method leaving
|
|
expose bmp.
|
|
|
|
do i = 1 to bmp.0
|
|
self~removeBitmap(bmp.i)
|
|
end
|
|
|
|
|
|
::method cancel
|
|
expose notStopped
|
|
|
|
self~disableControls
|
|
self~cancel:super
|
|
|
|
call Play "byebye.wav"
|
|
|
|
-- Be sure the bandit() method is not blocked so that this Rexx program ends.
|
|
notStopped = .true
|
|
|
|
return 1
|
|
|
|
::method calcSize private
|
|
use strict arg bitMapSize
|
|
|
|
s = .Size~new
|
|
|
|
-- For the width of the dialog, we have 3 bitmaps, an X magin on both sides,
|
|
-- and we space the bitmaps apart horizontaly using the X margin. 3 bitmaps
|
|
-- and 4 X margins
|
|
s~width = (3 * bitMapSize~width) + (4 * self~MARGIN_X)
|
|
|
|
-- The height is sligthly more complicated. It goes like this from top to
|
|
-- bottom: Y margin, jackpot line, Y margin, bitmap height, Y margin, push
|
|
-- button group height, Y margin.
|
|
s~height = (4 * self~MARGIN_Y) + self~JACKPOT_LINE_Y + bitMapSize~height + -
|
|
self~getButtonGroupHeight
|
|
|
|
return s
|
|
|
|
::method getButtonGroupHeight private
|
|
expose h
|
|
|
|
if \h~dataType('W') then do
|
|
-- To calculate the height of the push button group, we need to know the
|
|
-- height of a button, the number of buttons (2), and the vertical spacing
|
|
-- between buttons. It so happens that I know the vertical spacing is 1/2
|
|
-- the button height, truncated.
|
|
h = (2 * self~BUTTON_Y) + trunc(.5 * self~BUTTON_Y)
|
|
end
|
|
return h
|
|
|