/* Copyright (c) 1992, Trevor Smigiel. All rights reserved. (I hope Commodore doesn't mind that I borrowed their copyright notice.) The source and executable code of this program may only be distributed in free electronic form, via bulletin board or as part of a fully non-commercial and freely redistributable diskette. Both the source and executable code (including comments) must be included, without modification, in any copy. This example may not be published in printed form or distributed with any commercial product. This program is provided "as-is" and is subject to change; no warranties are made. All use is at your own risk. No liability or responsibility is assumed. */ #include #include #include #include #include #include #include "tetris.h" #include "con_input.h" /* */ #include #include #include #include "Tetris_protos.h" #include "TetrisImages_protos.h" UBYTE *vers = "$VER: Tetris 1.0"; void KeyTask(void); void JoyTask(void); #define MAX_PLAYERS 3 #define STACK_SIZE 10000L void (*TaskEntries[])(void) = { KeyTask, KeyTask, KeyTask, JoyTask }; /* Each switch on a joystick corrosponds to a bit in cd_State */ /* The index of the character in the key string, is the same bit in cd_State */ /* bit 6 5 4 3 2 1 0 */ /* mb rb lb u d l r */ char *ControlKeys[] = { "\x22\x20\x21\x10\x11", /* and the same for these */ "\x28\x26\x27\x16\x17", /* ditto */ "\x2f\x2d\x2e\x1e\x3e", /* These keys are only good for Tetris */ NULL, /* joystick */ }; char *ControlUsage[] = { "keyboard: w - rotate, a - left, s - down, d - right.", "keyboard: i - rotate, j - left, k - down, l - right.", "numberpad: 8 - rotate, 4 - left, 5 - down, 6 - right.", "joystick: up/button - rotate, left - left, down - down, right - right.", }; struct ControlData Controls[MAX_PLAYERS]; struct Screen *Screen; struct Window *Window; struct Gadget *Gadgets; struct MsgPort *tetris_MsgPort; struct MsgPort *timer_MsgPort; struct timerequest *timer_IORequest; struct MinList Boards = {(struct MinNode *)&Boards.mlh_Tail, NULL, (struct MinNode *)&Boards}; WORD heights[2]; WORD PWidth = 0; WORD PHeight = 0; WORD NumPlayers = 0; WORD Players = 1; WORD Level = 0; WORD BSize = 8; WORD XSize = 16; WORD YSize = 16; WORD Depth = 2; WORD NoWaitLevel = 0; WORD Flags = 0; #define GFLG_QUIT 0x0001 void event_handler(void) { struct TBoard *board; ULONG signals, signaled; ULONG timer_signal, window_signal, tetris_signal; WORD paused = 0; int i, gameover, nextlevel; for (i = 0; i < Players; i++) { if (Controls[i].cd_Task) { board = Controls[i].cd_Board; StartLevel(board, Level); } } tetris_signal = (1 << tetris_MsgPort->mp_SigBit); timer_signal = (1 << timer_MsgPort->mp_SigBit); window_signal = (1 << Window->UserPort->mp_SigBit); signals = tetris_signal | timer_signal | window_signal | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F; while (NumPlayers > 0) { signaled = Wait(signals); if (signaled & SIGBREAKF_CTRL_C) Flags |= GFLG_QUIT; if (signaled & SIGBREAKF_CTRL_F) { /* Child is done */ for (i = 0; i < Players; i++) { if ((Controls[i].cd_Task != NULL) && (Controls[i].cd_MsgPort == NULL)) { FreeBoard(Controls[i].cd_Board); DeleteTask(Controls[i].cd_Task); Controls[i].cd_Board = NULL; Controls[i].cd_Task = NULL; NumPlayers--; } } } if (signaled & timer_signal) { GetMsg(timer_MsgPort); /* There is only one message -- timer_IORequest */ timer_IORequest->tr_time.tv_secs = 0; timer_IORequest->tr_time.tv_micro = UPDATE_TIME; SendIO((struct IORequest *)timer_IORequest); for (i = 0; i < Players; i++) { if (Controls[i].cd_Task) UpdateTetris(Controls[i].cd_Board); } } if (signaled & tetris_signal) { struct ControlMsg *cmsg; while (cmsg = (struct ControlMsg *)GetMsg(tetris_MsgPort)) { if ((!paused) && (board = cmsg->cm_CData->cd_Board)) { int state = cmsg->cm_State; if (state & 1) MoveTetris(board, COMMAND_RIGHT); else if (state & 2) MoveTetris(board, COMMAND_LEFT); if (state & 4) MoveTetris(board, COMMAND_DOWN); else if (state & 8) MoveTetris(board, COMMAND_ROTATE); /* Two rotates to accomodate joysticks */ if (state & 0x70) /* Any button */ MoveTetris(board, COMMAND_ROTATE); } ReplyMsg((struct Message *)cmsg); } } if (signaled & window_signal) { struct IntuiMessage *msg; while (msg = (struct IntuiMessage *)GetMsg(Window->UserPort)) { switch (msg->Class) { case IDCMP_CHANGEWINDOW: case IDCMP_ACTIVEWINDOW: if (Window->Height != heights[paused]) { if (paused) { paused = 0; for (i = 0; i < Players; i++) { if (Controls[i].cd_Task) Signal(Controls[i].cd_Task, SIGBREAKF_CTRL_E); } SetWindowTitles(Window, "Tetris", "Tetris"); if (CheckIO((struct IORequest *) timer_IORequest)) { timer_IORequest->tr_time.tv_secs = 0; timer_IORequest->tr_time.tv_micro = UPDATE_TIME; SendIO((struct IORequest *)timer_IORequest); } } else { case IDCMP_INACTIVEWINDOW: paused = 1; for (i = 0; i < Players; i++) { if (Controls[i].cd_Task) Signal(Controls[i].cd_Task, SIGBREAKF_CTRL_D); } SetWindowTitles(Window, "Paused", "Tetris"); if (!CheckIO((struct IORequest *) timer_IORequest)) { AbortIO((struct IORequest *) timer_IORequest); WaitIO((struct IORequest *) timer_IORequest); SetSignal(0, timer_signal); } } } break; case IDCMP_REFRESHWINDOW: BeginRefresh(Window); for (i = 0; i < Players; i++) { if (Controls[i].cd_Task) DrawBoard(Controls[i].cd_Board); } EndRefresh(Window, TRUE); break; case IDCMP_CLOSEWINDOW: Flags |= GFLG_QUIT; break; default: break; } ReplyMsg((struct Message *)msg); } } if (Flags & GFLG_QUIT) { for (i = 0; i < Players; i++) { if (Controls[i].cd_Task) Signal(Controls[i].cd_Task, SIGBREAKF_CTRL_C); } paused = 1; if (!CheckIO((struct IORequest *) timer_IORequest)) { AbortIO((struct IORequest *) timer_IORequest); WaitIO((struct IORequest *) timer_IORequest); SetSignal(0, timer_signal); } } if (!paused) { nextlevel = gameover = 0; for (i = 0; i < Players; i++) { if (Controls[i].cd_Task) { board = Controls[i].cd_Board; if (board->flags & TETF_GAMEOVER) gameover++; else if (board->flags & TETF_NEXTLEVEL) nextlevel++; } } if ((NoWaitLevel) || (nextlevel == (i - gameover))) { for (i = 0; i < Players; i++) { if (Controls[i].cd_Task) { board = Controls[i].cd_Board; if (board->flags & TETF_NEXTLEVEL) { StartLevel(board, board->clevel + 1); } } } } } } /* end Wait Loop */ } #define next event_handler BOOL StartOfGame(void) { struct ControlData *c; int x, y; struct Task *ThisTask = FindTask(NULL); for (c = &Controls[0], NumPlayers = 0; NumPlayers < Players; c++, NumPlayers++) { c->cd_Board = NULL; c->cd_Task = CreateTask("Tetris_ConTask", 0, TaskEntries[NumPlayers], STACK_SIZE); if (!c->cd_Task) break; c->cd_Board = NewBoard(Window->RPort, Window->BorderLeft + BSize + (PWidth + BSize * 2) * NumPlayers, heights[1]); if (!c->cd_Board) { DeleteTask(c->cd_Task); c->cd_Task = NULL; break; } printf("Player %d uses %s\n", NumPlayers, ControlUsage[NumPlayers]); c->cd_Parent = ThisTask; c->cd_MsgPort = tetris_MsgPort; c->cd_State = 0; c->cd_Keys = ControlKeys[NumPlayers]; c->cd_Task->tc_UserData = c; Signal(c->cd_Task, SIGBREAKF_CTRL_F); /* Signal task, the data is ready */ } if (NumPlayers != Players) { x = NumPlayers * (PWidth + BSize * 2) + Screen->WBorLeft + Screen->WBorRight; y = heights[0] = PHeight + Screen->WBorBottom + heights[1] + BSize; ChangeWindowBox(Window, (Screen->Width - x) / 2, (Screen->Height - y) / 2, x, y); } return NumPlayers; } void EndOfGame(void) { int i; for (i = 0; i < NumPlayers; i++) { /* Make sure everything is freed */ if (Controls[i].cd_Board) { printf("FreeBoard - this shouldn't have happened.\n"); FreeBoard(Controls[i].cd_Board); } if (Controls[i].cd_Task) { printf("DeleteTask - this shouldn't have happened.\n"); DeleteTask(Controls[i].cd_Task); } } } void tetris(void) { if (InitTetrisImages(Screen->BitMap.Depth, XSize, YSize)) { if (StartOfGame()) { /* Add Players */ next(); EndOfGame(); } FreeTetrisImages(Screen->BitMap.Depth, XSize, YSize); } } #undef next #define next tetris struct Window * tetris_window(void) { struct Window *w; WORD zoom[4]; heights[1] = Screen->WBorTop + Screen->Font->ta_YSize + 1 + YSize * 4; heights[0] = PHeight + Screen->WBorBottom + heights[1] + BSize; zoom[3] = heights[1]; zoom[2] = Players * (PWidth + (BSize * 2)) + Screen->WBorLeft + Screen->WBorRight; zoom[1] = (Screen->Height - heights[0]) / 2; zoom[0] = (Screen->Width - zoom[2]) / 2; w = OpenWindowTags(NULL, WA_Title, "Tetris", WA_ScreenTitle, "Tetris", WA_Left, zoom[0], WA_Top, zoom[1], WA_Width,zoom[2], WA_Height, heights[0], WA_Zoom, zoom, WA_Gadgets, Gadgets, WA_Flags, WFLG_CLOSEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_ACTIVATE,// | WFLG_GIMMEZEROZERO, WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_IDCMPUPDATE | IDCMP_CHANGEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW | IDCMP_REFRESHWINDOW, TAG_DONE ); return w; } void window(void) { Window = tetris_window(); if (Window) { next(); CloseWindow(Window); } } #undef next #define next window void screen(void) { Screen = LockPubScreen(NULL); if (Screen) { next(); UnlockPubScreen(NULL, Screen); } } #undef next #define next screen void timer(void) { timer_MsgPort = CreateMsgPort(); if (timer_MsgPort) { timer_IORequest = (struct timerequest *)CreateIORequest(timer_MsgPort, sizeof(struct timerequest)); if (timer_IORequest) { if (!OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_IORequest, 0L)) { timer_IORequest->tr_node.io_Command = TR_ADDREQUEST; timer_IORequest->tr_time.tv_secs = 0; timer_IORequest->tr_time.tv_micro = UPDATE_TIME; SendIO((struct IORequest *)timer_IORequest); next(); if (!CheckIO((struct IORequest *) timer_IORequest)) { AbortIO((struct IORequest *) timer_IORequest); WaitIO((struct IORequest *) timer_IORequest); } CloseDevice((struct IORequest *) timer_IORequest); } else printf("No timer.device!\n"); DeleteIORequest((struct IORequest *) timer_IORequest); } else printf("No timer io request!\n"); DeleteMsgPort(timer_MsgPort); } else printf("No timer msg port!\n"); } #undef next #define next timer void msgport(void) { tetris_MsgPort = CreateMsgPort(); if (tetris_MsgPort) { tetris_MsgPort->mp_Node.ln_Name = TETRIS_PORT; next(); DeleteMsgPort(tetris_MsgPort); } else printf("No player msg port!\n"); } #undef next #define next msgport BOOL ParseArgs(void) { struct RDArgs *RDArgs; char Template[] = "PLAYERS/N,LEVEL/N,DEPTH/N,XSIZE/N,YSIZE/N,BORDER/N,NOWAIT/S"; LONG Args[] = {0, 0, 0, 0, 0, 0, 0}; RDArgs = ReadArgs(Template, Args, NULL); if (RDArgs) { if (Args[0]) { Players = *(LONG *)Args[0]; if (Players > MAX_PLAYERS) Players = MAX_PLAYERS; } if (Args[1]) Level = *(LONG *)Args[1]; if (Args[2]) Depth = *(LONG *)Args[2]; if (Args[3]) XSize = *(LONG *)Args[3]; if (Args[4]) YSize = *(LONG *)Args[4]; if (Args[5]) BSize = *(LONG *)Args[5]; NoWaitLevel = Args[6]; PWidth = (PFWIDTH * XSize); PHeight = (PFHEIGHT * YSize); FreeArgs(RDArgs); return TRUE; } return FALSE; } int main(void) { printf("Tetris version 1.0\nCopyright (C) 1992 Trevor Smigiel.\n"); if (ParseArgs()) { if(!(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)) next(); } else PrintFault(IoErr(), "ReadArgs"); return 0; }