Commit custom improvements and description in MATT-README.txt
authorMatthew Mondor <mmondor@pulsar-zone.net>
Tue, 11 Apr 2023 22:59:15 +0000 (22:59 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Tue, 11 Apr 2023 22:59:15 +0000 (22:59 +0000)
MATT-README.txt [new file with mode: 0644]
Makefile
pacman [deleted file]
pacman.c
pacman.h

diff --git a/MATT-README.txt b/MATT-README.txt
new file mode 100644 (file)
index 0000000..c3bd514
--- /dev/null
@@ -0,0 +1,37 @@
+Fork derived from the original code found here:
+http://archive.ubuntu.com/ubuntu/ubuntu/pool/universe/p/pacman4console/
+
+
+IMPROVEMENTS:
+
+- Applied patches from the ubuntu maintainer like adding a game
+  over screen.
+- Disables the cursor, re-enabling it when requesting input in
+  blocking mode.
+- Use blocking mode outside of the main action game loop.
+- Toggle the hero between C and O characters so that it is animated
+  and "munches".
+- Use flushinp(3) instead of ad hoc getch(3) to flush input.
+- Added an atexit(3) cleanup handler for endwin(3).
+
+
+BUGFIXES:
+
+- Used to take 100% CPU time in its delay loop.  It assumed that no
+  character buffering occurs and that non-blocking getch() had to
+  be called in a loop until the time expired, when it can simply be
+  called after the timer expires.
+- Used illegal usleep(1000000) with undefined behavior that waited
+  one secod on Linux+glibc but that doesn't sleep on NetBSD.  These
+  were replaced with sleep(1).  This affected transitions and the
+  score display.
+
+
+ISSUES:
+
+- The original makefile is buggy and includes uninstall in the
+  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.
index 6d0bd53..c3e9d24 100755 (executable)
--- a/Makefile
+++ b/Makefile
@@ -3,19 +3,20 @@ BINDIR=$(PREFIX)/bin
 DATAROOTDIR=$(PREFIX)/share
 
 all:
-       gcc pacman.c     -o pacman     -DDATAROOTDIR=\"$(DATAROOTDIR)\" $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -lncurses
-       gcc pacmanedit.c -o pacmanedit -DDATAROOTDIR=\"$(DATAROOTDIR)\" $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -lncurses
+#      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
 
 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
@@ -24,4 +25,4 @@ uninstall:
 
 clean:
        rm -f pacman
-       rm -f pacmanedit
+#      rm -f pacmanedit
diff --git a/pacman b/pacman
deleted file mode 100755 (executable)
index b9682e1..0000000
Binary files a/pacman and /dev/null differ
index f75b993..093f20a 100755 (executable)
--- a/pacman.c
+++ b/pacman.c
@@ -14,6 +14,7 @@
 #include <stdio.h>
 #include <curses.h>
 #include <string.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <time.h>
 #include <unistd.h>
@@ -36,6 +37,7 @@ void CheckScreenSize();                                 //Make sure resolution i
 void CreateWindows(int y, int x, int y0, int x0);       //Make ncurses windows
 void Delay();                                           //Slow down game for better control
 void DrawWindow();                                      //Refresh display
+void Cleanup(void);                                    //endwin(3) cleanup handler
 void ExitProgram(const char *message);                  //Exit and display something
 void GetInput();                                        //Get user input
 void InitCurses();                                      //Start up ncurses
@@ -68,6 +70,13 @@ int LevelNumber = 0;                      //What level number are we on?
 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';
+
+//Curses cleanup
+bool curses_initialized = false;
+
+
 /****************************************************************
 * Function:    main()                                           *
 * Parameters:  argc, argv (passed from command line)            *
@@ -113,8 +122,11 @@ int main(int argc, char *argv[100]) {
     InitCurses();                   //Must be called to start ncurses
     CheckScreenSize();              //Make sure screen is big enough
     CreateWindows(29, 28, 1, 1);    //Create the main and status windows
+    curses_initialized = true;
+    (void)atexit(Cleanup);
 
     IntroScreen();              //Show intro "movie"
+    (void)flushinp();
 
     int start_lives = Lives;
     int start_points = Points;
@@ -176,7 +188,7 @@ int CheckCollision() {
                 GhostsInARow *= 2;
 
                 wrefresh(win);      //Update display
-                usleep(1000000);    //and pause for a second
+                sleep(1);    //and pause for a second
 
                 //Reset the ghost's position to the starting location
                 Loc[a][0] = StartingPoints[a][0]; Loc[a][1] = StartingPoints[a][1];
@@ -192,7 +204,7 @@ int CheckCollision() {
 
                 //Subtract one life and pause for 1 second
                 Lives--;
-                usleep(1000000);
+                sleep(1);
 
                 //If no more lives, game over
                 if(Lives == -1) return 1;
@@ -213,7 +225,7 @@ int CheckCollision() {
                 Dir[4][0] =  0; Dir[4][1] = -1;
 
                 DrawWindow();       //Redraw window
-                usleep(1000000);    //Pause for 1 second before continuing game
+                sleep(1);    //Pause for 1 second before continuing game
             }
         }
     }
@@ -260,9 +272,14 @@ int GameOverScreen() {
 
     //And wait
     int chtmp;
+    (void)flushinp();
+    (void)curs_set(1);
+    (void)nodelay(stdscr, FALSE);
     do {
         chtmp = getch();
     } while (chtmp == ERR);
+    (void)nodelay(stdscr, TRUE);
+    (void)curs_set(0);
 
     if(chtmp == 'q' || chtmp == 'Q')
         return 1;
@@ -287,6 +304,7 @@ void CreateWindows(int y, int x, int y0, int x0) {
 * Returns:     none                                             *
 * Description: Pause the game and still get keyboard input      *
 ****************************************************************/
+#if 0
 void Delay() {
 
     //Needed to get time
@@ -299,6 +317,14 @@ void Delay() {
         ftime(&t_current);          //Update time and check if enough time has overlapped
     } while (abs(t_start.millitm - t_current.millitm) < SpeedOfGame);
 }
+#endif
+void
+Delay(void)
+{
+
+       usleep(1000000 / FPS);
+       GetInput();
+}
 
 /****************************************************************
 * Function:    DrawWindow()                                     *
@@ -366,8 +392,7 @@ void DrawWindow() {
     }
 
     //Display Pacman
-    wattron(win, COLOR_PAIR(Pacman)); mvwaddch(win, Loc[4][0], Loc[4][1], 'C');
-
+    wattron(win, COLOR_PAIR(Pacman)); mvwaddch(win, Loc[4][0], Loc[4][1], hero);
     wrefresh(win);                  //Update main window
 }
 
@@ -379,10 +404,20 @@ void DrawWindow() {
 ****************************************************************/
 void ExitProgram(const char *message) {
     endwin();                       //Uninitialize ncurses and destroy windows
+    curses_initialized = false;
     printf("%s\n", message);        //Display message
     exit(0);                        //End program, return 0
 }
 
+void Cleanup(void)
+{
+
+       if (curses_initialized) {
+               endwin();
+               curses_initialized = false;
+       }
+}
+
 /****************************************************************
 * Function:    GetInput()                                       *
 * Parameters:  none                                             *
@@ -437,7 +472,7 @@ void GetInput() {
 
     case 'p': case 'P':                            //Pause game
         PauseGame();
-        chtmp = getch();            //Update buffered input
+//      chtmp = getch();            //Update buffered input
         break;
 
     case 'q': case 'Q':                            //End program
@@ -456,13 +491,15 @@ void GetInput() {
 void InitCurses() {
 
     initscr();                      //Needed for ncurses windows
+
     start_color();                  //Activate colors in console
-    curs_set(0);                    //    Don't remember
-    keypad(stdscr, TRUE);           //Activate arrow keys
+    curs_set(0);                    //Hide cursor
+    keypad(stdscr, TRUE);           //Activate arrow key seq interpretation
     nodelay(stdscr, TRUE);          //Allow getch to work without pausing
-    nonl();                         //    Don't remember
-    cbreak();                       //    Don't remember
+    nonl();                         //Dont convert CR to NL
+    cbreak();                       //Disable line buffering input
     noecho();                       //Don't display input key
+    leaveok(stdscr, FALSE);         //Leave cursor alone
 
     //Make custom text colors
     init_pair(Normal,    COLOR_WHITE,   COLOR_BLACK);
@@ -488,7 +525,7 @@ void IntroScreen() {
     int a = 0;
     int b = 23;
 
-    a=getch(); a=getch(); a=getch();//Clear the buffer
+    (void)flushinp(); // Clear input buffer
 
     mvwprintw(win, 20, 8, "Press any key...");
 
@@ -505,7 +542,7 @@ void IntroScreen() {
     wattron(win, COLOR_PAIR(Pacman));
     mvwprintw(win, 8, 12, "PACMAN");
     wrefresh(win);
-    usleep(1000000);
+    sleep(1);
 
     //Ghosts chase Pacman
     for(a = 0; a < 23; a++) {
@@ -520,7 +557,8 @@ void IntroScreen() {
         usleep(100000);
     }
 
-    usleep(150000);
+    sleep(1);
+    usleep(50000);
 
     //Pacman eats powerup and chases Ghosts
     for(a = 25; a > 2; a--) {
@@ -618,7 +656,7 @@ int MainLoop() {
 
     DrawWindow();                    //Draw the screen
     wrefresh(win); wrefresh(status); //Refresh it just to make sure
-    usleep(1000000);                 //Pause for a second so they know they're about to play
+    sleep(1);                        //Pause for a second so they know they're about to play
 
     /* Move Pacman. Move ghosts. Check for extra life awarded
     from points. Pause for a brief moment. Repeat until all pellets are eaten */
@@ -627,10 +665,11 @@ int MainLoop() {
         MoveGhosts();    DrawWindow();    if (CheckCollision() == 1) return 1;
         if(Points > FreeLife) { Lives++; FreeLife *= 2;}
         Delay();
+       hero = (hero == 'C' ? 'O' : 'C');
     } while (Food > 0);
 
     DrawWindow();                   //Redraw window and...
-    usleep(1000000);                //Pause, level complete
+    sleep(1);                       //Pause, level complete
     return 0;
 }
 
@@ -802,9 +841,13 @@ void PauseGame() {
     wrefresh(win);
     
     //And wait until key is pressed
+    (void)curs_set(1);
+    (void)nodelay(stdscr, FALSE);
     do {
         chtmp = getch();            //Get input
     } while (chtmp == ERR);
+    (void)nodelay(stdscr, TRUE);
+    (void)curs_set(0);
 
 }
 
index 008c141..d9d0792 100755 (executable)
--- a/pacman.h
+++ b/pacman.h
@@ -1,10 +1,13 @@
 // Some variables that you may want to change
 
-#define DATAROOTDIR "/usr/share"
-#define LEVELS_FILE DATAROOTDIR "/pacman4console/Levels/level__.dat"
+#ifndef DATAROOTDIR
+#    define DATAROOTDIR "/usr/local/share"
+#endif
+#define LEVELS_FILE DATAROOTDIR "/pacman/level__.dat"
 char LevelFile[] = LEVELS_FILE;     //Locations of default levels
 int FreeLife = 1000;                //Starting points for free life
 int Points = 0;                     //Initial points
 int Lives = 3;                      //Number of lives you start with
 int HowSlow = 3;                    //How slow vulnerable ghost move
 int SpeedOfGame = 175;              //How much of a delay is in the game
+#define FPS 5