From f978263374721fcb429054bac1a70612c90f5c06 Mon Sep 17 00:00:00 2001
From: Drahflow <drahflow@gmx.de>
Date: Tue, 6 May 2014 22:47:43 +0200
Subject: [PATCH] Simple sidescroller, connect via telnet.

---
 sidescroll/Makefile       |   5 +
 sidescroll/sidescroll.c++ | 478 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 483 insertions(+)
 create mode 100644 sidescroll/Makefile
 create mode 100644 sidescroll/sidescroll.c++

diff --git a/sidescroll/Makefile b/sidescroll/Makefile
new file mode 100644
index 0000000..7ad49fd
--- /dev/null
+++ b/sidescroll/Makefile
@@ -0,0 +1,5 @@
+sidescroll: sidescroll.o
+	g++ -ggdb -lzmq -lpthread -o $@ $<
+
+%.o: %.c++
+	g++ -ggdb -W -Wall -Wextra -pedantic -std=c++11 -c -o $@ $<
diff --git a/sidescroll/sidescroll.c++ b/sidescroll/sidescroll.c++
new file mode 100644
index 0000000..47f637b
--- /dev/null
+++ b/sidescroll/sidescroll.c++
@@ -0,0 +1,478 @@
+#include <zmq.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <vector>
+#include <math.h>
+#include <netinet/ip.h>
+#include <iostream>
+#include <sys/epoll.h>
+#include <memory>
+#include <algorithm>
+#include <sys/time.h>
+#include <set>
+
+#define WIDTH 480
+#define HEIGHT 70
+
+#define CMD_PIXEL 0
+#define CMD_BLIT 1
+
+#define BOMB_LOAD_TIME 1000
+
+struct msgBlit {
+  uint8_t cmd;
+  uint32_t x;
+  uint32_t y;
+  uint32_t w;
+  uint32_t h;
+  uint8_t data[WIDTH * HEIGHT];
+} __attribute__ ((packed));
+
+volatile msgBlit *screen;
+volatile msgBlit *offscreen;
+int epollFd;
+int frameNo;
+
+void *blitScreen(void *) {
+  void *context = zmq_ctx_new ();
+  void *requester;
+
+  requester = zmq_socket (context, ZMQ_REQ);
+  zmq_connect(requester, "tcp://mensadisplay:5556");
+  // zmq_connect(requester, "tcp://192.168.178.147:5570");
+  // zmq_connect(requester, "tcp://localhost:5556");
+
+  while(1) {
+    zmq_msg_t msg;
+    zmq_msg_init_size(&msg, sizeof(msgBlit));
+
+    memcpy(zmq_msg_data(&msg), const_cast<msgBlit *>(screen), sizeof(msgBlit));
+    zmq_msg_send(&msg, requester, ZMQ_SNDMORE);
+
+    zmq_msg_init_size(&msg, 0);
+    zmq_msg_send(&msg, requester, 0);
+    zmq_recv(requester, NULL, 0, 0);
+  }
+
+  zmq_close(requester);
+  zmq_ctx_destroy(context);
+
+  return nullptr;
+}
+
+void setPixel(int x, int y, bool on) {
+  if(x < 0 || x >= WIDTH) return;
+  if(y < 0 || y >= HEIGHT) return;
+
+  offscreen->data[WIDTH * y + x] = on? 255: 0;
+}
+
+void clearScreen() {
+  for(int i = 0; i < WIDTH * HEIGHT; ++i) offscreen->data[i] = 0;
+}
+
+void flipScreen() {
+  volatile msgBlit *tmp = offscreen;
+  screen = offscreen;
+  offscreen = tmp;
+}
+
+class Actor;
+
+std::vector<Actor *> friends;
+std::vector<Actor *> enemies;
+std::vector<Actor *> newFriends;
+std::vector<Actor *> newEnemies;
+
+std::set<Actor *> killed;
+
+class Actor {
+  public:
+    float x, y;
+
+    virtual void draw() = 0;
+    virtual void act() = 0;
+    virtual void kill() {
+      killed.insert(this);
+    }
+
+    Actor(float x, float y): x(x), y(y) { }
+
+    virtual ~Actor() { }
+};
+
+class FriendShot: public Actor {
+  float dx, dy;
+
+  public:
+    FriendShot(float x, float y, float dx, float dy): Actor(x, y), dx(dx), dy(dy) { }
+
+    void draw() {
+      setPixel(x, y, true);
+    }
+
+    void act() {
+      if(x < 0 || x > WIDTH) kill();
+      if(y < 0 || y > HEIGHT) kill();
+      
+      for(auto &a: enemies) {
+        float dx = a->x - x;
+        float dy = a->y - y;
+
+        if(dx * dx + dy * dy < 7) {
+          a->kill();
+        }
+      }
+
+      x += dx;
+      y += dy;
+    }
+};
+
+class Bomb: public Actor {
+  float dx, dy;
+
+  public:
+    Bomb(float x, float y, float dx, float dy): Actor(x, y), dx(dx), dy(dy) { }
+
+    void draw() {
+      setPixel(x - 1, y, true);
+      setPixel(x, y - 1, true);
+      setPixel(x + 1, y, true);
+      setPixel(x, y + 1, true);
+      setPixel(x, y, true);
+    }
+
+    void act() {
+      if(x < 0 || x > WIDTH) kill();
+      if(y < 0 || y > HEIGHT) kill();
+      
+      x += dx;
+      y += dy;
+
+      if(x > WIDTH / 2 && frameNo % 5 == 0) {
+        for(float v = 0.5; v < 2; v += 0.1) {
+          for(float f = 0; f < 2 * M_PI; f += 0.02) {
+            newFriends.push_back(new FriendShot(x, y, v * sin(f), v * cos(f)));
+          }
+        }
+        killed.insert(this);
+      }
+    }
+
+    void kill() { }
+};
+
+class Player: public Actor {
+  int fd;
+  int bombReload;
+
+  public:
+    Player(int fd): Actor(0, 39), fd(fd), bombReload(0) {
+      x = 10;
+
+      for(auto &a: friends) {
+        Player *p = dynamic_cast<Player *>(a);
+        if(p && p->x == x) x += 5;
+      }
+    }
+
+    int con() {
+      return fd;
+    }
+
+   void draw() {
+      if(bombReload > BOMB_LOAD_TIME && frameNo % 3 == 0) return;
+
+      setPixel(x - 2, y - 2, true);
+      setPixel(x - 2, y - 1, true);
+      setPixel(x - 2, y    , true);
+      setPixel(x - 2, y + 1, true);
+      setPixel(x - 2, y + 2, true);
+
+      setPixel(x - 1, y - 1, true);
+      setPixel(x - 1, y    , true);
+      setPixel(x - 1, y + 1, true);
+
+      setPixel(x    , y    , true);
+    }
+
+    void act() {
+      ++bombReload;
+    }
+
+    void handleInput(char c[16]) {
+      std::cout << (int)c[0] << ","
+                << (int)c[1] << ","
+                << (int)c[2] << ","
+                << (int)c[3] << std::endl;
+      if(c[0] == 27) {
+        if(c[2] == 65) y -= 5;
+        if(c[2] == 66) y += 5;
+        if(y < 0) y = 0;
+        if(y >= HEIGHT) y = HEIGHT - 1;
+      }
+      if(c[0] == 32) {
+        newFriends.push_back(new FriendShot(x, y, 2, 0));
+      }
+      if(c[0] == 'b' && bombReload > BOMB_LOAD_TIME) {
+        bombReload = 0;
+        newFriends.push_back(new Bomb(x, y, 1, 0));
+      }
+    }
+
+    void killConnection() {
+      epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, nullptr);
+      close(fd);
+    }
+};
+
+class EnemyShot: public Actor {
+  float dx, dy;
+
+  public:
+    EnemyShot(float x, float y, float dx, float dy): Actor(x, y), dx(dx), dy(dy) { }
+
+    void draw() {
+      setPixel(x, y, true);
+    }
+
+    void act() {
+      if(x < 0 || x > WIDTH) kill();
+      if(y < 0 || y > HEIGHT) kill();
+
+      for(auto &a: friends) {
+        float dx = a->x - x;
+        float dy = a->y - y;
+
+        if(dx * dx + dy * dy < 7) {
+          Player *p = dynamic_cast<Player *>(a);
+          if(p) p->killConnection();
+
+          a->kill();
+        }
+      }
+      
+      x += dx;
+      y += dy;
+    }
+};
+
+class Ufo: public Actor {
+  public:
+    Ufo(): Actor(500, rand() % 70) { }
+
+    void draw() {
+      for(int xx = x - 2; xx < x + 3; ++xx) {
+        for(int yy = y - 2; yy < y + 3; ++yy) {
+          setPixel(xx, yy, true);
+        }
+      }
+
+      setPixel(x, y, false);
+    }
+
+    void act() {
+      if(x < -5) {
+        kill();
+        return;
+      }
+
+      if(rand() % 25 == 0) {
+        newEnemies.push_back(new EnemyShot(x, y, -2, (rand() % 1000 - 500.0) / 500));
+      }
+
+      --x;
+    }
+};
+
+class Ufo2: public Actor {
+  public:
+    Ufo2(): Actor(500, rand() % 70) { }
+
+    void draw() {
+      for(int xx = x - 3; xx < x + 4; ++xx) {
+        for(int yy = y - 3; yy < y + 4; ++yy) {
+          setPixel(xx, yy, true);
+        }
+      }
+
+      setPixel(x, y, false);
+    }
+
+    void act() {
+      if(x < -5) {
+        kill();
+        return;
+      }
+
+      if((frameNo / 50) % 3 == 0) {
+        int ty = y;
+        int tx = 0;
+
+        int playerCount = 0;
+        for(auto &a: friends) {
+          Player *p = dynamic_cast<Player *>(a);
+          if(p) ++playerCount;
+        }
+
+        if(playerCount) {
+          int tp = (frameNo / 50) % playerCount;
+          for(auto &a: friends) {
+            Player *p = dynamic_cast<Player *>(a);
+            if(p && !tp--) {
+              ty = p->y;
+              tx = p->x;
+            }
+          }
+
+          if(tx != x) {
+            newEnemies.push_back(new EnemyShot(x, y, -1.5, (ty - y) / (tx - x) * -1.5));
+          }
+        }
+      }
+
+      x -= 0.1;
+    }
+};
+
+epoll_event *epollEvent(uint32_t events, int fd) {
+  static epoll_event ret;
+  ret.events = events;
+  ret.data.fd = fd;
+  return &ret;
+}
+
+epoll_event *epollEvent(uint32_t events, Actor *a) {
+  static epoll_event ret;
+  ret.events = events;
+  ret.data.ptr = a;
+  return &ret;
+}
+
+uint64_t now() {
+  struct timeval tv;
+  gettimeofday(&tv, nullptr);
+  return tv.tv_sec * 1000000ull + tv.tv_usec;
+}
+
+void nextFrame() {
+  ++frameNo;
+  clearScreen();
+
+  for(auto &a: friends) {
+    a->draw();
+    a->act();
+  }
+
+  for(auto &a: enemies) {
+    a->draw();
+    a->act();
+  }
+
+  if(rand() % 100 == 0) {
+    newEnemies.push_back(new Ufo());
+  }
+
+  if(rand() % 1600 == 0) {
+    newEnemies.push_back(new Ufo2());
+  }
+
+  for(auto &a: killed) {
+    enemies.erase(std::remove(enemies.begin(), enemies.end(), a), enemies.end());
+    friends.erase(std::remove(friends.begin(), friends.end(), a), friends.end());
+    delete a;
+  }
+  killed.clear();
+
+  for(auto &a: newFriends) friends.push_back(a);
+  newFriends.clear();
+  for(auto &a: newEnemies) enemies.push_back(a);
+  newEnemies.clear();
+
+  flipScreen();
+}
+
+int main() {
+  screen = new msgBlit;
+  screen->cmd = CMD_BLIT;
+  screen->x = 0;
+  screen->y = 0;
+  screen->w = WIDTH;
+  screen->h = HEIGHT;
+
+  offscreen = new msgBlit;
+  offscreen->cmd = CMD_BLIT;
+  offscreen->x = 0;
+  offscreen->y = 0;
+  offscreen->w = WIDTH;
+  offscreen->h = HEIGHT;
+
+  pthread_t blitter;
+  pthread_create(&blitter, nullptr, blitScreen, nullptr);
+
+  int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
+  struct sockaddr_in addr;
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(7000);
+  addr.sin_addr.s_addr = INADDR_ANY;
+  int ret = bind(serverSocket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
+  if(ret < 0) {
+    std::cerr << "Cannot bind: " << strerror(errno) << std::endl;
+    return 1;
+  }
+  listen(serverSocket, 16);
+
+  epollFd = epoll_create(8);
+  epoll_ctl(epollFd, EPOLL_CTL_ADD, serverSocket, epollEvent(EPOLLIN, serverSocket));
+
+  uint64_t nextFrameTime = 0;
+
+  while (1) {
+    epoll_event ev;
+    int ret = epoll_wait(epollFd, &ev, 1, 1);
+    if(ret == 1) {
+      if(ev.data.fd == serverSocket) {
+        int con = accept(serverSocket, nullptr, 0);
+        if(con < 0) {
+          std::cerr << "Cannot accept: " << strerror(errno) << std::endl;
+        } else {
+          Player *p = new Player(con);
+          newFriends.push_back(p);
+
+          epoll_ctl(epollFd, EPOLL_CTL_ADD, con, epollEvent(EPOLLIN, p));
+
+          unsigned char disableLinemode[] = {
+            255, 253, 34,                 /* IAC DO LINEMODE */
+            255, 250, 34, 1, 0, 255, 240, /* IAC SB LINEMODE MODE 0 IAC SE */
+            255, 251, 1                   /* IAC WILL ECHO */
+          };
+          write(con, disableLinemode, sizeof(disableLinemode));
+          char welcome[] = "Welcome. Cursor keys to move, Space to fire, b for bomb (only if player blinks)\r\n";
+          write(con, welcome, strlen(welcome));
+        }
+      } else {
+        char buf[16];
+        Player *p = reinterpret_cast<Player *>(ev.data.ptr);
+
+        int len = read(p->con(), buf, 16);
+        if(len <= 0) {
+          p->killConnection();
+          p->kill();
+        } else {
+          p->handleInput(buf);
+        }
+      }
+    }
+
+    if(now() > nextFrameTime) {
+      nextFrame();
+      nextFrameTime = now() + 10000;
+    }
+  }
+
+  return 0;
+}
-- 
GitLab