|
#define Uses_TKeys |
|
#define Uses_TApplication |
|
#define Uses_TDeskTop |
|
#define Uses_TEvent |
|
#define Uses_TMenuBar |
|
#define Uses_TSubMenu |
|
#define Uses_TMenuItem |
|
#define Uses_TStatusLine |
|
#define Uses_TStatusItem |
|
#define Uses_TStatusDef |
|
#include <tvision/tv.h> |
|
|
|
class SnakeApp : public TApplication |
|
{ |
|
|
|
public: |
|
|
|
SnakeApp(); |
|
|
|
static TMenuBar *initMenuBar(TRect); |
|
static TStatusLine *initStatusLine(TRect); |
|
|
|
}; |
|
|
|
class SnakeView : public TView |
|
{ |
|
|
|
TPoint pos; |
|
|
|
public: |
|
|
|
SnakeView(const TRect &bounds); |
|
|
|
void handleEvent(TEvent &ev) override; |
|
void draw() override; |
|
|
|
}; |
|
|
|
SnakeApp::SnakeApp() : |
|
TProgInit( &SnakeApp::initStatusLine, |
|
&SnakeApp::initMenuBar, |
|
&TApplication::initDeskTop ) |
|
{ |
|
// Create an instance of our Snake game and place it on the desktop. |
|
TRect r = deskTop->getExtent().grow(-2, -1); |
|
auto *snakeView = new SnakeView(r); |
|
deskTop->insert(snakeView); |
|
} |
|
|
|
TMenuBar *SnakeApp::initMenuBar(TRect r) |
|
{ |
|
r.b.y = r.a.y + 1; |
|
return new TMenuBar(r, |
|
*new TSubMenu("~F~ile", kbAltF) + |
|
*new TMenuItem("E~x~it", cmQuit, kbNoKey, hcNoContext, "Alt-X") |
|
); |
|
} |
|
|
|
TStatusLine *SnakeApp::initStatusLine(TRect r) |
|
{ |
|
r.a.y = r.b.y - 1; |
|
return new TStatusLine(r, |
|
*new TStatusDef(0, 0xFFFF) + |
|
*new TStatusItem("~Alt-X~ Exit", kbAltX, cmQuit) + |
|
*new TStatusItem(0, kbF10, cmMenu) |
|
); |
|
} |
|
|
|
SnakeView::SnakeView(const TRect &bounds) : |
|
TView(bounds) |
|
{ |
|
growMode = gfGrowHiX | gfGrowHiY; // So that it gets resized along the desktop. |
|
options |= ofSelectable; // Otherwise it won't react to key/mouse events. |
|
eventMask |= evKeyDown | evMouseDown | evMouseMove; |
|
pos.x = (bounds.b.x - bounds.a.x)/2; // Display the snake at the center of the screen. |
|
pos.y = (bounds.b.y - bounds.a.y)/2; |
|
} |
|
|
|
void SnakeView::handleEvent(TEvent &event) |
|
{ |
|
TView::handleEvent(event); |
|
bool handled = true; |
|
switch (event.what) |
|
{ |
|
case evKeyDown: |
|
{ |
|
switch (event.keyDown.keyCode) |
|
{ // Arrow keys move the snake by one position. |
|
case kbUp: |
|
pos.y = max(pos.y - 1, 0); |
|
break; |
|
case kbDown: |
|
pos.y = min(pos.y + 1, size.y - 1); |
|
break; |
|
case kbLeft: |
|
pos.x = max(pos.x - 2, 0); // The snake emoji is two columns wide, |
|
break; // so it moves by two horizontal positions. |
|
case kbRight: |
|
pos.x = min(pos.x + 2, size.x - 2); |
|
break; |
|
default: |
|
handled = false; |
|
break; |
|
} |
|
break; |
|
} |
|
// Mouse press and drag also move the snake: |
|
case evMouseDown: |
|
case evMouseMove: |
|
pos = makeLocal(event.mouse.where); |
|
break; |
|
default: |
|
handled = false; |
|
break; |
|
} |
|
if (handled) |
|
{ |
|
drawView(); |
|
clearEvent(event); |
|
} |
|
} |
|
|
|
void SnakeView::draw() |
|
{ |
|
if (0 <= size.x && 0 <= size.y) |
|
{ |
|
TDrawBuffer b; |
|
|
|
TColorAttr cSnake {0xcc33ff, 0x33cc33}; // {foreground, background} in RGB. |
|
TColorAttr cBgrnd {0xffcc00, 0xccff33}; |
|
|
|
for (int x = 0; x < size.x; ++x) |
|
b.moveStr(x, "░", cBgrnd); |
|
|
|
writeLine(0, 0, size.x, size.y, b); // Draw the background. |
|
|
|
b.moveStr(0, "🐍", cSnake); |
|
writeLine(pos.x, pos.y, 2, 1, b); // Draw the snake. |
|
} |
|
} |
|
|
|
int main() |
|
{ |
|
SnakeApp app; |
|
app.run(); |
|
app.shutDown(); |
|
} |