Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Misc system files
.zshrc
.dstore
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ clean:
UNAME := $(shell uname -s)

ifeq ($(UNAME),Darwin)
IFLAGS := -I . -I /Library/Frameworks/SDL2.framework/Headers
# Use the SDL2 installed by Homebrew (brew install sdl2). sdl2-config reports
# the correct include/lib paths whether SDL2 lives under /opt/homebrew (Apple
# Silicon) or /usr/local (Intel). Make sure sdl2-config is on PATH (see mac.md).
IFLAGS := -I . $(shell sdl2-config --cflags)
CFLAGS := -Wno-format -fsanitize=float-divide-by-zero -g -flto -DNDEBUG -fno-strict-aliasing -std=gnu99 $(IFLAGS)
LDFLAGS := -F /Library/Frameworks/ -framework SDL2
LDFLAGS := $(shell sdl2-config --libs)
else
IFLAGS := -I . -I /usr/include/SDL2
CFLAGS := -rdynamic -g -flto -DNDEBUG -fno-strict-aliasing -O1 -std=gnu99 $(IFLAGS)
Expand Down
113 changes: 108 additions & 5 deletions base/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ static int keymap[NUM_SCAN];
// These are the only especially encoded keys the game uses.
#define SPECIAL_KEY_LIST \
KEY_MAP(SDL_SCANCODE_F1, 59) \
KEY_MAP(SDL_SCANCODE_F2, 60) \
KEY_MAP(SDL_SCANCODE_UP, 72) \
KEY_MAP(SDL_SCANCODE_LEFT, 75) \
KEY_MAP(SDL_SCANCODE_DOWN, 80) \
Expand All @@ -64,6 +65,15 @@ static const int special_keycodes[] = { SPECIAL_KEY_LIST };

static const int NUM_SPECIAL = sizeof(special_keys) / sizeof(special_keys[0]);

// Keys travel through the circular buffer as plain SDL scancodes, but the
// modifier state (shift / caps lock) that was active when the key was pressed
// is lost by the time the game thread decodes them. We preserve it by packing
// the modifier flags into high bits of the buffered value (scancodes are < 512,
// so bits 16/17 are free). keydecode() unpacks them to produce capitals.
#define KEYCODE_SCAN_MASK 0x1FF
#define KEYCODE_SHIFT_BIT (1 << 16)
#define KEYCODE_CAPS_BIT (1 << 17)

#include "include/base/input_proto.h"

void input_init()
Expand Down Expand Up @@ -200,11 +210,18 @@ static void process_key_event(const SDL_Event *e)

if (e->type == SDL_KEYDOWN)
{
// Modifier keys (shift, ctrl, alt, caps lock, ...) are not characters.
// Don't buffer them as keypresses - their state is read separately and
// folded into the keys they modify. (Previously these leaked through and
// decoded to junk: shift, caps lock, etc. appeared as stray letters.)
if (is_modifier(scancode))
return;

if (keymap[scancode] == 0)
{
// immediate keypress this frame
keymap[scancode] = frames;
pushkey(scancode);
pushkey(encode_key(scancode, e->key.keysym.mod));
}
}
else if (e->type == SDL_KEYUP)
Expand All @@ -229,7 +246,7 @@ static void keyscan()
// Do a repeat if held down for half a second or more
// at a rate of 20 presses per second (every 3 frames)
if ((delta >= 30 && (delta % 3) == 0))
pushkey(i);
pushkey(encode_key(i, SDL_GetModState()));
}
}

Expand All @@ -246,9 +263,16 @@ void get_mouse(int *x, int *y)
*y = r;
}

// Used by the TP emulation layer to decode a scancode
int keydecode(int scancode, int *special)
// Used by the TP emulation layer to decode a buffered key into the code the
// game expects. The argument carries the scancode in its low bits plus the
// shift/caps modifier flags (see encode_key).
int keydecode(int code, int *special)
{
int scancode = code & KEYCODE_SCAN_MASK;
int shift = (code & KEYCODE_SHIFT_BIT) != 0;
int caps = (code & KEYCODE_CAPS_BIT) != 0;

// Special navigation / function keys map to fixed DOS-style codes.
int si = find_special(scancode);

if (si != -1)
Expand All @@ -258,7 +282,46 @@ int keydecode(int scancode, int *special)
}

*special = 0;
return (int) SDL_GetKeyFromScancode((SDL_Scancode) scancode);

// Letters: apply shift / caps lock so the player can type capitals.
// shift XOR caps -> uppercase, just like a real keyboard.
if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z)
{
int c = 'a' + (scancode - SDL_SCANCODE_A);
if (shift ^ caps)
c -= 32;
return c;
}

// Numeric keypad: map to the digits the DOS game expects, regardless of
// Num Lock, so numpad 7/9 throw grenades and 8/4/6/2 steer the ejected
// pilot the way they did on the original.
switch (scancode)
{
case SDL_SCANCODE_KP_0: return '0';
case SDL_SCANCODE_KP_1: return '1';
case SDL_SCANCODE_KP_2: return '2';
case SDL_SCANCODE_KP_3: return '3';
case SDL_SCANCODE_KP_4: return '4';
case SDL_SCANCODE_KP_5: return '5';
case SDL_SCANCODE_KP_6: return '6';
case SDL_SCANCODE_KP_7: return '7';
case SDL_SCANCODE_KP_8: return '8';
case SDL_SCANCODE_KP_9: return '9';
case SDL_SCANCODE_KP_ENTER: return 13;
case SDL_SCANCODE_KP_PERIOD: return '.';
}

// Everything else: the plain ASCII value (digits, space, enter, esc,
// backspace, punctuation). SDL keycodes above 255 are non-character keys
// (unused F-keys, etc.); drop them instead of truncating into bogus
// letters the way the old code did.
int k = (int) SDL_GetKeyFromScancode((SDL_Scancode) scancode);

if (k > 255)
return 0;

return k;
}

// Used by keydecode to detect a special key
Expand All @@ -272,3 +335,43 @@ static int find_special(int code)

return -1;
}

// True for keys that are modifiers rather than characters. These are tracked
// via their modifier state and must not be buffered as keypresses themselves.
static int is_modifier(int scancode)
{
switch (scancode)
{
case SDL_SCANCODE_LCTRL:
case SDL_SCANCODE_RCTRL:
case SDL_SCANCODE_LSHIFT:
case SDL_SCANCODE_RSHIFT:
case SDL_SCANCODE_LALT:
case SDL_SCANCODE_RALT:
case SDL_SCANCODE_LGUI:
case SDL_SCANCODE_RGUI:
case SDL_SCANCODE_CAPSLOCK:
case SDL_SCANCODE_NUMLOCKCLEAR:
case SDL_SCANCODE_SCROLLLOCK:
case SDL_SCANCODE_MODE:
return 1;
default:
return 0;
}
}

// Pack a scancode together with the shift/caps state active when it was
// pressed, so keydecode (which runs later, on the game thread) can produce
// the right character. Scancodes are < 512, so the high bits are free.
static int encode_key(int scancode, unsigned mod)
{
int v = scancode & KEYCODE_SCAN_MASK;

if (mod & KMOD_SHIFT)
v |= KEYCODE_SHIFT_BIT;

if (mod & KMOD_CAPS)
v |= KEYCODE_CAPS_BIT;

return v;
}
Binary file added bin/base/font.c.o
Binary file not shown.
Binary file added bin/base/input.c.o
Binary file not shown.
Binary file added bin/base/render.c.o
Binary file not shown.
Binary file added bin/base/sound.c.o
Binary file not shown.
Binary file added bin/base/turbo.c.o
Binary file not shown.
Binary file added bin/chopper258
Binary file not shown.
Binary file added bin/exe/chopper258.c.o
Binary file not shown.
Binary file added bin/game/chopper2.c.o
Binary file not shown.
61 changes: 46 additions & 15 deletions game/chopper2.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ int16_t pict2[1+12];
int16_t pict3[1+45]; // { parachute }
int16_t pblank[1+12];
bool mainquit;
bool tomenu; // { set by ESC: abandon current screen/mission and return to the main menu }

// { starting }

Expand Down Expand Up @@ -267,6 +268,17 @@ void ToggleMouse();
#include "title.h"
#include "terrain.h"

// ESC during a mission abandons it and returns to the main menu (rather than
// quitting the program). quit ends the mission loop; firstgo=0 makes the next
// TitleScreen show the menu again; tomenu tells the outer loop to skip the
// post-mission results/save and go straight back to the menu.
void ReturnToMenu()
{
tomenu = true;
firstgo = 0;
quit = true;
}

void GraphicStartUp()
{
int16_t gm;
Expand Down Expand Up @@ -296,6 +308,7 @@ void ResetVars()
}

quit = false;
tomenu = false;
x = 25;
mfall = 0;

Expand Down Expand Up @@ -677,7 +690,7 @@ void Input()
writeln_u16_str(ammo," ");
}
break;
case 27: Shutdown(); break;
case 27: ReturnToMenu(); break;
case 'e': case 'E': case 60:
dostatus();
ammo = 10;
Expand Down Expand Up @@ -1431,7 +1444,7 @@ void ManCheck() // ( man in air (not soldier) )
writeln_u16_str(ammo," ");
}
break;
case 27 : Shutdown(); break;
case 27 : ReturnToMenu(); break;
case 'p':case 'P' : Pause(); break;
case 's':case 'S' : Speeds(); break;
case 't':case 'T' : ToggleMouse(); break;
Expand Down Expand Up @@ -1523,7 +1536,7 @@ void Input2() // ( after eject )
man.time = -120;
}
break;
case 27 : Shutdown(); break;
case 27 : ReturnToMenu(); break;
case 'p':case 'P' : Pause(); break;
case 's':case 'S' : Speeds(); break;
case 't':case 'T' : ToggleMouse(); break;
Expand Down Expand Up @@ -2207,6 +2220,8 @@ void Start()
ResetVars();
TitleScreen(&level,&pilnum,&pilot,&free_);

if (tomenu) return; // ESC at the title/roster/difficulty screen -> back to menu

InitMouse();
ResetVars2();

Expand All @@ -2228,6 +2243,8 @@ void Start()
}
}

if (tomenu) return; // ESC during mission selection -> back to menu

MissionSet();

if (step == 5) // { start at NorthCape Base }
Expand Down Expand Up @@ -2277,25 +2294,39 @@ void* gamecode(void *arg)

do {
Start();

// ESC at the title/roster/difficulty screen: no mission was set up,
// so there's nothing to tear down - just loop back to the menu.
if (tomenu)
{
keyhit = '@';
continue;
}

gettime(&time1,&time22,&time3,&time4);
Main();
CalcTime();
nosound();
for (loop = 1; loop <= MaxHoles; loop++) // (* free memory of screen save *)
freemem(hole.image[loop],hole.size[loop]);
delay (2000);
if (pl.yv == -32) pl.alive = true;
if (free_ == false) MissionResults (&pilot,onscreen,CalcDif,man.cond,mis,time2,time_,building,deheli,
cruise.alive,tank.alive,pl.alive,cruise.xv,step,damage,totalejects,level,HitTotal);
else
if ((man.cond < 1) && (onscreen == 3) && (x < 50) && (y > 133) && (time2 < 5))
Practice3();
else
if ((deheli[1]) && (man.cond > 0)) Practice1();
else Practice2();
if (free_ == false)

// ESC mid-mission abandons it: skip the stats/save and return to menu.
if (!tomenu)
{
Save(&pilot, pilnum);
delay (2000);
if (pl.yv == -32) pl.alive = true;
if (free_ == false) MissionResults (&pilot,onscreen,CalcDif,man.cond,mis,time2,time_,building,deheli,
cruise.alive,tank.alive,pl.alive,cruise.xv,step,damage,totalejects,level,HitTotal);
else
if ((man.cond < 1) && (onscreen == 3) && (x < 50) && (y > 133) && (time2 < 5))
Practice3();
else
if ((deheli[1]) && (man.cond > 0)) Practice1();
else Practice2();
if (free_ == false)
{
Save(&pilot, pilnum);
}
}

keyhit = '@';
Expand Down
5 changes: 4 additions & 1 deletion game/missions.h
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,10 @@ void StatsScreen(pilottype *pilot,
outtextxyp(200,145,pilot->rank);
outtextxy (30,155,"total damage in (mil.)");

sprintf(damge, " $%4.1f", damage);
// Lifetime total: pilot->dmg was just updated above (this mission's
// damage added to the running total). Print that, not this mission's
// damage alone.
sprintf(damge, " $%4.1f", pilot->dmg);
outtextxyp(200,155,damge);
}

Expand Down
14 changes: 8 additions & 6 deletions game/title.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void EasyWait()
outtextxy (100,185,"press any key");

key = readkey();
if (key == 27) Shutdown();
(void) key; // any key (including ESC) just returns to the menu via the caller
}

// Print out the game instructions.
Expand All @@ -52,9 +52,9 @@ void Instruct()
outtextxy (5,127," \"P\" - pause");
outtextxy (5,137," \"S\" - toggle game speed");
outtextxy (5,147," \"X\" - toggle sound");
outtextxy (5,157," Esc - quit game");
outtextxy (5,157," Esc - return to main menu");
outtextxy (5,167," \"A\" - toggle auto-eject");
outtextxy (5,177," home , PgUp keys - throws grenades (when ejected)");
outtextxy (5,177," 7/9 keys (or home/PgUp) - throw grenades (ejected)");
EasyWait();
cleardevice();
settextstyle (2,0,4);
Expand Down Expand Up @@ -95,7 +95,7 @@ void Instruct()
outtextxy (5,130," To exit helicopter without ejecting : land");
outtextxy (5,140,"then press \"E\". To enter the helicopter : walk");
outtextxy (5,150,"into it.");
outtextxy (5,170," Caps-Lock should be off. Good Luck!");
outtextxy (5,170," Use Shift for capital letters. Good Luck!");
outtextxy (5,160," Gear must be UP for auto-eject to work.");
EasyWait();
}
Expand Down Expand Up @@ -398,7 +398,7 @@ void Roster(int16_t *pilnum, struct pilottype *pil)

if (t_keyhit == 72) dec(pilnum);
else if (t_keyhit == 80) inc(pilnum);
else if (t_keyhit == 27) Shutdown();
else if (t_keyhit == 27) { tomenu = true; firstgo = 0; return; } // ESC -> main menu
else if (t_keyhit == 59) // { enter name }
{
struct pilottype *with = &t_pilot[*pilnum];
Expand Down Expand Up @@ -491,7 +491,7 @@ void SelectDifficulty(char *level)
outtextxy (50,100," 2) AVERAGE (75 bombs)");
outtextxy (50,115," 3) COMMANDO (50 bombs)");
*level = readkey();
if (*level == 27) Shutdown();
if (*level == 27) { tomenu = true; firstgo = 0; return; } // ESC -> main menu
} while (!(in(*level,'1','3')));

}
Expand Down Expand Up @@ -556,6 +556,7 @@ void TitleScreen(char *level, int16_t *pilnum, struct pilottype *pil, bool *isfr
if (*isfree == false)
{
Roster(pilnum, pil);
if (tomenu) return; // ESC at the roster -> back to the menu
SelectDifficulty(level);
}
}
Expand All @@ -579,6 +580,7 @@ void Mission(const char** scene, int16_t *mis)

do {
dif = readkey();
if (dif == 27) { tomenu = true; firstgo = 0; return; } // ESC -> main menu
} while (!in(dif, '1', '4'));

MissionSetUp(); // { draw box on screen }
Expand Down
2 changes: 2 additions & 0 deletions include/base/input_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ static void keyscan(void);
void get_mouse(int *x, int *y);
int keydecode(int scancode, int *special);
static int find_special(int code);
static int is_modifier(int scancode);
static int encode_key(int scancode, unsigned mod);
Loading