|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T s
Length: 11377 (0x2c71)
Types: TextFile
Names: »screen.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/General/Tt/screen.c«
/***************************************************************************\
|* *|
|* screen.c: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This module handles all *|
|* the icky curses(3x) bits. *|
|* *|
|* Author: Mike Taylor (mirk@uk.ac.warwick.cs) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
\***************************************************************************/
#include <curses.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include "screen.h"
#include "tt.h"
#include "pieces.h"
#include "utils.h"
#include "game.h"
/***************************************************************************\
|* *|
|* The function myrefresh() calls the curses(3x) function refresh() *|
|* after first moving the cursor out of harm's way at the bottom of *|
|* the screen. *|
|* *|
\***************************************************************************/
void myrefresh ()
{
int x;
if ((x = screen_depth-2) < GAME_DEPTH+1)
x = GAME_DEPTH+1;
move (x, 0);
refresh ();
}
/***************************************************************************\
|* *|
|* The function hoopy_refresh() touches the screen, then refreshes it, *|
|* so curses(3X) doesn't get any chance to phlegm about with only doing *|
|* the bits that it thinks are OK. So this should fix up screens that *|
|* have been interfered with by biff(1), mesg(1) etc. *|
|* *|
\***************************************************************************/
void hoopy_refresh ()
{
clear ();
setup_screen ();
draw_board ();
update_scores ();
myrefresh ();
}
/***************************************************************************\
|* *|
|* The function print_msg() prints a short message centered on the *|
|* floor of the playing area, with a space before and after it. *|
|* If the message is NULL (ie. (char*)0), or null, (ie. ""), then *|
|* no message is printed. *|
|* *|
\***************************************************************************/
void print_msg (line)
char *line;
{
int i;
move (GAME_DEPTH, 2);
for (i = 0; i < 2*GAME_WIDTH; i++)
addch (FLOOR_CHAR);
if ((line != NULL) && (*line != '\0'))
mvaddstr (GAME_DEPTH, GAME_WIDTH+1-(strlen (line))/2,
form (" %s ", line));
myrefresh ();
}
/***************************************************************************\
|* *|
|* The function clear_area() fills the playing area with BLANK_CHARs. *|
|* It is used to clear the screen before each game, and also clearing *|
|* the screen while displaying pieces one at a time when in debug mode. *|
|* *|
\***************************************************************************/
void clear_area ()
{
int i, j, status, fastflag = 0;
fd_set fds;
struct timeval timeout;
static char buffer[LINELEN];
for (i = 0; i < GAME_DEPTH; i++) {
move (i, 2);
for (j = 0; j < 2*GAME_WIDTH; j++)
addch (BLANK_CHAR);
if (fastflag == 0) {
move (i+1, 2);
for (j = 0; j < 2*GAME_WIDTH; j++)
addch (FLOOR_CHAR);
myrefresh ();
FD_ZERO (&fds);
FD_SET (0, &fds);
timeout.tv_sec = 0L; /* Future implementations of select(2) */
timeout.tv_usec = 50000L; /* might change this value on return */
if ((status = select (1, &fds, (fd_set*) NULL, (fd_set*) NULL,
&timeout)) == -1)
if (errno != EINTR)
die (LE_SELECT, "select(2) failed in clear_area()");
if (status != 0) {
fastflag = 1;
(void) read (0, buffer, LINELEN);
}
}
}
move (GAME_DEPTH, 2);
for (j = 0; j < 2*GAME_WIDTH; j++)
addch (FLOOR_CHAR);
mvaddch (GAME_DEPTH, 0, CORNER_CHAR);
addch (CORNER_CHAR);
mvaddch (GAME_DEPTH, 2*GAME_WIDTH+2, CORNER_CHAR);
addch (CORNER_CHAR);
myrefresh ();
}
/***************************************************************************\
|* *|
|* The function setup_screen should be called exactly once, near the *|
|* beginning of execution. It initialises curses(3x), and prints the *|
|* game titles, the walls and the floor of the game area, and clears *|
|* this area using clear_area() (perhaps unsurprisingly) *|
|* *|
|* STOP PRESS: It no longer calls clear_area(), since play_game() *|
|* does that fairly immediately after this function returns. *|
|* *|
|* STOP PRESS^2: It now does do it again, since play_game() does the *|
|* sneaky-but-slow clear that it fine at times, but cruddy for *|
|* initialisation. *|
|* *|
\***************************************************************************/
void setup_screen ()
{
int i;
for (i = 0; i < GAME_DEPTH; i++) {
mvaddch (i, 0, WALL_CHAR);
addch (WALL_CHAR);
mvaddch (i, 2*GAME_WIDTH+2, WALL_CHAR);
addch (WALL_CHAR);
}
move (GAME_DEPTH, 2);
for (i = 0; i < 2*GAME_WIDTH; i++)
addch (FLOOR_CHAR);
mvaddch (GAME_DEPTH, 0, CORNER_CHAR);
addch (CORNER_CHAR);
mvaddch (GAME_DEPTH, 2*GAME_WIDTH+2, CORNER_CHAR);
addch (CORNER_CHAR);
mvaddstr (0, 2*GAME_WIDTH+6, form ("%sTETRIS FOR TERMINALS%*s%s",
so_str, so_gunk, "", se_str));
mvaddstr (2, 2*GAME_WIDTH+6, "Written by Mike Taylor");
mvaddstr (3, 2*GAME_WIDTH+6, "Email: mirk@uk.ac.warwick.cs");
mvaddstr (4, 2*GAME_WIDTH+6, "Started: Fri May 26 12:26:05 BST 1989");
mvaddstr (6, 2*GAME_WIDTH+6, form ("Game level: %d", game_level));
mvaddstr (8, 2*GAME_WIDTH+6, "Score:");
mvaddstr (9, 2*GAME_WIDTH+6, "Pieces:");
mvaddstr (10, 2*GAME_WIDTH+6, "Levels:");
mvaddstr (12, 2*GAME_WIDTH+8, "Use keys:");
mvaddstr (13, 2*GAME_WIDTH+8, "=========");
mvaddstr (14, 2*GAME_WIDTH+8, form ("Move left: '%c'", left_key));
mvaddstr (15, 2*GAME_WIDTH+8, form ("Move right: '%c'", right_key));
mvaddstr (16, 2*GAME_WIDTH+8, form ("Rotate: '%c'", rotate_key));
mvaddstr (17, 2*GAME_WIDTH+8, form ("Drop: '%c'", drop_key));
mvaddstr (18, 2*GAME_WIDTH+8, form ("Pause: '%c'", susp_key));
mvaddstr (19, 2*GAME_WIDTH+8, form ("Quit: '%c'", quit_key));
mvaddstr (20, 2*GAME_WIDTH+8, "Refresh: '^L'");
}
/***************************************************************************\
|* *|
|* The function setup_curses should be called exactly once, near the *|
|* beginning of execution. It initialises curses(3x), and notes that *|
|* this has been done, by setting the global variable in_curses. *|
|* *|
\***************************************************************************/
void setup_curses ()
{
initscr ();
clear ();
#ifndef LINT
noecho ();
cbreak ();
#endif /* LINT */
in_curses = 1;
}
/***************************************************************************\
|* *|
|* The function update_scores() puts the sepecified values of score, *|
|* no_pieces and no_levels on the screen in the specified positions. *|
|* *|
\***************************************************************************/
void update_scores ()
{
move (8, 34);
clrtoeol ();
addstr (form ("%d", score));
move (9, 34);
clrtoeol ();
addstr (form ("%d", no_pieces));
move (10, 34);
clrtoeol ();
addstr (form ("%d", no_levels));
}
/***************************************************************************\
|* *|
|* The function draw_board() puts the current state of the global array *|
|* board[] ontop the curses(3x) screen, then refresh()es it. *|
|* *|
\***************************************************************************/
void draw_board ()
{
int i, j;
for (i = 0; i < GAME_DEPTH; i++)
for (j = 0; j < GAME_WIDTH; j++)
if (board[i+4][j] == PI_EMPTY) {
mvaddch (i, 2*j+2, BLANK_CHAR);
addch (BLANK_CHAR);
}
else
mvaddstr (i, 2*j+2, pieces[board[i+4][j]].app);
}
/***************************************************************************\
|* *|
|* The function draw_piece draws or erases one of the tetris pieces on *|
|* the screen in a specified orientation and position. The form of the *|
|* function is: *|
|* *|
|* draw_piece (piece_no, orientation, y, x, flag) *|
|* *|
|* All the arrguments are integers. Flag is either PD_DRAW or *|
|* PD_ERASE, specifying the effect of the function. Piece_no is *|
|* between 0 and 6 inclusive, and specifies what sort of piece is *|
|* required. Orientation is between 0 and 3 inclusive, and states *|
|* which way up the piece is to be drawn, and y and x express the *|
|* position as an index into the GAME_DEPTH by GAME_WIDTH array *|
|* that is the board. *|
|* *|
\***************************************************************************/
void draw_piece (piece_no, orient, y, x, flag)
int piece_no;
int orient;
int y;
int x;
int flag;
{
int i;
extern WINDOW *stdscr;
for (i = 0; i < NO_SQUARES; i++)
if (y+pieces[piece_no].index[orient][i][0] >= 0)
if (flag == PD_ERASE) {
mvaddch (y+pieces[piece_no].index[orient][i][0],
(2*(x+pieces[piece_no].index[orient][i][1]))+2,
BLANK_CHAR);
addch (BLANK_CHAR);
}
else
mvaddstr (y+pieces[piece_no].index[orient][i][0],
(2*(x+pieces[piece_no].index[orient][i][1]))+2,
pieces[piece_no].app);
}
/***************************************************************************\
|* *|
|* The function place_piece takes the same parameters as draw_piece, *|
|* except for the flag, and does not draw the piece, but places it *|
|* on the board. No checking is done to see if it will fit, since *|
|* should already have been done by can_place(). *|
|* *|
\***************************************************************************/
void place_piece (piece_no, orient, y, x)
int piece_no;
int orient;
int y;
int x;
{
int i;
for (i = 0; i < NO_SQUARES; i++)
board[y+4+pieces[piece_no].index[orient][i][0]]
[x+pieces[piece_no].index[orient][i][1]] = piece_no;
}
/***************************************************************************\
|* *|
|* The function can_place takes the same parameters as place_piece, *|
|* It does not draw the piece, nor place it on the board, but returns *|
|* an integer value -- 0 if the piece will not fit on the board, 1 if *|
|* it will (with the specified orientation, position, etc.) *|
|* *|
\***************************************************************************/
#define HERE(x) pieces[piece_no].index[orient][i][x]
int can_place (piece_no, orient, y, x)
int piece_no;
int orient;
int y;
int x;
{
int i;
for (i = 0; i < NO_SQUARES; i++)
if (((x+HERE(1) >= GAME_WIDTH) || /* Off right of screen or */
(x+HERE(1) < 0)) || /* Off left of screen */
(y+HERE(0) >= GAME_DEPTH) || /* Off bottom of screen */
(board[y+4+HERE(0)][x+HERE(1)] != PI_EMPTY))
/* Board position not empty */
return (0);
return (1);
}
/*-------------------------------------------------------------------------*/