1.

Interloper is a turn based strategy game where each player tries to reach the opposing ‘base’ or eliminate all opponents.  One could almost think of it as a post nuclear raid in an underground bunker, eliminating the unwanted intruders.  As the teams make their way through the tunnels, they have to ration their time units to fire, or hide or duck back along corridors.  Audio clues from enemies firing their guns or from the brief glimpses around corners warn you that you’re not alone in this game.  Currently the game is set up for one human player to play against a computer controlled opponent.  A two player hot-seat version is playable, as well as an AI against AI fight, but isn’t very entertaining to watch, as it takes a while for a player to move his team through the maze.

The goal of the game was to mimic the old school XCOM games using 3D graphics.  The feeling of frustration when you’re close to an enemy but can’t fire, the methodical tactics -- moving your units out slowly to support one another, these place this game from a simpler time.  Our final result wasn’t an emotional chiller, nor as dark as we may have wanted, but it still remained fun to write. Multiple camera angles, 3DS generated Models (WITH Texture coordinates that we couldn’t find texture maps that would fit on said models…J), sound, dynamic terrain generation, visibility and unit targeting, these are most of the entertaining features of our game.  While we’ve made a try at physics based movement and collision detection, we still retained the original tile based movement scheme.

Controls for this game are straightforward. The middle mouse button will provide intuitive movement for a unit, with the left button providing fire control.  The right button drops down the list of keyboard commands as well as acting as a multiple-choice mouse option.  Targeting different units and switching between units are normally handled via the ‘f’ and ‘/tab’ keys, with the ‘w’ button switching between the viewpoints.  At the end of the turn, you can signal being done by pressing the ‘e’ key and ending your turn.

 

Major Modules:

 

Players – Contained a large amount of the code.  All unit actions that a player (human or computer) could do or needed to do was encapsulated in this class

 

class Players {

 public:

   Players();

   ~Players() {}

 

   //! Initialize the player

   bool Init(struct fVec3 *pos, int face, int color);

   void ResetTimer() { timeLeft = maxTime;}

 

   //! Render a Player

   bool LoadModel(char * name);

   bool Render() const;

   void UpdateState(float timeElapsed);

  

   //!PrintFunctions (stdout)

   void PrintStats(); //Print attributes

   void PrintEnemyList(); //Print the visible list of enemies

   void InsertVisList(Players * enemy_ptr);

   void ClearVisList();

   int GetNumSeen() {return vis_enemies.size();}

   void SetRenderMode(Render_Mode mode);

 

   //!ScreenPrint Functions

   string GetHealthString();

   string GetTUString();

 

   //! Return Vectors

   struct fVec3 Facing() const;

   int RotationFacing() const {return rotationFacing;}

   struct fVec3 GetPosition() const {return position;}

 

  //! movement Calls

   bool Forward();

   bool TurnLeft();

   bool TurnRight();

 

   //! Combat Calls

   bool Shoot();

   Weapon weapon;

   bool LoseHealth(int x);

 

   //! Enemy Calls

   Players* NextCurrent();

   Players* GetCurrentEnemy();

   Players* NextCurrentEnemy();

 

   //! Camera following

   void FollowPlayer(Camera *cam);

   Camera * ThisCamera() { return myCamera;}

   void ReleaseCamera();

   void UpdateFollower(float timeElapsed);

   void ChangeView() { myView = !myView;}

   void ResetView() { myView = 0;}

   int ViewType() { return myView;}

 

   bool isDead() const

      {return (hp<=0);}

   bool isAlive() const

      {return (hp>0);}

   bool isCommanded();

   bool notUpdated();

   bool atEnd();

   bool active() {return AI;}

   bool setActive(bool value) {AI = value; return AI;}

bool shortCircuit;

   int Sonar();

   void setEnd(char x) { endPos = x;}

 private:

   struct fVec3 position;

   struct fVec3 velocity;

   struct fVec3 facing;

   struct fVec3 destination;

   Quad rotation;

   Quad finalRotation;

   short rotationFacing;

   Camera * myCamera;

   int myView;

   bool command;

   bool facingOK;

   bool updated;

   bool AI;

   list<Players*> vis_enemies;

   

   int unitColor;

   int timeLeft;

   int maxTime;

   int hp;

   Players* currentEnemy;

   char endPos;

   Render_Mode render_mode;

   Model3ds model;

};

 

Game – The highest level of our code, this was the display and hardware level.

public:

  Game();

  virtual ~Game();

 

  //! default game window size and title

  static const int DEFAULT_WINDOW_WIDTH  = 640;

  static const int DEFAULT_WINDOW_HEIGHT = 480;

  static const int MIN_WINDOW_SIZE = 5;

  static const char* WINDOW_TITLE;

 

  //! initialize game data

  bool Init(const char* levelFilename);

 

  //! exectute main game loop

  void Run();

 

  //! Switches Turns

  void TurnOver();

 

private:

 

  //! initialize main window

  bool _InitWindow();

 

  //! initialize the popup menu

  bool _InitPopupMenu(); 

 

  //! read level description file

  bool _ReadLevel(const char* levelFilename);

 

  //! renders world contents to windows

  void _Render();  

 

The World level dealt with the different teams of the game individually, containing both the active camera baring units as well as keeping track of who was alive on each team of Players.

 

  //! Initialize the world

  bool Init(const char* skyboxPath, const char* courseFilename);

    

  //! Render the world

  bool Render();

  bool Collision(fVec3 position, fVec3 target, float radius);

  //! update world state for single game loop iteration

  //  timeElapsed is in seconds

  void UpdateState(float timeElapsed);

  void TurnOver();

  bool allDead();

  bool StillMoving();

  bool gameOver() { return won;}

  bool playerTurn() { return currentTurn;}

  Players* ViewChange();

  void UpdateEnemyList(Players * activePlayer, bool playerTurn);

  bool InSight(fVec3 p_pos, fVec3 en_pos);

  void PrintEnemyList();

  void CreateProjectile(struct fVec3 src, struct fVec3 targ,int dmg, float rge,$

             float  blast_rad, float speed, int time );

  void UpdateProjectiles(int time);

  void PrintProjectileList();

  void Action(int action);

 

  Players *player1;

  Players *player2;

 

 private:

 

  Skybox   _skybox;        //!< textured skybox cube

  //  Course   _course;        //!< course map == singleton, _course

  // Needs to use course, where did it go again?

 

  void runLights();

  int getNearest(struct fVec3 *location, struct fVec3 *dest);

  int getLoc(struct fVec3 *location, int val);

  int UnitReplace();

 

  Octal    *_Octal;

  bool won;

  bool currentTurn;

 

  Players team1[NUM_UNITS];

  Players team2[NUM_UNITS];

  list<Projectile> bullets;

 

  //AI information

  void AI(Players *, Players **);

  void AI2(Players *, Players **);  //Dumb AI

  bool TeamMoving(Players*);

  int AIcount;

  int currentAI;

  bool team1AI;

  bool team2AI;

};

Support Modules

Sound – Straightforward Singleton class.  This controlled playing sound within the game code.  An example of not well utilized code, as we only had a few background sounds.

 

Artifact – Handled the majority of the objects in the world.  Consisted of mainly display lists, and rendering instructions.

 

//! Turns on all the objects in the world

bool InitWorldTexture(const char* texturePath);

 

void displaySquare(int type);  //Helper function to create square objects

void displayObjCreate(int nameAndTexture, int secondTexture);

void displayFloor();

void displayInter();

void displayHorizontal();

void displayVertical();

void displayWall();

void displayDoor();

 

Object * makeFloor(float x, float y, float z);

Object * makeInter(float x, float y, float z);

Object * makeHorizontal(float x, float y, float z);

Object * makeVertical(float x, float y, float z);

Object * makeWall(float x, float y, float z);

Object * makeDoor(float x, float y, float z);

 

Weapon – a modular class to encapsulate weapons.  Due to time constraints and other limitations, we ended up with a single generic weapon for our game.

  

class Weapon{

public:

   Weapon();

   Weapon(string nme, int dam,float rge, int cost, int blast_rad, float bull_ra$

   void SetWeapon(string nme, int dam,float rge, int cost, int blast_rad, float$

   void Fire(struct fVec3 source, struct fVec3 target);

      

   string GetName()

      {return name;}

 

   int  GetDamage()

      {return damage;}

   float GetRange()

      {return range;}

   float GetBlastRadius()

     {return blast_radius;}

   int GetTimeUnitCost()

      {return tu_cost;}

    float GetRadius()

      {return radius;}

    float GetSpeed()

       {return speed;}

   private:

  string name;

  int damage;

  float range;

  int tu_cost;

  float blast_radius;

  float radius;

  float speed;

};

Octal – An octal tree for geometry clipping

class Octal {

 public:

   Octal() {Init();}

   ~Octal() {Destroy();}

 

  //! Initialize/Destroy the Quad tree

  void Init();

  void Destroy();

 

  //! Exposing values

  struct fVec3 Center() { return myCenter;} 

  float Size() { return myEdge;}

  //! Node/Leaf

  bool setCenter(struct fVec3 newCenter, float side);

  bool IsNode() { return isNode;}

  //! Add element to the Quad tree

  void Add(Object *a);

  //! Prints out in order lowest block

  void PrintOctal(Octal *root);

  //! Temporary drawing functions

  void drawAll(struct fVec3 *ptr, int total);

 

 

Camera – A simple class that made the turn based concept MUCH simpler by allowing us to “pass it” to units within the world rather than giving everyone one. This is a great idea that I wish we had implemented more. It handled who saw who as well as visibility stuff.

 

struct CameraState

{

  float eye[3];     //! eye position

  float target[3];  //! 'lookAt' target

  float up[3];      //! camera 'up' direction

};

Quad – a Quaternion class for the third person camera. Barely modified from previous incarnations

 

class Quad {

 public:

   Quad();

   Quad Inverse() const;

   float Norm() const;

 

   struct fVec3 AxisOfRotation() const;

   float AngleOfRotation() const;

   void Quad::AngleToQuad(float angle);

   Quad operator*(Quad a);

   bool Quad::operator!=(Quad a) const;

   bool Quad::operator==(Quad a) const;

   void setQuad(Quad a);

   void quad_from_matrix(float *mat);

   void matrix_from_quad(float *mat) const;

   void matrix_from_quad(float *mat, struct fVec3 pos) const;

   struct fVec3 Quad::fromZAxis() const;

   float AngleBetween(Quad a) const;

Object – a derived class that served as a utility for rendering

 

class Object {

 public:

   Object() {Init();}

   //   virtual ~Object() {};

 

   //! Initialize an object

   virtual bool Init();

   virtual bool Init(float, float, float, Object*, GLuint, int, float, float);

   virtual bool Destroy();

 

   //! Render an object

   void Render() const;

 

   //! update state information

   void UpdateState(float timeElapsed);

 

   //! return internals

   struct fVec3 Position() const {return position;}

   int Type() const {return type;}

   float Radius() const {if(type == SPHERE) return arm; return -1;}

   float Side() const {if(type == CUBE) return arm; return -1;}

   float Base() const {if(type == BOX) return arm; return -1;}

   float BaseHeight() const {if(type == BOX) return arm2; return -1;}

 

   //! Linked List calls

   Object *childOf() const {return child;}

   bool addChild(Object *Child);

   Object *removeHead();

 

 

Course – we used the same duplicate design from labs for our game it seemed in the spirit of XCOM and efficient

class Course {

 

 public:

   inline static Course& getInstance() {

      if(!s_pCourse) s_pCourse = new Course();

      return *s_pCourse;

   }

  static Course *s_pCourse;

 

  Course();

  virtual ~Course();

  //! Initialize the map

  bool Init(const char* courseFilename);

  //! Render the course using the current viewing transform

  bool Render() const;

  //! access the course map

  unsigned char GetMapLocation(int x, int y) const

    { assert(x >= 0 && x < _courseWidth && y >= 0 && y < _courseLength);

      return _courseMap[(y * _courseWidth) + x]; }

 

  void SetMapLocation(int x, int y, unsigned char value)

    { assert(x >= 0 && x < _courseWidth && y >= 0 && y < _courseLength);

      _courseMap[(y * _courseWidth) + x] = value; }

unsigned short Width() const

     { return _courseWidth;}

  unsigned short Height() const

     { return _courseLength;}

  unsigned short maxDim() const

     {

        if(_courseWidth > _courseLength)

           return _courseWidth;

        return _courseLength;

     }

   

  //! Output the course to a stream

  bool WriteToStream(ostream& os) const;

 

  //! Takes course and puts it into a vector point list

  bool Course::MakeList(struct fVec3 ** output, int * total);

  float Course::GetHeight(int i, int j);

  bool Course::isValidPos(float x, float y, float z);

  struct fVec3 * Course::fillOctal(Octal * head);

 private:

     

  // Data

          

  unsigned char* _courseMap;    //!< course map buffer

  unsigned short _courseWidth;   //!< size of course grid width

  unsigned short _courseLength;  //!< size of course grid length

 

Peripheral Modules

 

Vector – A Vector class that provided geometric functions as needed.

 

struct fVec3 {

   float pts[3];

};

 

void setVector(struct fVec3*, float, float, float);

void setVector(struct fVec3*,struct fVec3*);

 

//Adds X + Y => X

void addVector(struct fVec3*,struct fVec3);

void multVector(struct fVec3*,float);

void divVector(struct fVec3*, int);

 

float absF(float x);

 

3DS – a simple 3DS file parser/class stolen from the web implemented and modified for use in our own model class

 

class CLoad3DS

{

public:

    CLoad3DS();                             // This inits the data members

 

    // This is the function that you call to load the 3DS

    bool Import3DS(t3DModel *pModel, char *strFileName);

 

private:

    // This reads in a string and saves it in the char array passed in

    int GetString(char *);

 

    // This reads the next chunk

    void ReadChunk(tChunk *);

 

    // This reads the next large chunk

    void ProcessNextChunk(t3DModel *pModel, tChunk *);

 

    // This reads the object chunks

    void ProcessNextObjectChunk(t3DModel *pModel, t3DObject *pObject, tChunk *);

 

    // This reads the material chunks

    void ProcessNextMaterialChunk(t3DModel *pModel, tChunk *);

 

    // This reads the RGB value for the object's color

    void ReadColorChunk(tMaterialInfo *pMaterial, tChunk *pChunk);

 

    // This reads the objects vertices

    void ReadVertices(t3DObject *pObject, tChunk *);

 

    // This reads the objects face information

    void ReadVertexIndices(t3DObject *pObject, tChunk *);

 

Union-find && mazegen:  These were fairly simple union find algorithm on a character array that produces a solution from one start point to one endpoint. Cell based so not especially attractive but Utilitarian Practical and was quick to get up and running. We intentionally separated from the rest of the code as to leave it modularly producing text files. Also we could generate our own maps for the purpose of debugging.

 

 

Content

All of the models and textures were ‘borrowed’ from various web sources.  Although we may have been able to create our own units, time constraints and lack of ability hampered our creative endeavors.  In other words, most of the materials we do have aren’t up to the high standards of most current games.  Our sound was provided courtesy of the fmod sound library, and the 3DS loader was modified from an example given on Digiben’s website.  As our code runs on a cluster computer, we used OpenGL GLUT the SDL and FMOD as our exterior libraries, as well including files from several game programming tutorial sites.

 

 

3. Technical Challenges

Perhaps the most difficult part of our assignment was the large amount of half written and partially implemented code we generated.  Although we borrowed a 3DS Loader, this had to be rewritten to align to our personal data structures and how we accessed our data.  We have two different methods of generating a map, and the implementation we finally decided upon was the third generation of map symbols and how we were to implement walls in our game.

Reusing code from other sources required us to understand and integrate large amounts of modular code.  Everything from the sound libraries to the 3DS loader could easily have been a project on their own.  Thankfully, we were able to bring everything together in time to get our project finished on time.

Two of the most difficult sections of our game weren’t supposed to be challenging at all:  Artificial intelligence and shooting opponents.

Intelligent AI  Remains a fun exercise in tweaking constants to get right.  Although the current implementation depends heavily on random chance, a much larger algorithm was designed and implemented… however the tweaking didn’t go quite as well.  The random AI seemed to beat the actually programmed one with such ease and we removed our AI from the final version of the game.  We would have liked to have more time to work on these constants, but problems with model loading and terrain mapping interfered with anything more than the bare bones implementation of computer sentience.

Shooting/Visibility was to be a core component of our game.  However, this section of our code took much longer than we had scheduled for it.  Instead of being completed before the Thanksgiving break, we were forced to continue working on errors well into December.  There were problems with how we allowed weapons fired, and what constituted “valid” targets.  Although we intended multiple weapons, I’m afraid we ended this project with a single generic ‘weapon.’

  4.

Eli:

            Three Things that Went Right:

·        Got an early start

·        Met our design spec with a few extras on time

·        Enjoyed the project

 

Three Things that Went Wrong:

·        The Workload was far from even some days we were putting in 16 hours or more others days went by without anyone touching the code. Some times one person put in significantly more time than other. Very separate and isolated work environment.

·        Quite a few hang ups where a person felt there hands were tied

·        A lot of late nights and a few frustrating moments

 

Wish I could have hired an artist since neither me nor my partner were one. Perhaps rethought the notion of a 5 minute presentation to make a more immediate response sort of game. Turn based strategy actually requires a great deal of planning and over head despite the time based movement

Paul:

            Three Things that Went Right:

·        Have a fully working game

·        Learned a lot about the different sections of a game, the modular design of our code made is easy to add and remove sections at a time.

·        We’re NOT using teapots!

Three Things that Went Wrong:

·        We had the tendency to not work together on code.  I tended to code in the graphics cluster, while Eli worked from home.  This led to a large lack of communication, and some confusion among code.

·        Not following our time charts.  There were a lot of things I’d have liked to have working fully.  Up until recently, lots of our code was in partially functional, or almost working modules that weren’t implemented fully.

·        Lack of ‘prettyness.’  This game doesn’t quite have the polish on it that I would have liked.  In addition, we cut a few features that I had thought would have been needed to instead work on not quite essential features.

The largest problem was the lack of communication and focus.  I know I spent quite a few nights as the only one in the graphics cluster, and I know that Eli spent awhile at home coding as well.  But there were too many times that I solved a days long problem in Eli’s code or he fixed an error that was bugging me, that should have been fixed a lot earlier.  Wasted time was quite a large problem.  The planning of our code, while nice, did have to be taken apart and revamped from time to time when new features were added.  AI required a rewrite of all the input functions and the addition of a limited finite state machine to handle commands.  Original plans weren’t implemented and instead were simply added into existing structures.   This project was entertaining, but I would have liked a project that stuck closer to the design specifications and time line than this one did.