2012-03-11 10:57:13 -04:00
|
|
|
/*******************************************************************************
|
|
|
|
|
|
|
|
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU General Public License as published by the Free
|
|
|
|
Software Foundation, either version 3 of the License, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
pong.cpp
|
|
|
|
Two-player pong.
|
|
|
|
|
|
|
|
*******************************************************************************/
|
|
|
|
|
2011-08-11 16:51:04 -04:00
|
|
|
#include <libmaxsi/platform.h>
|
|
|
|
#include <libmaxsi/io.h>
|
|
|
|
#include <libmaxsi/thread.h>
|
2011-09-06 10:16:44 -04:00
|
|
|
#include <libmaxsi/string.h>
|
2011-08-11 16:51:04 -04:00
|
|
|
#include <libmaxsi/sortix-vga.h>
|
|
|
|
#include <libmaxsi/sortix-sound.h>
|
2012-01-22 17:46:41 -05:00
|
|
|
#include <sys/keycodes.h>
|
|
|
|
#include <sys/termmode.h>
|
2011-11-10 06:27:31 -05:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2011-11-25 07:38:31 -05:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
2011-12-04 09:04:06 -05:00
|
|
|
#include <error.h>
|
2011-11-25 07:38:31 -05:00
|
|
|
#include <string.h>
|
2011-08-11 16:51:04 -04:00
|
|
|
|
|
|
|
using namespace Maxsi;
|
|
|
|
|
|
|
|
int Init();
|
|
|
|
void Reset();
|
|
|
|
void ClearScreen();
|
|
|
|
void Collision();
|
|
|
|
void Goal(nat player);
|
|
|
|
void UpdateUI();
|
|
|
|
void Update();
|
|
|
|
void ReadInput();
|
|
|
|
int main(int argc, char* argv[]);
|
|
|
|
|
|
|
|
const int width = 80;
|
|
|
|
const int height = 25;
|
|
|
|
const int padsize = 5;
|
|
|
|
|
|
|
|
const unsigned goalfreq = 800;
|
|
|
|
const unsigned collisionfreq = 1200;
|
|
|
|
|
2011-11-25 07:38:31 -05:00
|
|
|
int vgafd;
|
|
|
|
uint16_t frame[width*height];
|
2011-08-11 16:51:04 -04:00
|
|
|
|
|
|
|
int ballx;
|
|
|
|
int bally;
|
|
|
|
int oldballx;
|
|
|
|
int oldbally;
|
|
|
|
int ballvelx;
|
|
|
|
int ballvely;
|
|
|
|
int p1y;
|
|
|
|
int p2y;
|
|
|
|
bool p1vup;
|
|
|
|
bool p1vdown;
|
|
|
|
bool p2vup;
|
|
|
|
bool p2vdown;
|
|
|
|
unsigned p1score;
|
|
|
|
unsigned p2score;
|
|
|
|
unsigned time;
|
|
|
|
unsigned soundleft;
|
|
|
|
|
2011-11-25 07:38:31 -05:00
|
|
|
bool FlushVGA()
|
2011-08-11 16:51:04 -04:00
|
|
|
{
|
2012-03-24 10:34:30 -04:00
|
|
|
return writeall(vgafd, frame, sizeof(frame)) < sizeof(frame);
|
2011-11-25 07:38:31 -05:00
|
|
|
}
|
2011-08-11 16:51:04 -04:00
|
|
|
|
2011-11-25 07:38:31 -05:00
|
|
|
int Init()
|
|
|
|
{
|
2012-09-08 16:46:20 -04:00
|
|
|
bool has_vga_mode_set = true;
|
|
|
|
FILE* modefp = fopen("/dev/video/mode", "r");
|
|
|
|
if ( modefp )
|
|
|
|
{
|
|
|
|
char* mode = NULL;
|
|
|
|
size_t modesize;
|
|
|
|
if ( 0 <= getline(&mode, &modesize, modefp) )
|
|
|
|
{
|
|
|
|
if ( strcmp(mode, "driver=none\n") != 0 )
|
|
|
|
has_vga_mode_set = false;
|
|
|
|
free(mode);
|
|
|
|
}
|
|
|
|
fclose(modefp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !has_vga_mode_set )
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Sorry, this game only works in VGA mode.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-11-25 07:38:31 -05:00
|
|
|
vgafd = open("/dev/vga", O_RDWR);
|
2011-12-04 09:04:06 -05:00
|
|
|
if ( vgafd < 0 ) { error(0, errno, "unable to open vga device /dev/vga"); return 1; }
|
2011-08-11 16:51:04 -04:00
|
|
|
|
|
|
|
Reset();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
ClearScreen();
|
|
|
|
|
|
|
|
ballx = width/2;
|
|
|
|
bally = height/2;
|
|
|
|
ballvelx = -1;
|
|
|
|
ballvely = -1;
|
2011-11-28 05:48:02 -05:00
|
|
|
oldballx = ballx - ballvelx;
|
|
|
|
oldbally = bally - ballvely;
|
2011-08-11 16:51:04 -04:00
|
|
|
p1y = (height-padsize) / 5;
|
|
|
|
p2y = (height-padsize) / 5;
|
|
|
|
p1vup = false;
|
|
|
|
p1vdown = false;
|
|
|
|
p2vup = false;
|
|
|
|
p2vdown = false;
|
|
|
|
p1score = 0;
|
|
|
|
p2score = 0;
|
|
|
|
time = 0;
|
|
|
|
soundleft = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClearScreen()
|
|
|
|
{
|
|
|
|
for ( int y = 0; y < height; y++ )
|
|
|
|
{
|
|
|
|
uint16_t color = COLOR8_BLACK << 8;
|
|
|
|
|
|
|
|
for ( int x = 0; x < width; x++ )
|
|
|
|
{
|
2011-11-25 07:38:31 -05:00
|
|
|
frame[x + y*width] = ' ' | color;
|
2011-08-11 16:51:04 -04:00
|
|
|
}
|
|
|
|
}
|
2011-11-25 07:38:31 -05:00
|
|
|
|
|
|
|
FlushVGA();
|
2011-08-11 16:51:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Collision()
|
|
|
|
{
|
|
|
|
System::Sound::SetFrequency(collisionfreq);
|
|
|
|
soundleft = 40;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Goal(nat player)
|
|
|
|
{
|
2011-11-25 07:38:31 -05:00
|
|
|
frame[ballx + bally*width] = ' ' | (COLOR8_WHITE << 8);
|
2011-08-11 16:51:04 -04:00
|
|
|
|
2011-11-10 06:27:31 -05:00
|
|
|
int offset = (rand() % 4) - 2;
|
2011-08-11 16:51:04 -04:00
|
|
|
ballx = width/2;
|
2011-11-10 06:27:31 -05:00
|
|
|
bally = height/2 + offset;
|
2011-08-11 16:51:04 -04:00
|
|
|
|
|
|
|
if ( player == 1 )
|
|
|
|
{
|
|
|
|
ballvelx = 1;
|
2011-11-10 06:27:31 -05:00
|
|
|
ballvely = (rand() % 2) * 2 - 1;
|
2011-08-11 16:51:04 -04:00
|
|
|
p1score++;
|
|
|
|
}
|
|
|
|
else if ( player == 2 )
|
|
|
|
{
|
|
|
|
ballvelx = -1;
|
2011-11-10 06:27:31 -05:00
|
|
|
ballvely = (rand() % 2) * 2 - 1;
|
2011-08-11 16:51:04 -04:00
|
|
|
p2score++;
|
|
|
|
}
|
|
|
|
|
2011-11-10 06:27:31 -05:00
|
|
|
if ( ballvely > 0 ) { ballvely /= ballvely; }
|
|
|
|
if ( ballvely < 0 ) { ballvely /= -ballvely; }
|
|
|
|
|
2011-08-11 16:51:04 -04:00
|
|
|
System::Sound::SetFrequency(goalfreq);
|
|
|
|
soundleft = 50;
|
|
|
|
|
2012-03-21 19:52:29 -04:00
|
|
|
UpdateUI();
|
2011-08-11 16:51:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void UpdateUI()
|
|
|
|
{
|
2011-11-25 07:38:31 -05:00
|
|
|
for ( int x = 0; x < width; x++ ) { frame[x] = ' ' | (COLOR8_LIGHT_GREY << 12) | (COLOR8_RED << 8); }
|
2011-09-06 10:16:44 -04:00
|
|
|
|
|
|
|
char num[12];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = String::ConvertUInt32(p1score, num);
|
2011-11-25 07:38:31 -05:00
|
|
|
for ( int i = 0; i < len; i++ ) { frame[i] = ( frame[i] & 0xFF00 ) | num[i]; }
|
2011-09-06 10:16:44 -04:00
|
|
|
|
|
|
|
len = String::ConvertUInt32(p2score, num);
|
2011-11-25 07:38:31 -05:00
|
|
|
for ( int i = 0; i < len; i++ ) { frame[width - len + i] = ( frame[width - len + i] & 0xFF00 ) | num[i]; }
|
2011-08-11 16:51:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Update()
|
|
|
|
{
|
|
|
|
int p1v = 0, p2v = 0;
|
|
|
|
|
|
|
|
if ( p1vup && !p1vdown ) { p1v = -1; } else if ( !p1vup && p1vdown ) { p1v = 1; }
|
|
|
|
if ( p2vup && !p2vdown ) { p2v = -1; } else if ( !p2vup && p2vdown ) { p2v = 1; }
|
|
|
|
|
|
|
|
if ( p1v < 0 && p1y > 1 ) { p1y--; }
|
|
|
|
if ( p1v > 0 && p1y + padsize < height ) { p1y++; }
|
|
|
|
if ( p2v < 0 && p2y > 1 ) { p2y--; }
|
|
|
|
if ( p2v > 0 && p2y + padsize < height ) { p2y++; }
|
|
|
|
|
|
|
|
for ( int y = 1; y < height; y++ )
|
|
|
|
{
|
2011-11-25 07:38:31 -05:00
|
|
|
uint16_t color = ( y < p1y || y >= p1y + padsize ) ? COLOR8_BLACK << 12 : COLOR8_RED << 12; frame[y*width] = ' ' | color;
|
2011-08-11 16:51:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for ( int y = 1; y < height; y++ )
|
|
|
|
{
|
2011-11-25 07:38:31 -05:00
|
|
|
uint16_t color = ( y < p2y || y >= p2y + padsize ) ? COLOR8_BLACK << 12 : COLOR8_BLUE << 12; frame[width-1 + y*width] = ' ' | color;
|
2011-08-11 16:51:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( bally + ballvely <= 1 ) { ballvely = 0 - ballvely; Collision(); }
|
|
|
|
if ( bally + ballvely >= height ) { ballvely = 0 - ballvely; Collision(); }
|
|
|
|
|
|
|
|
if ( ballx + ballvelx < 1 ) { if ( bally + ballvely < p1y - 1 || bally + ballvely > p1y + padsize + 1 ) { Goal(2); } else { ballvelx = 0 - ballvelx; Collision(); } }
|
|
|
|
if ( ballx + ballvelx >= width-1 ) { if ( bally + ballvely < p2y - 1 || bally + ballvely > p2y + padsize + 1 ) { Goal(1); } else { ballvelx = 0 - ballvelx; Collision(); } }
|
|
|
|
|
2011-11-25 07:38:31 -05:00
|
|
|
frame[oldballx + oldbally*width] = ' ' | (COLOR8_WHITE << 8);
|
|
|
|
frame[ballx + bally*width] = '.' | (COLOR8_WHITE << 8);
|
2011-08-11 16:51:04 -04:00
|
|
|
oldballx = ballx; oldbally = bally;
|
|
|
|
|
|
|
|
ballx += ballvelx;
|
|
|
|
bally += ballvely;
|
|
|
|
|
2011-11-25 07:38:31 -05:00
|
|
|
frame[ballx + bally*width] = 'o' | (COLOR8_WHITE << 8);
|
2011-08-11 16:51:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void ReadInput()
|
|
|
|
{
|
2012-01-22 17:46:41 -05:00
|
|
|
unsigned termmode = TERMMODE_KBKEY
|
|
|
|
| TERMMODE_UNICODE
|
|
|
|
| TERMMODE_SIGNAL
|
|
|
|
| TERMMODE_NONBLOCK;
|
|
|
|
if ( settermmode(0, termmode) ) { error(1, errno, "settermmode"); }
|
|
|
|
while ( true )
|
2011-08-11 16:51:04 -04:00
|
|
|
{
|
2012-01-22 17:46:41 -05:00
|
|
|
uint32_t codepoint;
|
|
|
|
ssize_t numbytes = read(0, &codepoint, sizeof(codepoint));
|
|
|
|
if ( !numbytes ) { return; }
|
|
|
|
if ( numbytes < 0 ) { return; }
|
|
|
|
int kbkey = KBKEY_DECODE(codepoint);
|
|
|
|
int abskbkey = (kbkey < 0) ? -kbkey : kbkey;
|
|
|
|
if ( kbkey == KBKEY_ENTER ) { Reset(); }
|
|
|
|
if ( abskbkey == KBKEY_W ) { p1vup = (0 < kbkey); }
|
|
|
|
if ( abskbkey == KBKEY_S ) { p1vdown = (0 < kbkey); }
|
|
|
|
if ( abskbkey == KBKEY_UP ) { p2vup = (0 < kbkey); }
|
|
|
|
if ( abskbkey == KBKEY_DOWN ) { p2vdown = (0 < kbkey); }
|
2011-08-11 16:51:04 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-08 14:55:43 -04:00
|
|
|
int usage(int /*argc*/, char* argv[])
|
2011-11-10 06:27:31 -05:00
|
|
|
{
|
|
|
|
printf("usage: %s [OPTIONS]\n", argv[0]);
|
|
|
|
printf("Options:\n");
|
|
|
|
printf(" --speed <miliseconds> How many miliseconds between updates\n");
|
|
|
|
printf(" --usage Display this screen\n");
|
|
|
|
printf(" --help Display this screen\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-11 16:51:04 -04:00
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
2011-11-10 06:27:31 -05:00
|
|
|
int sleepms = 50;
|
|
|
|
for ( int i = 1; i < argc; i++ )
|
|
|
|
{
|
|
|
|
if ( String::Compare(argv[i], "--help") == 0 ) { return usage(argc, argv); }
|
|
|
|
if ( String::Compare(argv[i], "--usage") == 0 ) { return usage(argc, argv); }
|
|
|
|
if ( String::Compare(argv[i], "--speed") == 0 && 1 < argc-i )
|
|
|
|
{
|
|
|
|
sleepms = String::ToInt(argv[++i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-11 16:51:04 -04:00
|
|
|
int result = Init();
|
|
|
|
if ( result != 0 ) { return result; }
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
2011-11-28 05:48:02 -05:00
|
|
|
Thread::USleep(sleepms * 1000);
|
2011-08-11 16:51:04 -04:00
|
|
|
ReadInput();
|
|
|
|
Update();
|
|
|
|
UpdateUI();
|
2011-11-25 07:38:31 -05:00
|
|
|
FlushVGA();
|
2012-09-08 14:55:43 -04:00
|
|
|
if ( /*soundleft < 0*/ false ) { continue; }
|
2011-08-28 10:59:07 -04:00
|
|
|
if ( soundleft <= 50 )
|
|
|
|
{
|
|
|
|
System::Sound::SetFrequency(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
soundleft -= sleepms;
|
|
|
|
}
|
2011-08-11 16:51:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|