Compare commits

...

13 Commits
2.4 ... master

Author SHA1 Message Date
Jeremy Rand
836d4fbc21 Update the readme. 2021-07-26 23:29:07 -04:00
Jeremy Rand
78d451657e Issue release 2.7. 2021-03-17 22:48:01 -04:00
Jeremy Rand
b477317279 Some fixes to the save option upgrade code and mark this as a beta build. 2021-03-15 00:37:47 -04:00
Jeremy Rand
8bb8f4511b Do not set the high bit of the read character to indicate that the apple key(s) are held down if the high bit is already set. 2021-03-12 00:38:54 -05:00
Jeremy Rand
3861bfc00c Add the option to change the keys used to navigate the gem board. 2021-03-12 00:14:05 -05:00
Jeremy Rand
ab895a6d88 Update README to reflect the new release. 2020-05-28 22:37:56 -04:00
Jeremy Rand
8a7c234e61 Implement a series of fixes for Mockingboard speech support based on feedback from TomCh. Much of this is actually changed merged from the Mockingboard detection code from TotalReplay (also feedback from TomCh). Bump the version number to 2.6 to prepare for the next release. 2020-05-28 22:37:14 -04:00
Jeremy Rand
bc1cbc975c Fix an off by one error found by TomCh in the speech code which lead to the beginning of the next speech phrase to be played. This is why "excellent" sounded more like "excellent-ay". Worse, it would lead to the 5 bytes after the phrase "incredible" to be treated like speech data and who knows what that did.
Also, bump the version number to 2.6.a1 in preparation for a 2.6 release with these fixes.
2020-05-04 23:07:02 -04:00
Jeremy Rand
e21c7375f6 Ensure that the game hint is set correctly even after loading a new level. The key is to check for an "end of game" condition at the top of the main game loop. Unless there is a bug in the "next level" code, it will never been the end of the game when the next level starts. But it is the end of game check which also sets the hint square which is the first square it can find with a valid move. 2020-05-03 23:04:25 -04:00
Jeremy Rand
1d5eb0a636 Bump the version up to 2.5. 2020-03-30 23:35:32 -04:00
Jeremy Rand
d8ec0349c5 Add a comment about the problems detecting the //e card from get_ostype(). 2020-03-24 23:54:14 -04:00
Jeremy Rand
0a5c8b5fe3 Detection of the //e card is buggy in the cc65 runtime so implement my own detection of this HW to workaround the buggy double lores implementation. 2020-03-24 02:06:45 -04:00
Jeremy Rand
49ccd2bfdc Add code to handle buggy double lores HW on the Apple //e card for the Mac. Apparently it doesn't handle the colour shifting that is required in the aux bank on other Apple // HW. 2020-03-23 23:28:28 -04:00
13 changed files with 363 additions and 212 deletions

View File

@ -1,10 +1,10 @@
A2Bejwld
Apple // Bejeweled
========
This is an implementation of Bejeweled written for the Apple //. It is my HackFest entry at KansasFest 2016.
![A2Bejwld Screenshot](/a2bejwld.png "A2Bejwld Screenshot")
![A2Bejwld Screenshot](/a2bejwld.png "Apple // Bejeweled Screenshot")
[Download a disk image](https://github.com/jeremysrand/a2bejwld/releases/download/2.4/a2bejwld.dsk)
[Download a disk image](https://github.com/jeremysrand/a2bejwld/releases/download/2.7/a2bejwld.dsk)
[Watch the YouTube video](https://youtu.be/yseAGBzREik)

View File

@ -7,12 +7,12 @@
<key>Binary.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>3</integer>
</dict>
<key>DiskImage.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
<integer>1</integer>
</dict>
<key>a2bejwld.xcscheme_^#shared#^_</key>
<dict>
@ -22,7 +22,7 @@
<key>doNotBuild.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
<integer>2</integer>
</dict>
</dict>
</dict>

View File

@ -265,17 +265,6 @@ void swapSquares(tSquare square1, tGemType gemType1, bool starred1,
temp = y2;
y2 = y1;
y1 = temp;
#if 0
// We don't need to swap square numbers. The code from here on
// doesn't distinguish between the two square numbers. So, save
// some time and don't swap.
//
// Be careful if this assumption is no longer true.
temp = square2;
square2 = square1;
square1 = temp;
#endif
temp = gemType2;
gemType2 = gemType1;

View File

@ -19,6 +19,16 @@ extern void __fastcall__ clearDblLoRes(void);
extern void __fastcall__ unshowDblLoRes(void);
extern void __fastcall__ mixedTextMode(void);
// The Apple //e card does not implement double lores correctly. The colours
// used on the aux bank need to be shifted but the //e card expects unshifted
// colour values (ie the same as those on the main. This function is called
// when the game detects it is not running on a //c or //gs. I would like to
// get_ostype() from the cc65 runtime but it doesn't work reliably for detecting
// the //e card. So I put in my own detection based on the technote. If the
// function detects it is runing on a //e card, it causes the graphics
// routines to use the same colours in the main and aux banks.
extern void __fastcall__ setBuggyDblLoRes(void);
extern void __fastcall__ drawBgSquare(tSquare square);
extern void __fastcall__ drawGem(tSquare square);

View File

@ -17,6 +17,8 @@
.export _explodeGemFrame1, _explodeGemFrame2
.export _explodeGemFrame3, _explodeGemFrame4
.export _explodeGemFrame5, _explodeGemFrame6
.export _setBuggyDblLoRes
.include "apple2.inc"
@ -909,6 +911,60 @@ xPos: .BYTE $0
square: .BYTE $0
.endproc
.proc _setBuggyDblLoRes
; Unfortunately, there is a bug in get_ostype() in cc65 with detecting the Apple //e card.
; There is a version byte in the ROM of the Apple //e card and the tech note describing
; machine detection implies it is 0. The first version of the Mac SW for the card maybe used
; 0 but for sure there are versions 2 and 3 out there also. But cc65 tests for 0 specifically.
; That causes it to detect a Apple //e card with version greater than 0 as an Apple //e
; enhanced.
;
; Combine this with the fact that double lores graphics on the //e card is broken and I have
; a problem. So, rather than use get_ostype() to detect the //e card, I am detecting it myself
; below based on what the tech note says. If I find the game is running on the //e card, then
; I remap the colours used in aux memory to look correct on that HW.
;
; The bug in cc65 has been sent as a pull request back to that project for a fix here:
; https://github.com/cc65/cc65/pull/1013
; Once this is fixed and I adopt a newer version of cc65 in this project, I can clean up the
; HW detection problem.
;
; Next I will need to contact Apple to get a fix for the double lores graphics on the //e card...
bit $c082
lda $fbb3
cmp #$6
bne @L3
lda $fbc0
cmp #$e0
bne @L3
lda $fbdd
cmp #$02
bne @L3
ldx #63
@L1:
lda bgColor,X
sta bgAuxColor,X
dex
bpl @L1
ldx #8
@L2:
lda gemColours,X
sta gemAuxColours,X
dex
bpl @L2
@L3:
bit $c080
rts
.endproc
.DATA

View File

@ -32,7 +32,6 @@ static tJoyCallbacks *gJoyCallbacks = NULL;
static tJoyState gJoyState = {
JOY_POS_CENTER,
false,
false
};
@ -52,17 +51,15 @@ void initJoystick(tJoyCallbacks *callbacks)
}
bool isButtonPressed(tJoyButtonNum buttonNum)
bool isButtonPressed(void)
{
if (buttonNum == JOY_BUTTON_0) {
__asm__ volatile("LDA %w", BTN0);
__asm__ volatile("STA %v", gJoystickTemp);
} else if (buttonNum == JOY_BUTTON_1) {
__asm__ volatile("LDA %w", BTN1);
__asm__ volatile("STA %v", gJoystickTemp);
} else {
return false;
}
__asm__ volatile("LDA %w", BTN0);
__asm__ volatile("STA %v", gJoystickTemp);
if (gJoystickTemp > 127)
return true;
__asm__ volatile("LDA %w", BTN1);
__asm__ volatile("STA %v", gJoystickTemp);
return ((gJoystickTemp > 127) ? true : false);
}
@ -123,8 +120,7 @@ static void readJoystickState(tJoyState *state)
pos = JOY_POS_RIGHT;
}
state->button0 = isButtonPressed(0);
state->button1 = isButtonPressed(1);
state->button = isButtonPressed();
if (axisUpDown < LOWER_THRESHOLD) {
switch (pos) {
@ -157,8 +153,7 @@ static void readJoystickState(tJoyState *state)
static bool joystickStateChanged(tJoyState *state1, tJoyState *state2) {
if ((state1->position != state2->position) ||
(state1->button0 != state2->button0) ||
(state1->button1 != state2->button1)) {
(state1->button != state2->button)) {
return true;
}
return false;

View File

@ -26,21 +26,14 @@
#define JOY_POS_DOWN_RIGHT 8
#define NUM_JOY_POSITIONS 9
#define JOY_BUTTON_0 0
#define JOY_BUTTON_1 1
#define NUM_JOY_BUTTONS 2
// Typedefs
typedef int8_t tJoyButtonNum;
typedef int8_t tJoyPos;
typedef struct tJoyState {
tJoyPos position;
bool button0;
bool button1;
bool button;
} tJoyState;
typedef struct tJoyCallbacks {
@ -64,6 +57,6 @@ typedef struct tJoyCallbacks {
extern void initJoystick(tJoyCallbacks *callbacks);
extern bool isButtonPressed(tJoyButtonNum buttonNum);
extern bool isButtonPressed(void);
extern bool pollJoystick(void);

View File

@ -11,6 +11,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "dbllores.h"
#include "machine.h"
#include "vbl.h"
@ -32,24 +33,9 @@ static tMachineGSSpeed gOldSpeed = GS_SPEED_SLOW;
// Implementation
static bool machineIs2c(void)
static bool machineIs2GS(uint8_t machineType)
{
switch (get_ostype()) {
case APPLE_IIC:
case APPLE_IIC35:
case APPLE_IICEXP:
case APPLE_IICREV:
case APPLE_IICPLUS:
return true;
}
return false;
}
static bool machineIs2GS(void)
{
switch (get_ostype()) {
switch (machineType) {
case APPLE_IIGS:
case APPLE_IIGS1:
case APPLE_IIGS3:
@ -86,18 +72,25 @@ static tMachineGSSpeed setGSSpeed(tMachineGSSpeed newSpeed)
void initMachine(void)
{
if (machineIs2c()) {
uint8_t machineType = get_ostype();
if ((machineType == APPLE_IIC) ||
(machineType == APPLE_IIC35) ||
(machineType == APPLE_IICEXP) ||
(machineType == APPLE_IICREV) ||
(machineType == APPLE_IICPLUS)) {
gVblWait = vblWait2c;
} else if (machineIs2GS()) {
} else if (machineIs2GS(machineType)) {
vblInit2gs();
gOldSpeed = setGSSpeed(GS_SPEED_SLOW);
}
} else
setBuggyDblLoRes();
}
void uninitMachine(void)
{
if (machineIs2GS()) {
if (machineIs2GS(get_ostype())) {
setGSSpeed(gOldSpeed);
}
}

Binary file not shown.

View File

@ -216,7 +216,7 @@ bool mockingBoardSpeak(uint8_t *data, uint16_t dataLen)
return false;
mockingBoardSpeechData = data;
mockingBoardSpeechLen = dataLen + 1;
mockingBoardSpeechLen = dataLen;
mockingBoardSpeakPriv();
return true;

View File

@ -99,14 +99,14 @@ readChip:
lda #$00
sta $80
sta $82 ; type
ldx #$C1
ldx #$C7
@slotLoop:
stx $81
ldy #$04 ; 6522 #1 $Cx04
jsr @timercheck
beq @foundI
inx
cpx #$C8
dex
cpx #$C0
bne @slotLoop
ldx #00 ; not found
jmp @cleanup
@ -117,12 +117,12 @@ readChip:
beq @foundII
ldy #$0C
sty @mb_smc3 + 1
sty @mb_smc1 + 1
iny
sty @mb_smc8 + 1
sty @mb_smc10 + 1
iny
sty @mb_smc7 + 1
sty @mb_smc11 + 1
sty @mb_smc5 + 1
sty @mb_smc14 + 1
.BYTE $2C ; Hide next 2 bytes using a BIT opcode
@foundII: ; stereo
@ -142,6 +142,7 @@ readChip:
sta @mb_smc11 + 2
sta @mb_smc12 + 2
sta @mb_smc13 + 2
sta @mb_smc14 + 2
; detect speech chip
@ -151,25 +152,20 @@ readChip:
lda #>@mb_irq
sta $3ff
lda #0
@mb_smc1:
sta $c403
@mb_smc2:
sta $c402
lda #$0c
@mb_smc3:
@mb_smc1:
sta $c48c
lda #$80
@mb_smc4:
@mb_smc2:
sta $c443
lda #$c0
@mb_smc5:
@mb_smc3:
sta $c440
lda #$70
@mb_smc6:
@mb_smc4:
sta $c443
lda #$82
@mb_smc7:
@mb_smc5:
sta $c48e
ldx #0
@ -191,11 +187,33 @@ readChip:
sei
ror $82
lda $81
and #7
ldy #$ff
@mb_smc6:
sty $c403
lda #$7
@mb_smc7:
sta $c402
@mb_smc8:
sty $c483
@mb_smc9:
sta $c482
and $81
ora $82
tax
iny
sty $80
tya
sta ($80),y
lda #4
sta ($80),y
tya
ldy #$80
sta ($80),y
lda #4
sta ($80),y
@cleanup:
lda zp80Backup
sta $80
@ -223,24 +241,21 @@ readChip:
@mb_irq:
lda #2
@mb_smc8:
@mb_smc10:
sta $c48d
lda #$80
@mb_smc11:
sta $c443
lda #0
@mb_smc9:
@mb_smc12:
sta $c440
lda #$70
@mb_smc10:
@mb_smc13:
sta $c443
sta $80
lda #2
@mb_smc11:
@mb_smc14:
sta $c48e
lda #$ff
@mb_smc12:
sta $c403
lda #7
@mb_smc13:
sta $c402
lda $45
rti
@ -287,11 +302,6 @@ irq2Backup: .BYTE $00
.proc _mockingBoardSpeakPriv
sei
lda #$00
ldx #DDRA
jsr writeChip
ldx #DDRB
jsr writeChip
; Get the starting address of the data and store in the work pointer
lda _mockingBoardSpeechData+1
@ -376,6 +386,9 @@ irq2Backup: .BYTE $00
@L5:
; If at the end, turn everything off. Store a pause phoneme.
lda #$80
ldx #CTTRAMP
jsr writeChip
lda #$00
ldx #DURPHON
jsr writeChip
@ -394,12 +407,6 @@ irq2Backup: .BYTE $00
lda #$02
ldx #IER
jsr writeChip
lda #$FF
ldx #DDRA
jsr writeChip
lda #$07
ldx #DDRB
jsr writeChip
@L2:
; Set the carry flag to indicate we handled the interrupt and return to the caller.

View File

@ -8,6 +8,7 @@
#include <conio.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -26,20 +27,36 @@
// Defines
#define SAVE_OPTIONS_FILE "A2BEJWLD.OPTS"
#define VERSION "v2.4"
#define VERSION "v2.7"
#define OPTIONS_VERSION_UNSAVED 0
#define OPTIONS_VERSION 2
#define OPTIONS_VERSION_V2 2
#define OPTIONS_VERSION 3
#define OPTION_JOYSTICK_ENABLED (1 << 0)
#define OPTION_MOUSE_ENABLED (1 << 1)
#define OPTION_SOUND_ENABLED (1 << 2)
#define OPTION_MOCKINGBOARD_ENABLED (1 << 3)
#define OPTION_MOCKINGBOARD_SPEECH_ENABLED (1 << 4)
// Typedefs
typedef struct tGameOptions {
typedef struct tGameOptionsV2 {
uint8_t optionsVersion;
bool enableJoystick;
bool enableMouse;
bool enableSound;
bool enableMockingboard;
bool enableMockingboardSpeech;
} tGameOptionsV2;
typedef struct tGameOptions {
uint8_t optionsVersion;
uint8_t flags;
char upChar;
char downChar;
char leftChar;
char rightChar;
} tGameOptions;
@ -104,27 +121,32 @@ static bool gShouldSave = false;
static tGameOptions gGameOptions = {
OPTIONS_VERSION_UNSAVED, // optionsVersion
false, // enableJoystick
true, // enableMouse
true, // enableSound
true, // enableMockingboard
true // enableMockingboardSpeech
(OPTION_MOUSE_ENABLED | OPTION_SOUND_ENABLED | OPTION_MOCKINGBOARD_ENABLED | OPTION_MOCKINGBOARD_SPEECH_ENABLED), // flags
'I', // upChar
'M', // downChar
'J', // leftChar
'K' // rightChar
};
// Implementation
static void printChar (char ch)
{
if (ch == '\n')
ch = '\r';
ch |= 0x80;
cout(ch);
}
static void printString(char * buffer)
{
char ch;
while (*buffer != '\0') {
ch = *buffer;
if (ch == '\n')
ch = '\r';
ch |= 0x80;
cout(ch);
printChar(ch);
buffer++;
}
}
@ -140,7 +162,7 @@ static void printInteger(uint16_t val)
static void badThingHappened(void)
{
if (gGameOptions.enableSound)
if ((gGameOptions.flags & OPTION_SOUND_ENABLED) != 0)
printString("\007");
}
@ -179,7 +201,7 @@ static bool loadOptions(void)
fclose(optionsFile);
// If we are upgrading from v1 of the options file, then:
// If we are upgrading from v1 to v2 of the options file, then:
// - Force the mouse option on. This option is now only used to disable the mouse when one is
// present. When no mouse is installed, this option does nothing.
// - There used to be a tSlot of the mockingboard where we now have the enableMockingboard boolean.
@ -187,10 +209,34 @@ static bool loadOptions(void)
// - There used to be a boolean to enable/disable the speech chip on the mockingboard. It was only
// true if the user enabled it. Now that we can detect the speech chip, the value is default true
// and the user can disable speech if they want.
if (gGameOptions < OPTIONS_VERSION) {
gGameOptions.enableMouse = true;
gGameOptions.enableMockingboard = true;
gGameOptions.enableMockingboardSpeech = true;
if (gGameOptions.optionsVersion < OPTIONS_VERSION) {
tGameOptionsV2 * oldOptions = (tGameOptionsV2 *)&gGameOptions;
if (oldOptions->enableJoystick)
gGameOptions.flags = OPTION_JOYSTICK_ENABLED;
else
gGameOptions.flags = 0;
if (oldOptions->enableMouse)
gGameOptions.flags |= OPTION_MOUSE_ENABLED;
if (oldOptions->enableSound)
gGameOptions.flags |= OPTION_SOUND_ENABLED;
if (oldOptions->enableMockingboard)
gGameOptions.flags |= OPTION_MOCKINGBOARD_ENABLED;
if (oldOptions->enableMockingboardSpeech)
gGameOptions.flags |= OPTION_MOCKINGBOARD_SPEECH_ENABLED;
if (gGameOptions.optionsVersion < OPTIONS_VERSION_V2)
gGameOptions.flags |= (OPTION_MOUSE_ENABLED | OPTION_MOCKINGBOARD_ENABLED | OPTION_MOCKINGBOARD_SPEECH_ENABLED);
gGameOptions.upChar = 'I';
gGameOptions.downChar = 'M';
gGameOptions.leftChar = 'J';
gGameOptions.rightChar = 'K';
gGameOptions.optionsVersion = OPTIONS_VERSION;
}
return true;
@ -206,11 +252,12 @@ static void applyNewOptions(tGameOptions *newOptions)
printString("\n\n\n Saving options...");
if ((gGameOptions.enableSound != newOptions->enableSound) ||
(gGameOptions.enableMockingboard != newOptions->enableMockingboard) ||
(gGameOptions.enableMockingboardSpeech != newOptions->enableMockingboardSpeech)) {
if ((gGameOptions.flags & (OPTION_SOUND_ENABLED | OPTION_MOCKINGBOARD_ENABLED | OPTION_MOCKINGBOARD_SPEECH_ENABLED)) !=
(newOptions->flags & (OPTION_SOUND_ENABLED | OPTION_MOCKINGBOARD_ENABLED | OPTION_MOCKINGBOARD_SPEECH_ENABLED))) {
// If the sound parameters have changed, then re-init sounds
soundInit(newOptions->enableSound, newOptions->enableMockingboard, newOptions->enableMockingboardSpeech);
soundInit(((newOptions->flags & OPTION_SOUND_ENABLED) != 0),
((newOptions->flags & OPTION_MOCKINGBOARD_ENABLED) != 0),
((newOptions->flags & OPTION_MOCKINGBOARD_SPEECH_ENABLED) !=0));
}
memcpy(&gGameOptions, newOptions, sizeof(gGameOptions));
@ -228,7 +275,13 @@ static void showCursor(void)
static void replaceCursor(char ch)
{
cout(CH_CURS_LEFT);
cout(ch | 0x80);
printChar(ch);
}
static char getKey(void)
{
return toupper(cgetc());
}
@ -239,16 +292,14 @@ static bool yorn(void)
showCursor();
while (true) {
ch = cgetc();
ch = getKey();
if ((ch == 'N') ||
(ch == 'n')) {
if (ch == 'N') {
result = false;
break;
}
if ((ch == 'Y') ||
(ch == 'y'))
if (ch == 'Y')
break;
badThingHappened();
@ -265,9 +316,12 @@ static void getSoundOptions(tGameOptions *newOptions)
tSlot slot;
printString("\n\nEnable sounds? (Y/N) ");
newOptions->enableSound = yorn();
if (!newOptions->enableSound)
if (yorn()) {
newOptions->flags |= OPTION_SOUND_ENABLED;
} else {
newOptions->flags &= ~OPTION_SOUND_ENABLED;
return;
}
// If no mockingboard present, don't bother to ask whether to enable/disable it.
slot = mockingBoardSlot();
@ -277,9 +331,12 @@ static void getSoundOptions(tGameOptions *newOptions)
printString("\nEnable MockingBoard sound found in slot ");
printInteger(slot);
printString("? (Y/N) ");
newOptions->enableMockingboard = yorn();
if (!newOptions->enableMockingboard)
if (yorn()) {
newOptions->flags |= OPTION_MOCKINGBOARD_ENABLED;
} else {
newOptions->flags &= ~OPTION_MOCKINGBOARD_ENABLED;
return;
}
// If the mockingboard does not have a speech chip, do not prompt whether to
// enable/disable it.
@ -287,7 +344,50 @@ static void getSoundOptions(tGameOptions *newOptions)
return;
printString("\nEnable speech on the Mockingboard? (Y/N) ");
newOptions->enableMockingboardSpeech = yorn();
if (yorn()) {
newOptions->flags |= OPTION_MOCKINGBOARD_SPEECH_ENABLED;
} else {
newOptions->flags &= ~OPTION_MOCKINGBOARD_SPEECH_ENABLED;
}
}
static char getKeyDirection(char *dir, char current, char other1, char other2, char other3)
{
char ch;
printString("\nKey for ");
printString(dir);
printString(" movement (current ");
printChar(current);
printString(") ");
showCursor();
while (true) {
ch = getKey();
if ((isalnum(ch)) &&
(ch != 'Q') &&
(ch != 'R') &&
(ch != 'O') &&
(ch != 'H') &&
(ch != other1) &&
(ch != other2) &&
(ch != other3))
break;
badThingHappened();
}
replaceCursor(ch);
return ch;
}
static void getKeyboardOptions(tGameOptions *newOptions)
{
printChar('\n');
newOptions->upChar = getKeyDirection("up", newOptions->upChar, 0, 0, 0);
newOptions->downChar = getKeyDirection("down", newOptions->downChar, newOptions->upChar, 0, 0);
newOptions->leftChar = getKeyDirection("left", newOptions->leftChar, newOptions->upChar, newOptions->downChar, 0);
newOptions->rightChar = getKeyDirection("right", newOptions->rightChar, newOptions->upChar, newOptions->downChar, newOptions->leftChar);
}
@ -302,6 +402,9 @@ static void selectOptions(void)
memcpy(&newOptions, &gGameOptions, sizeof(newOptions));
while (true) {
bool enableSound = ((newOptions.flags & OPTION_SOUND_ENABLED) != 0);
bool enableMockingboard = ((newOptions.flags & OPTION_MOCKINGBOARD_ENABLED) != 0);
clrscr();
printString(
// 0000000001111111111222222222233333333334444444444555555555566666666667
@ -309,19 +412,31 @@ static void selectOptions(void)
" Apple // Bejeweled\n"
" Options\n"
"\n"
" K - Keyboard control - ");
printChar(newOptions.upChar);
printChar('\n');
printString(" ");
printChar(newOptions.leftChar);
printChar(' ');
printChar(newOptions.rightChar);
printChar('\n');
printString(" ");
printChar(newOptions.downChar);
printChar('\n');
printString(
" J - Joystick control - ");
printString(newOptions.enableJoystick ? "Enabled\n" : "Disabled\n");
printString(((newOptions.flags & OPTION_JOYSTICK_ENABLED) != 0) ? "Enabled\n" : "Disabled\n");
if (hasMouse())
{
printString(
" M - Mouse control - ");
printString(newOptions.enableMouse ? "Enabled\n" : "Disabled\n");
printString(((newOptions.flags & OPTION_MOUSE_ENABLED) != 0) ? "Enabled\n" : "Disabled\n");
}
printString(
" S - Sound - ");
printString(newOptions.enableSound ? "Enabled\n" : "Disabled\n");
printString(enableSound ? "Enabled\n" : "Disabled\n");
if (newOptions.enableSound) {
if (enableSound) {
tSlot slot = mockingBoardSlot();
if (slot != 0) {
@ -329,18 +444,18 @@ static void selectOptions(void)
// 0000000001111111111222222222233333333334444444444555555555566666666667
// 1234567890123456789012345678901234567890123456789012345678901234567890
" MockingBoard - ");
printString(newOptions.enableMockingboard ? "Enabled (Slot " : "Disabled (Slot ");
printString(enableMockingboard ? "Enabled (Slot " : "Disabled (Slot ");
printInteger(slot);
printString(")\n");
if ((newOptions.enableMockingboard) &&
if ((enableMockingboard) &&
(mockingBoardHasSpeechChip()))
{
printString(
// 0000000001111111111222222222233333333334444444444555555555566666666667
// 1234567890123456789012345678901234567890123456789012345678901234567890
" Speech - ");
printString(newOptions.enableMockingboardSpeech ? "Enabled\n" : "Disabled\n");
printString(((newOptions.flags & OPTION_MOCKINGBOARD_SPEECH_ENABLED) != 0) ? "Enabled\n" : "Disabled\n");
}
}
}
@ -351,21 +466,22 @@ static void selectOptions(void)
" Type a letter to change a setting or any other key to save settings\n"
" and continue");
switch (cgetc()) {
case 'j':
switch (getKey()) {
case 'J':
newOptions.enableJoystick = !newOptions.enableJoystick;
newOptions.flags ^= OPTION_JOYSTICK_ENABLED;
break;
case 's':
case 'S':
getSoundOptions(&newOptions);
break;
case 'K':
getKeyboardOptions(&newOptions);
break;
case 'm':
case 'M':
if (hasMouse()) {
newOptions.enableMouse = !newOptions.enableMouse;
newOptions.flags ^= OPTION_MOUSE_ENABLED;
break;
}
// Fall through. If no mouse, then pressing m is a fall through into the save code.
@ -392,7 +508,18 @@ void printInstructions(void)
" Apple // Bejeweled (" VERSION ")\n"
" by Jeremy Rand\n"
"\n"
" Use I-J-K-M, the arrow keys, joystick or mouse to move your selection.\n"
" Use ");
printChar(gGameOptions.upChar);
printChar('-');
printChar(gGameOptions.leftChar);
printChar('-');
printChar(gGameOptions.rightChar);
printChar('-');
printChar(gGameOptions.downChar);
printString(
" the arrow keys, joystick or mouse to move your selection.\n"
" Hold either apple key, joystick or mouse button and move your selection\n"
" to swap two jewels and match 3 or more jewels. When you match three\n"
" jewels, they disappear and new jewels will drop from the top.\n"
@ -420,8 +547,7 @@ void printInstructions(void)
srand(seed);
switch (cgetc()) {
case 'o':
switch (getKey()) {
case 'O':
selectOptions();
break;
@ -609,12 +735,6 @@ static bool swapDir(tDirection dir)
}
static bool isAppleButtonPressed(void)
{
return (isButtonPressed(JOY_BUTTON_0) || isButtonPressed(JOY_BUTTON_1));
}
static void endGame(void)
{
char ch;
@ -632,18 +752,15 @@ static void endGame(void)
showCursor();
while (true) {
ch = cgetc();
ch = getKey();
switch (ch) {
case 'y':
case 'Y':
replaceCursor(ch);
printString("\n");
return;
case 'n':
case 'N':
case CH_ESC:
case 'q':
case 'Q':
replaceCursor(ch);
quitGame();
@ -717,7 +834,9 @@ void initUI(void)
optionsLoaded = loadOptions();
soundInit(gGameOptions.enableSound, gGameOptions.enableMockingboard, gGameOptions.enableMockingboardSpeech);
soundInit(((gGameOptions.flags & OPTION_SOUND_ENABLED) != 0),
((gGameOptions.flags & OPTION_MOCKINGBOARD_ENABLED) != 0),
((gGameOptions.flags & OPTION_MOCKINGBOARD_SPEECH_ENABLED) != 0));
initGameEngine(&gCallbacks);
@ -789,8 +908,7 @@ static bool joystickChangedCallback(tJoyState *oldState, tJoyState *newState)
if (oldState->position != JOY_POS_CENTER)
return false;
if ((newState->button0) ||
(newState->button1)) {
if (newState->button) {
switch (newState->position) {
case JOY_POS_UP:
return swapDir(DIR_UP);
@ -818,10 +936,7 @@ static bool joystickChangedCallback(tJoyState *oldState, tJoyState *newState)
static bool joystickNoChangeCallback(tJoyState *oldState)
{
if (oldState->button0)
return false;
if (oldState->button1)
if (oldState->button)
return false;
joystickMove(oldState->position);
@ -837,60 +952,56 @@ static bool pollKeyboard(void)
if (!kbhit())
return result;
ch = cgetc();
ch = getKey();
if (ch == gGameOptions.upChar)
ch = 0x0b;
else if (ch == gGameOptions.downChar)
ch = 0x0a;
else if (ch == gGameOptions.leftChar)
ch = CH_CURS_LEFT;
else if (ch == gGameOptions.rightChar)
ch = CH_CURS_RIGHT;
if ((ch < 128) &&
(isButtonPressed()))
ch += 128;
switch (ch) {
case 'i':
case 'I':
// case CH_CURS_UP:
case 0x0b:
if (!isAppleButtonPressed()) {
moveDir(DIR_UP);
break;
}
// Fallthrough...
moveDir(DIR_UP);
break;
case 139:
result = swapDir(DIR_UP);
break;
case 'j':
case 'J':
case CH_CURS_LEFT:
if (!isAppleButtonPressed()) {
moveDir(DIR_LEFT);
break;
}
// Fallthrough...
moveDir(DIR_LEFT);
break;
case 136:
result = swapDir(DIR_LEFT);
break;
case 'k':
case 'K':
case CH_CURS_RIGHT:
if (!isAppleButtonPressed()) {
moveDir(DIR_RIGHT);
break;
}
// Fallthrough...
moveDir(DIR_RIGHT);
break;
case 149:
result = swapDir(DIR_RIGHT);
break;
case 'm':
case 'M':
// case CH_CURS_DOWN:
case 0x0a:
if (!isAppleButtonPressed()) {
moveDir(DIR_DOWN);
break;
}
// Fallthrough...
moveDir(DIR_DOWN);
break;
case 138:
result = swapDir(DIR_DOWN);
break;
case CH_ESC:
case 'q':
case 'Q':
if (gShouldSave) {
videomode(0x12);
@ -901,21 +1012,18 @@ static bool pollKeyboard(void)
quitGame();
break;
case 'r':
case 'R':
refreshScore(0);
startNewGame();
gShouldSave = false;
return true;
case 'o':
case 'O':
selectOptions();
showAndClearDblLoRes();
drawBoard();
break;
case 'h':
case 'H':
getHint();
break;
@ -965,6 +1073,14 @@ void playGame(void)
drawBoard();
speakGo();
while (true) {
if (gameIsOver()) {
endGame();
showAndClearDblLoRes();
refreshScore(0);
startNewGame();
gShouldSave = false;
}
resetStarAnim();
while (true) {
@ -974,23 +1090,15 @@ void playGame(void)
break;
}
if ((gGameOptions.enableJoystick) &&
if (((gGameOptions.flags & OPTION_JOYSTICK_ENABLED) != 0) &&
(pollJoystick())) {
break;
}
if ((gGameOptions.enableMouse) &&
if (((gGameOptions.flags & OPTION_MOUSE_ENABLED) != 0) &&
(pollMouse())) {
break;
}
}
if (gameIsOver()) {
endGame();
showAndClearDblLoRes();
refreshScore(0);
startNewGame();
gShouldSave = false;
}
}
}