/*
Adam Obenauf, ml22
4/17/11
project2.cpp
Text based rpg game based on the combat system of eve online
*/
//Preprocessor directives
#include<iostream>
#include<iomanip>
#include<cmath>
#include<string>
#include<fstream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "prompts.h"
#include "attributecalc.h"
#include "display.h"
using namespace std;
//Structures
struct ship
{
int size;
int velocity;
int damageBonusShield;
int damageBonusArmor;
int damage;
int shieldHitPoints;
int armorHitPoints;
int hullHitPoints;
int accuracyFalloff;
int optimalRange;
float trackingSpeed;
};
//Function prototypes
//CalculateShipAttributes
//Return type: ship
//Parameters: int specialization, int size, int primaryWeaponChoice, int ammoChoice
//This function calculates the attributes of a ship based on specialization/size/weapon choice/ammo choice and returns that ship
ship CalculateShipAttributes (int specialization, int size, int primaryWeaponChoice, int ammoChoice);
//WriteShipFile
//Return type: void
//Parameters: ship myShip, string shipName
//This function is passed a ship and the name of the file and writes the data to that file
void WriteShipFile (ship myShip, string shipName);
//LoadShip
//Return type: ship
//Parameters: string shipName
//This function is passed the name of a file, it reads the data from that file and returns it as a ship
ship LoadShip(string shipName);
//DoesFileExist
//Return type: bool
//Parameters: string shipName
//This function is passed the name of a file and checks if the file exists, it returns true/false based on whether it exists or not
bool DoesFileExist(string shipName);
//RandomNum
//Return type: int
//Parameters: int lo, int hi
//This function is passed two numbers and returns a random number between them (inclusive)
int RandomNum(int lo, int hi);
//DisplayShipAttributes
//Return type: void
//Parameters: ship playerShip
//This function is passed a ship and displays the attributes of that ship
void DisplayShipAttributes(ship playerShip);
//IsHit
//Return type: bool
//Parameters: float accuracyFalloff, float optimalRange, float currentRange, float velocity, float trackingSpeed
//This function is passed a bunch of attributes and returns whether or not the person attacking hit the target
bool IsHit(float accuracyFalloff, float optimalRange, float currentRange, float velocity, float trackingSpeed);
//UpdateShipWeapon
//Return type: void
//Parameters: int size, int weaponChoice, int ammoChoice, ship myShip passed by reference
//This function updates some of a ships's attributes when a weapon change occurs
void UpdateShipWeapon(int size, int weaponChoice, int ammoChoice, ship& myShip);
//PercHealth
//Return type: float
//Parameters: int current, string shipName, int healthType
//This function calculates the percentage of health remaining by using current health, originalHealth (reads this from a file), and health type
float PercHealth (int current, string shipName, int healthType);
//PlayerAttacksEnemy
//Return type: void
//Parameters: ship playerShip passed by reference, ship enemyShip passed by reference, int currentRange
//This function updates the enemyShips shield/armor/hull hitpoints based on damage inflicted by the player from a hit (if the hit is successful), it displays a message about whether you hit the enemy or not and how much damage you inflicted to shields/armor/hull
void PlayerAttacksEnemy(ship playerShip, ship& enemyShip, int currentRange);
//EnemyAttacksPlayer
//Return type: void
//Parameters: ship playerShip passed by reference, ship enemyShip passed by reference, int currentRange
//This function updates the playerShips shield/armor/hull hitpoints based on damage inflicted by the enemy from a hit (if the hit is successful), it displays a message about whether the enemy hit you or not and how much damage it inflicted to your shields/armor/hull
void EnemyAttacksPlayer(ship& playerShip, ship enemyShip, int currentRange);
//Main function
int main()
{
srand((unsigned)time(0));
//Declare variables
int menuChoice = 0;
ship playerShip;
ship enemyShip;
string shipName = "";
bool fileExists;
int shipMenu = 0;
int specialization = 0;
int size = 0;
int weaponChoice = 0;
int ammoChoice = 0;
int currentRange = 0;
float percHealth = 0;
int combatChoice = 0;
cout<<endl<<"Welcome to blah blah blah";
do
{
cout<<endl;
menuChoice = MainMenu();
//Instructions
if(menuChoice==1)
{
cout<<endl<<"Instructions go here";
}
//Actual game
else if(menuChoice==2)
{
shipName="PlayerShip.txt";
fileExists=DoesFileExist(shipName);
if(fileExists==true)
{
playerShip=LoadShip(shipName);
//actual game begins here
//Generate enemy vessel
//Make it the same size as the player vessel, everything else is random
shipName="EnemyShip.txt";
size=RandomNum(1,3);
specialization=RandomNum(1,3);
weaponChoice=RandomNum(1,4);
ammoChoice=RandomNum(1,4);
enemyShip=CalculateShipAttributes (specialization, size, weaponChoice, ammoChoice);
WriteShipFile (enemyShip, shipName);
cout<<endl<<"The enemy has the following attributes:"<<endl;
DisplayShipAttributes (enemyShip);
//Nested for loops go here
//Combat do while loop
do
{
//If the player is faster than the enemy they set the range
if(playerShip.velocity>=enemyShip.velocity)
{
currentRange=playerShip.optimalRange;
}
else
{
currentRange=enemyShip.optimalRange;
}
//Display current range and everyone's health
cout<<endl<<endl<<"Current Range: "<<currentRange<<"km";
cout<<fixed<<setprecision(0);
cout<<endl<<"Your shields: ";
percHealth = PercHealth (playerShip.shieldHitPoints, "PlayerShip.txt", 1);
cout<<percHealth<<"%";
/*
DisplayHealthBar(percHealth);
*/
cout<<endl<<"Your armor: ";
percHealth = PercHealth (playerShip.armorHitPoints, "PlayerShip.txt", 2);
cout<<percHealth<<"%";
cout<<endl<<"Your hull: ";
percHealth = PercHealth (playerShip.hullHitPoints, "PlayerShip.txt", 3);
cout<<percHealth<<"%";
cout<<endl;
cout<<endl<<"Enemy shields: ";
percHealth = PercHealth (enemyShip.shieldHitPoints, "EnemyShip.txt", 1);
cout<<percHealth<<"%";
cout<<endl<<"Enemy armor: ";
percHealth = PercHealth (enemyShip.armorHitPoints, "EnemyShip.txt", 2);
cout<<percHealth<<"%";
cout<<endl<<"Enemy hull: ";
percHealth = PercHealth (enemyShip.hullHitPoints, "EnemyShip.txt", 3);
cout<<percHealth<<"%";
combatChoice = PromptCombatChoice();
//Player attacks enemy
if(combatChoice==1)
{
PlayerAttacksEnemy(playerShip, enemyShip, currentRange);
}
else if(combatChoice==2)
{
//Player switches weapon/ammo
weaponChoice = PromptWeaponChoice();
ammoChoice = PromptAmmoChoice();
UpdateShipWeapon(playerShip.size, weaponChoice, ammoChoice, playerShip);
}
else if(combatChoice==3)
{
//Player attempts escape, only succesfull if they are faster than the enemy
if(playerShip.velocity>enemyShip.velocity)
{
cout<<endl<<endl<<"You escaped!";
}
else
{
cout<<endl<<endl<<"You are not fast enough to escape.";
}
}
EnemyAttacksPlayer(playerShip, enemyShip, currentRange);
//Set anything that is negative to 0
if(playerShip.shieldHitPoints<0)
{
playerShip.shieldHitPoints=0;
}
if(playerShip.armorHitPoints<0)
{
playerShip.armorHitPoints=0;
}
if(playerShip.hullHitPoints<0)
{
playerShip.hullHitPoints=0;
}
if(enemyShip.shieldHitPoints<0)
{
enemyShip.shieldHitPoints=0;
}
if(enemyShip.armorHitPoints<0)
{
enemyShip.armorHitPoints=0;
}
if(enemyShip.hullHitPoints<0)
{
enemyShip.hullHitPoints=0;
}
}
while(enemyShip.hullHitPoints>0 & playerShip.hullHitPoints>0);
//Either the enemy or player has died
if(enemyShip.hullHitPoints=0)
{
cout<<endl<<endl<<"Congradulations! You have defeated the enemy!";
}
else if(playerShip.hullHitPoints=0)
{
cout<<endl<<endl<<"Your enemy has destroyed you. Please try again.";
}
}
else
{
cout<<endl<<"Error. File does not exist. Please create a ship first.";
}
}
//Create
else if(menuChoice==3)
{
shipName="PlayerShip.txt";
specialization = PromptSpecialization();
size = PromptSize();
weaponChoice = PromptWeaponChoice();
ammoChoice = PromptAmmoChoice();
playerShip=CalculateShipAttributes (specialization, size, weaponChoice, ammoChoice);
playerShip.size=size;
WriteShipFile (playerShip, shipName);
//Go ahead and automatically load the ship they just created in case they forget
cout<<endl<<"Your ship has the following attributes:"<<endl;
DisplayShipAttributes (playerShip);
}
//Display
else if(menuChoice==4)
{
shipName="PlayerShip.txt";
fileExists=DoesFileExist(shipName);
if(fileExists==true)
{
playerShip=LoadShip(shipName);
cout<<endl<<"Your ship has the following attributes:"<<endl;
DisplayShipAttributes (playerShip);
}
else
{
cout<<endl<<"Error. File does not exist. Please create a ship first.";
}
}
//View high scores
else if(menuChoice==5)
{
cout<<"Unavailible at the moment."<<endl;
}
//Quit
else if(menuChoice==6)
{
cout<<endl<<"Have a nice day!";
}
}
while(menuChoice!=6);
cout<<endl;
return 0;
}
ship CalculateShipAttributes (int specialization, int size, int primaryWeaponChoice, int ammoChoice)
{
ship myShip;
//Calculate the attributes
myShip.velocity = CalcVelocity(size);
myShip.damage = CalcDamage (size, primaryWeaponChoice);
myShip.shieldHitPoints = CalcShields (size);
myShip.armorHitPoints = CalcArmor (size);
myShip.hullHitPoints = CalcHull (size);
myShip.accuracyFalloff = CalcAccuracyFalloff (size, primaryWeaponChoice);
myShip.optimalRange = CalcOptimalRange (size, primaryWeaponChoice);
myShip.trackingSpeed = CalcTrackingSpeed (size, primaryWeaponChoice);
CalcDamageBonuses (myShip.damageBonusShield, myShip.damageBonusArmor, ammoChoice, myShip.damage);
//Specializations
if(specialization==1)
{
myShip.velocity = myShip.velocity * 2;
}
else if(specialization==2)
{
myShip.shieldHitPoints=myShip.shieldHitPoints * 2;
}
else if(specialization==3)
{
myShip.armorHitPoints=myShip.armorHitPoints * 2;
}
return myShip;
}
void UpdateShipWeapon(int size, int weaponChoice, int ammoChoice, ship& myShip)
{
myShip.damage = CalcDamage (size, weaponChoice);
myShip.accuracyFalloff = CalcAccuracyFalloff (size, weaponChoice);
myShip.optimalRange = CalcOptimalRange (size, weaponChoice);
myShip.trackingSpeed = CalcTrackingSpeed (size, weaponChoice);
CalcDamageBonuses (myShip.damageBonusShield, myShip.damageBonusArmor, ammoChoice, myShip.damage);
}
void WriteShipFile (ship myShip, string shipName)
{
//Write it to a file
ofstream outputFile;
outputFile.open(shipName.c_str());
outputFile<<myShip.size<<endl;
outputFile<<myShip.velocity<<endl;
outputFile<<myShip.damageBonusShield<<endl;
outputFile<<myShip.damageBonusArmor<<endl;
outputFile<<myShip.damage<<endl;
outputFile<<myShip.shieldHitPoints<<endl;
outputFile<<myShip.armorHitPoints<<endl;
outputFile<<myShip.hullHitPoints<<endl;
outputFile<<myShip.accuracyFalloff<<endl;
outputFile<<myShip.optimalRange<<endl;
outputFile<<myShip.trackingSpeed<<endl;
outputFile.close();
}
bool IsHit(float accuracyFalloff, float optimalRange, float currentRange, float velocity, float trackingSpeed)
{
float hitChance = 0.0;
int randomNum = 0;
//in radians/s
float angularVelocity = velocity/currentRange;
//angular speed of the enemy is greater than tracking speed of the turret
if(angularVelocity>trackingSpeed)
{
return false;
}
//Extremely close range
else if (currentRange < accuracyFalloff)
{
return true;
}
else if (currentRange >= accuracyFalloff)
{
//calculate hitChance
hitChance = 1.f - abs(optimalRange - currentRange) / accuracyFalloff * 0.5f;
//If we have 1/6 chance for example that's 0.15. 0.15 times 1000 is 150, we pick a random number between 0 and 1000 and we'll have a 1/6th chance of it being less than or equal to 150
randomNum=RandomNum(0,1000);
if (hitChance*1000 <= randomNum)
{
return true;
}
else if(hitChance*1000 > randomNum)
{
return false;
}
}
}
int RandomNum(int lo, int hi)
{
// note: floor() really only needed if hi < 0
int range = (hi - lo+1);
return rand()%range+lo;
}
ship LoadShip(string shipName)
{
ship Ship1;
ifstream inputFile;
inputFile.open(shipName.c_str());
inputFile>>Ship1.size;
inputFile>>Ship1.velocity;
inputFile>>Ship1.damageBonusShield;
inputFile>>Ship1.damageBonusArmor;
inputFile>>Ship1.damage;
inputFile>>Ship1.shieldHitPoints;
inputFile>>Ship1.armorHitPoints;
inputFile>>Ship1.hullHitPoints;
inputFile>>Ship1.accuracyFalloff;
inputFile>>Ship1.optimalRange;
inputFile>>Ship1.trackingSpeed;
inputFile.close();
return Ship1;
}
bool DoesFileExist(string shipName)
{
ifstream inputFile;
inputFile.open(shipName.c_str());
if(inputFile.fail())
{
return false;
}
else
{
return true;
}
}
void DisplayShipAttributes (ship playerShip)
{
//Display everything
cout<<"Velocity: \t\t\t"<<playerShip.velocity<<" meters per second"<<endl;
cout<<"Base damage: \t\t\t"<<playerShip.damage<<" hitpoints"<<endl;
cout<<"Bonus damage against shields: \t"<<playerShip.damageBonusShield<<" hitpoints"<<endl;
cout<<"Bonus damage against armor: \t"<<playerShip.damageBonusArmor<<" hitpoints"<<endl;
cout<<"Shields: \t\t\t"<<playerShip.shieldHitPoints<<" hitpoints"<<endl;
cout<<"Armor: \t\t\t"<<playerShip.armorHitPoints<<" hitpoints"<<endl;
cout<<"Hull: \t\t\t\t"<<playerShip.hullHitPoints<<" hitpoints"<<endl;
cout<<"Accuracy falloff: \t\t"<<playerShip.accuracyFalloff<<" meters"<<endl;
cout<<"Optimal range: \t\t"<<playerShip.optimalRange<<" meters"<<endl;
cout<<"Tracking speed: \t\t"<<playerShip.trackingSpeed<<" radians per second"<<endl;
}
//Make a function to get the percentage remaining health, but use 50 instead of 100
float PercHealth (int current, string shipName, int healthType)
{
ship myShip=LoadShip(shipName);
if(healthType==1)
{
return (current/myShip.shieldHitPoints) * 100;
}
else if(healthType==2)
{
return (current/myShip.armorHitPoints) * 100;
}
else if(healthType==3)
{
return (current/myShip.hullHitPoints) * 100;
}
}
void PlayerAttacksEnemy(ship playerShip, ship& enemyShip, int currentRange)
{
if (IsHit(playerShip.accuracyFalloff, playerShip.optimalRange, currentRange, enemyShip.velocity, playerShip.trackingSpeed)==true)
{
//Player deals damage
cout<<endl<<endl<<"You hit the enemy!";
if(enemyShip.shieldHitPoints>0)
{
enemyShip.shieldHitPoints=enemyShip.shieldHitPoints-(playerShip.damage + playerShip.damageBonusShield);
cout<<endl<<"You dealt "<<playerShip.damage + playerShip.damageBonusShield<<" damage to the enemy's shields.";
}
else if(enemyShip.armorHitPoints>0)
{
enemyShip.armorHitPoints=enemyShip.armorHitPoints-(playerShip.damage + playerShip.damageBonusArmor);
cout<<endl<<"You dealt "<<playerShip.damage + playerShip.damageBonusArmor<<" damage to the enemy's armor.";
}
else if (enemyShip.armorHitPoints<0)
{
enemyShip.hullHitPoints=enemyShip.hullHitPoints-playerShip.damage;
}
cout<<endl<<"You dealt "<<playerShip.damage<<" damage to the enemy's hull";
}
else
{
//Player misses the enemy
cout<<endl<<endl<<"You missed!";
}
}
void EnemyAttacksPlayer(ship& playerShip, ship enemyShip, int currentRange)
{
//Enemy attacks player
if (IsHit(enemyShip.accuracyFalloff, enemyShip.optimalRange, currentRange, playerShip.velocity, enemyShip.trackingSpeed)==true)
{
//enemy hits
cout<<endl<<endl<<"The enemy hit you!";
if(playerShip.shieldHitPoints>0)
{
playerShip.shieldHitPoints=playerShip.shieldHitPoints-(enemyShip.damage + enemyShip.damageBonusShield);
cout<<endl<<"The enemy dealt "<<enemyShip.damage + enemyShip.damageBonusShield<<" damage to your shields.";
}
else if(playerShip.armorHitPoints>0)
{
playerShip.armorHitPoints=playerShip.armorHitPoints-(enemyShip.damage + enemyShip.damageBonusArmor);
cout<<endl<<"The enemy dealt "<<enemyShip.damage + enemyShip.damageBonusArmor<<" damage to your armor.";
}
else if(playerShip.armorHitPoints<0)
{
enemyShip.hullHitPoints=enemyShip.hullHitPoints-playerShip.damage;
cout<<endl<<"The enemy dealt "<<enemyShip.damage<<" damage to your hull.";
}
}
else
{
//Enemy misses
cout<<endl<<endl<<"The enemy missed you!";
}
}