Buggy level editor fixed.
authorMatthew Mondor <mmondor@pulsar-zone.net>
Tue, 25 Apr 2023 07:36:48 +0000 (07:36 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Tue, 25 Apr 2023 07:36:48 +0000 (07:36 +0000)
MATT-README.txt
Makefile
pacman.c
pacmanedit [deleted file]
pacmanedit.c

index a1c574f..d2fab50 100644 (file)
@@ -29,6 +29,7 @@ BUGFIXES:
   score display.
 - There were issues with input flushing, often preventing the Game
   Over screen from displaying.
+- The level editor was crashing and was using 100% CPU time.
 
 
 ISSUES:
@@ -37,9 +38,10 @@ ISSUES:
   clean target.  Can also easily use native BSD curses where
   available rather than always depending on ncurses.  Should ideally
   detect the situation.
-- The level editor crashes, it's possible that it requires porting
-  to 64-bit or other fixes.  Did not bother.
 - There are some globals in pacman.h that should really be macro
   definitions, and/or moved to the main module.
 - Uses ad-hoc command line arguments processing rather than getopt(3).
 - Should save/load/remember the high score.
+- Should support monochrome displays.
+- With the colors used some ghosts can be difficult to see properly under
+  common terminals+screens.
index c3e9d24..cad3da4 100755 (executable)
--- a/Makefile
+++ b/Makefile
@@ -1,22 +1,36 @@
+CC=cc
 PREFIX=/usr/local
 BINDIR=$(PREFIX)/bin
 DATAROOTDIR=$(PREFIX)/share
 
+LIBS=-lcurses
+
+CFLAGS=-O2 -Wall
+
+# Very useful to correct important bugs
+#CFLAGS+=-Wall -O0 -g -Werror=missing-prototypes \
+# -Werror=implicit-function-declaration -Werror=int-to-pointer-cast \
+# -Werror=pointer-to-int-cast -Werror=format-extra-args -Werror=format= \
+# -Werror=maybe-uninitialized
+
+LDFLAGS+=$(LIBS)
+#LDFLAGS+=-g
+
+
 all:
-#      gcc pacman.c     -o pacman     -DDATAROOTDIR=\"$(DATAROOTDIR)\" $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -lncurses
-       gcc pacman.c     -o pacman     -DDATAROOTDIR=\"$(DATAROOTDIR)\" $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -lcurses
-#      gcc pacmanedit.c -o pacmanedit -DDATAROOTDIR=\"$(DATAROOTDIR)\" $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -lncurses
+       $(CC) pacman.c     -o pacman     -DDATAROOTDIR=\"$(DATAROOTDIR)\" $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
+       $(CC) pacmanedit.c -o pacmanedit -DDATAROOTDIR=\"$(DATAROOTDIR)\" $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
 
 install:    all
        install -D pacman $(DESTDIR)/usr/games/pacman4console
-#      install -D pacmanedit $(DESTDIR)/usr/bin/pacman4consoleedit
+       install -D pacmanedit $(DESTDIR)/usr/bin/pacman4consoleedit
        install -d $(DESTDIR)/usr/share/pacman4console/Levels
        install -m 644 Levels/*dat $(DESTDIR)/usr/share/pacman4console/Levels/
 
 
 uninstall:
        rm -f $(DESTDIR)$(BINDIR)/pacman
-#      rm -f $(DESTDIR)$(BINDIR)/pacmanedit
+       rm -f $(DESTDIR)$(BINDIR)/pacmanedit
        rm -f $(DESTDIR)$(DATAROOTDIR)/pacman/Levels/level0[1-9].dat
        rm -f $(DESTDIR)$(DATAROOTDIR)/pacman/Levels/README
        rm -f $(DESTDIR)$(DATAROOTDIR)/pacman/Levels/template.dat
@@ -25,4 +39,4 @@ uninstall:
 
 clean:
        rm -f pacman
-#      rm -f pacmanedit
+       rm -f pacmanedit
index 68f646d..d84d2bb 100755 (executable)
--- a/pacman.c
+++ b/pacman.c
@@ -3,6 +3,8 @@
 * By: Mike Billars (michael@gmail.com)              *
 * Date: 2014-04-26                                  *
 *                                                   *
+* Improved by Matthew Mondor in 2023.               *
+*                                                   *
 * Please see file COPYING for details on licensing  *
 *       and redistribution of this program          *
 *                                                   *
 void IntroScreen(void);                                 //Show introduction screen and menu
 int  CheckCollision(void);                              //See if Pacman and Ghosts collided
 void CheckScreenSize(void);                             //Make sure resolution is at least 33x29
-void CreateWindows(int y, int x, int y0, int x0);       //Make ncurses windows
+void CreateWindows(int y, int x, int y0, int x0);       //Make curses windows
 void Delay(void);                                       //Slow down game for better control
 void DrawWindow(void);                                  //Refresh display
 void Cleanup(void);                                    //endwin(3) cleanup handler
 void ExitProgram(const char *message);                  //Exit and display something
 void GetInput(void);                                    //Get user input
-void InitCurses(void);                                  //Start up ncurses
+void InitCurses(void);                                  //Start up curses
 void LoadLevel(char *levelfile);                        //Load level into memory
 int  MainLoop(void);                                    //Main program function
 void MoveGhosts(void);                                  //Update Ghosts' location
 void MovePacman(void);                                  //Update Pacman's location
 void PauseGame(void);                                   //Pause
 void PrintHelp(char* name);                             //Print help and exit
+int  GameOverScreen(void);
 
 /*******************
 * GLOBAL VARIABLES *
 *******************/
 //I know global variables are bad, but it's just sooo easy!
 
-//Windows used by ncurses
+//Windows used by curses
 WINDOW * win;
 WINDOW * status;
 
 //For colors
 enum { Wall = 1, Normal, Pellet, PowerUp, GhostWall, Ghost1, Ghost2, Ghost3, Ghost4, BlueGhost, Pacman };
 
-int Loc[5][2] = { 0 };                    //Location of Ghosts and Pacman
-int Dir[5][2] = { 0 };                    //Direction of Ghosts and Pacman
-int StartingPoints[5][2] = { 0 };         //Default location in case Pacman/Ghosts die
-int Invincible = 0;                       //Check for invincibility
-int Food = 0;                             //Number of pellets left in level
-int Level[29][28] = { 0 };                //Main level array
-int LevelNumber = 0;                      //What level number are we on?
-int HiPoints = 0;                         //Highscore
-int HiLevelNumber = 0;                    //Level number reached by hiscore
-int GhostsInARow = 0;                     //Keep track of how many points to give for eating ghosts
-int tleft = 0;                            //How long left for invincibility
+int Loc[5][2];                  //Location of Ghosts and Pacman
+int Dir[5][2];                  //Direction of Ghosts and Pacman
+int StartingPoints[5][2];       //Default location in case Pacman/Ghosts die
+int Invincible = 0;             //Check for invincibility
+int Food = 0;                   //Number of pellets left in level
+int Level[29][28];              //Main level array
+int LevelNumber = 0;            //What level number are we on?
+int HiPoints = 0;               //Highscore
+int HiLevelNumber = 0;          //Level number reached by hiscore
+int GhostsInARow = 0;           //Keep track of how many points to give for eating ghosts
+int tleft = 0;                  //How long left for invincibility
 
 //Hero
 int hero = 'C';
@@ -120,7 +123,7 @@ int main(int argc, char *argv[100]) {
 
     srand( (unsigned)time( NULL ) );
 
-    InitCurses();                   //Must be called to start ncurses
+    InitCurses();                   //Must be called to start curses
     CheckScreenSize();              //Make sure screen is big enough
     CreateWindows(30, 28, 1, 1);    //Create the main and status windows
     curses_initialized = true;
@@ -254,7 +257,7 @@ void CheckScreenSize() {
 
 }
 
-int GameOverScreen() {
+int GameOverScreen(void) {
     int ch;
 
     if (Points > HiPoints) {
@@ -328,7 +331,7 @@ void DrawWindow() {
     //Temporary variables
     int a = 0; int b = 0;
     char chr = ' ';                 //Variable used to display certain characters
-    int attr;                       //Variable that displays normal/bold character
+    int attr = A_NORMAL;            //Variable that displays normal/bold character
 
     //Display level array
     for(a = 0; a < 29; a++) for(b = 0; b < 28; b++) {
@@ -399,7 +402,7 @@ void DrawWindow() {
 * Description: Gracefully end program                           *
 ****************************************************************/
 void ExitProgram(const char *message) {
-    endwin();                       //Uninitialize ncurses and destroy windows
+    endwin();                       //Uninitialize curses and destroy windows
     curses_initialized = false;
     printf("%s\n", message);        //Display message
     exit(0);                        //End program, return 0
@@ -482,11 +485,11 @@ void GetInput() {
 * Function:    InitCurses()                                     *
 * Parameters:  none                                             *
 * Returns:     none                                             *
-* Description: Initialize ncurses and set defined colors        *
+* Description: Initialize curses and set defined colors         *
 ****************************************************************/
 void InitCurses() {
 
-    initscr();                      //Needed for ncurses windows
+    initscr();                      //Needed for curses windows
 
     start_color();                  //Activate colors in console
     curs_set(0);                    //Hide cursor
diff --git a/pacmanedit b/pacmanedit
deleted file mode 100755 (executable)
index ebe8842..0000000
Binary files a/pacmanedit and /dev/null differ
index c99305e..a0976c9 100755 (executable)
@@ -3,6 +3,8 @@
 * By: Mike Billars (michaelbillars@gmail.com)       *
 * Date: 2014-04-26                                  *
 *                                                   *
+* Improved by Matthew Mondor in 2023.               *
+*                                                   *
 * Please see file COPYING for details on licensing  *
 *       and redistribution of this program          *
 *                                                   *
@@ -11,6 +13,7 @@
 /***********
 * INCLUDES *
 ***********/
+#include <stdbool.h>
 #include <stdio.h>
 #include <curses.h>
 #include <string.h>
 /*************
 * PROTOTYPES *
 *************/
-void CheckScreenSize();                                 //Make sure resolution is at least 32x29
+void CheckScreenSize(void);                             //Make sure resolution is at least 32x29
 void CreateWindows(int y, int x, int y0, int x0);       //Make ncurses windows
-void DrawWindow();                                      //Refresh display
+void DrawWindow(void);                                  //Refresh display
 void ExitProgram(char message[255]);                    //Exit gracefully
-void GetInput();                                        //Get user input
-void InitCurses();                                      //Start up ncurses
-void LoadLevel();                                       //Load level into memory
-void MainLoop();                                        //Main loop
-void SaveLevel();                                       //Save level to specified file
+void Cleanup(void);                                     //Curses cleanup
+void GetInput(void);                                    //Get user input
+void InitCurses(void);                                  //Start up ncurses
+void LoadLevel(void);                                   //Load level into memory
+void MainLoop(void);                                    //Main loop
+void SaveLevel(void);                                   //Save level to specified file
 
 /*******************
 * GLOBAL VARIABLES *
 *******************/
 //I know global variables are bad, but it's just sooo easy!
 
+//Curses cleanup
+bool curses_initialized = false;
+
 //Windows used by ncurses
 WINDOW * win;
 WINDOW * status;
@@ -41,10 +48,10 @@ WINDOW * status;
 //For colors
 enum { Wall = 1, Normal, Pellet, PowerUp, GhostWall, Ghost1, Ghost2, Ghost3, Ghost4, BlueGhost, Pacman, Cursor};
 
-int Level[29][28] = { 0 };                              //Main level array
-int Loc[6][2] = { 0 };                                  //Ghosts, Pacman, and cursor locations
-int tleft = 0;                                          //How long left for invincibility
-char filename[255] = "";                                //Name of file to load/save
+int Level[29][28];              //Main level array
+int Loc[6][2];                  //Ghosts, Pacman, and cursor locations
+int tleft = 0;                  //How long left for invincibility
+char filename[255] = "";        //Name of file to load/save
 
 /****************************************************************
 * Function:    main()                                           *
@@ -54,13 +61,16 @@ char filename[255] = "";                                //Name of file to load/s
 ****************************************************************/
 int main(int argc, char *argv[100]) {
 
+    InitCurses();                                   //Initialize curses
+    curses_initialized = true;
+    (void)atexit(Cleanup);
+
     //Make sure they entered a filename to open/save
     if((argc > 1) && (strlen(argv[1]) >= 1)) {
 
         strcpy(filename, argv[1]);                      //Copy command line parameter into string
         LoadLevel();                                    //Load the file
 
-        InitCurses();                                   //Initialize ncurses
         CheckScreenSize();                              //Make sure the screen is big enough
         CreateWindows(29, 28, 1, 1);                    //
 
@@ -72,6 +82,16 @@ int main(int argc, char *argv[100]) {
     else
         ExitProgram("You must specify a file to use (load and save)");
 
+    exit(EXIT_SUCCESS);
+}
+
+void Cleanup(void)
+{
+
+       if (curses_initialized) {
+               endwin();
+               curses_initialized = false;
+       }
 }
 
 /****************************************************************
@@ -80,13 +100,13 @@ int main(int argc, char *argv[100]) {
 * Returns:     none                                             *
 * Description: Make sure the virtual console is big enough      *
 ****************************************************************/
-void CheckScreenSize() {
+void CheckScreenSize(void) {
 
     //Make sure the window is big enough
     int h, w; getmaxyx(stdscr, h, w);
 
     if((h < 32) || (w < 29)) {
-        endwin();
+       Cleanup();
         fprintf(stderr, "\nSorry.\n");
         fprintf(stderr, "To edit levels for Pacman for Console, your console window\n");
         fprintf(stderr, "must be at least 32x29 Please resize your window/resolution\n");
@@ -113,10 +133,10 @@ void CreateWindows(int y, int x, int y0, int x0) {
 * Returns:     none                                             *
 * Description: Redraw each window to update the screen          *
 ****************************************************************/
-void DrawWindow() {
+void DrawWindow(void) {
     int a = 0; int b = 0;
     char chr = ' ';
-    int attr;
+    int attr = A_NORMAL;
 
     //Display level array
     for(a = 0; a < 29; a++) for(b = 0; b < 28; b++) {
@@ -165,7 +185,7 @@ void DrawWindow() {
 ****************************************************************/
 void ExitProgram(char message[255]) {
 
-    endwin();
+    Cleanup();
 
     //Must save file
     if(message[0]=='s') {
@@ -175,7 +195,7 @@ void ExitProgram(char message[255]) {
     else
         printf("%s\n\n", message);
 
-    exit(0);
+    exit(EXIT_FAILURE);
 }
 
 /****************************************************************
@@ -184,21 +204,13 @@ void ExitProgram(char message[255]) {
 * Returns:     none                                             *
 * Description: Get input from user and take appropriate action  *
 ****************************************************************/
-void GetInput() {
+void GetInput(void) {
     int ch;
     int a=0;
     int b=0;
 
-    static int chtmp;
-
     ch = getch();
 
-    //Buffer input
-    do {
-        DrawWindow();
-        ch = getch();
-    } while (ch == ERR);
-
     switch (ch) {
     case KEY_UP:    case 'w': case 'W':
         Loc[5][0]--;
@@ -290,16 +302,18 @@ void GetInput() {
 * Returns:     none                                             *
 * Description: Initialize ncurses and set defined colors        *
 ****************************************************************/
-void InitCurses() {
+void InitCurses(void) {
 
     initscr();                      //Needed for ncurses windows
     start_color();                  //Activate colors in console
-    curs_set(0);                    //      Don't remember
+    curs_set(0);                    //Invisible cursor
     keypad(stdscr, TRUE);           //Activate arrow keys
-    nodelay(stdscr, TRUE);          //Allow getch to work without pausing
-    nonl();                         //      Don't remember
-    cbreak();                       //      Don't remember
+    nodelay(stdscr, FALSE);         //getch(3) mode
+    timeout(-1);                    //blocking getch(3)
+    nonl();                         //No Newline translation
+    cbreak();                       //No line buffering
     noecho();                       //Don't display input key
+    leaveok(stdscr, FALSE);         //Leave cursor alone
 
     //Make custom text colors
     init_pair(Normal,    COLOR_WHITE,   COLOR_BLACK);
@@ -322,7 +336,7 @@ void InitCurses() {
 * Returns:     none                                             *
 * Description: Open level file and load it into memory          *
 ****************************************************************/
-void LoadLevel() {
+void LoadLevel(void) {
 
     int a = 0; int b = 0;
     FILE *fin;
@@ -366,10 +380,11 @@ void LoadLevel() {
 * Returns:     none                                             *
 * Description: Control the main execution of the game           *
 ****************************************************************/
-void MainLoop() {
+void MainLoop(void) {
+    (void)ungetch(' '); // For some reason we need this
     do {
-        GetInput();
         DrawWindow();
+        GetInput();
     } while (1);
 }
 
@@ -379,7 +394,7 @@ void MainLoop() {
 * Returns:     none                                             *
 * Description: Save current level array to a specified file     *
 ****************************************************************/
-void SaveLevel() {
+void SaveLevel(void) {
 
     int a=0;
     int b=0;