/*
Free Battleship clone for Win32/Linux Console.
Copyright (C) 2005 Dennis Gertitschke
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 2
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "../inc/User.h"
#include "../inc/Ship.h"
#include "../inc/Screen.h"
#include "../inc/Error.h"
#include "../inc/Common.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef WIN32
#include <windows.h>
#endif
User::User(){
for(int i=0;i<100;i++){
m_barrValidEnd[i]=false;
}
m_cStartX=m_cEndX=m_cDirection=0;
m_iStartY=m_iEndY=0;
m_pScreen->setMode(GAMETYPE_PLAYER_VS_COMPUTER);
}
User::~User(){
}
void User::registerCallback(void *pObj,F pFunc){
m_pObj=pObj;
m_pFunc=pFunc;
}
void User::reset(){
m_bDefeat=false;
m_iShipCount=4;
for(int i=0;i<100;i++){
m_arrBoard[i]=NULL;
m_barrValidStart[i]=true;
m_barrValidEnd[i]=false;
m_barrValidShot[i]=true;
}
m_cStartX=m_cEndX=m_cDirection=0;
m_iStartY=m_iEndY=m_iIndex=0;
m_pFrigate->reset();
m_pDestroyer->reset();
m_pCruiser->reset();
m_pBattleship->reset();
}
void User::shoot(){
char cShotX;
int iShotY;
do{
m_pScreen->getUserCoordinate(cShotX,iShotY);
if(!validateCoordinate(cShotX,iShotY)){
Error myerror("Invalid coordinate");
m_pScreen->printErrorMsg(myerror);
}
else{
m_iIndex=transformToBoardIndex(cShotX,iShotY);
if(!m_barrValidShot[m_iIndex]){
Error myerror("You already shot that field");
m_pScreen->printErrorMsg(myerror);
}
}
}while(!validateCoordinate(cShotX,iShotY)||!m_barrValidShot[m_iIndex]);
m_barrValidShot[m_iIndex]=false;
// call back the computer
m_pFunc(m_pObj,cShotX,iShotY);
}
void User::validateCompShotCallback(void *pObj,char cX,int iY,bool& bRefHit,int& iRefHitCount){
User *self=static_cast<User*>(pObj);
self->validateCompShot(cX,iY,bRefHit,iRefHitCount);
}
void User::validateCompShot(char cX,int iY,bool& bRefHit,int& iRefHitCount){
int iShot=transformToBoardIndex(cX,iY);
char cTmpX=cX;
int iTmpY=iY;
if(m_arrBoard[iShot]!=NULL){
bRefHit=true;
iRefHitCount++;
m_arrBoard[iShot]->hit();
if(m_arrBoard[iShot]->getStatus()){
m_iShipCount--;
iRefHitCount=0;
if(!m_iShipCount)
m_bDefeat=true;
}
m_pScreen->printUserStatus(bRefHit,m_arrBoard[iShot]->getId(),m_arrBoard[iShot]->getStatus());
transformToUserScreen(cTmpX,iTmpY);
m_pScreen->displayHit(cTmpX,iTmpY);
}
else{
bRefHit=false;
m_pScreen->printUserStatus(bRefHit,NULL,false);
transformToUserScreen(cTmpX,iTmpY);
m_pScreen->displayMiss(cTmpX,iTmpY);
}
}
bool User::validateDirection(char cDirection){
if(cDirection!='H'&&cDirection!='V')
return false;
else
return true;
}
void User::transformToUserScreen(char& cRefX,int& iRefY){
cRefX=static_cast<char>((cRefX-65)+2*(cRefX-40));
iRefY=iRefY+4;
}
void User::placeShip(Ship *pShip){
int i=0;
char cTmpX;
int iTmpY;
while(i<pShip->getLength()){
switch(m_cDirection){
case 'H': if(m_cStartX<m_cEndX){
cTmpX=m_cStartX;
iTmpY=m_iStartY;
markNeighboursInvalid(m_cStartX,m_iStartY);
m_iIndex=transformToBoardIndex(m_cStartX,m_iStartY);
m_arrBoard[m_iIndex]=pShip;
m_barrValidStart[m_iIndex]=false;
m_cStartX++;
transformToUserScreen(cTmpX,iTmpY);
m_pScreen->displayShip(cTmpX,iTmpY);
}
else{
cTmpX=m_cStartX;
iTmpY=m_iStartY;
markNeighboursInvalid(m_cStartX,m_iStartY);
m_iIndex=transformToBoardIndex(m_cStartX,m_iStartY);
m_arrBoard[m_iIndex]=pShip;
m_barrValidStart[m_iIndex]=false;
m_cStartX--;
transformToUserScreen(cTmpX,iTmpY);
m_pScreen->displayShip(cTmpX,iTmpY);
}
break;
case 'V': if(m_iStartY<m_iEndY){
cTmpX=m_cStartX;
iTmpY=m_iStartY;
markNeighboursInvalid(m_cStartX,m_iStartY);
m_iIndex=transformToBoardIndex(m_cStartX,m_iStartY);
m_arrBoard[m_iIndex]=pShip;
m_barrValidStart[m_iIndex]=false;
m_iStartY++;
transformToUserScreen(cTmpX,iTmpY);
m_pScreen->displayShip(cTmpX,iTmpY);
}
else{
cTmpX=m_cStartX;
iTmpY=m_iStartY;
markNeighboursInvalid(m_cStartX,m_iStartY);
m_iIndex=transformToBoardIndex(m_cStartX,m_iStartY);
m_arrBoard[m_iIndex]=pShip;
m_barrValidStart[m_iIndex]=false;
m_iStartY--;
transformToUserScreen(cTmpX,iTmpY);
m_pScreen->displayShip(cTmpX,iTmpY);
}
break;
}
i++;
}
}
void User::resetEndpoints(){
for(int i=0;i<100;i++)
m_barrValidEnd[i]=false;
}
bool User::readValidStartEnd(Ship *pShip){
int iOffset;
iOffset=pShip->getLength()-1;
m_pScreen->promptUserToHide(pShip->getId(),pShip->getLength());
do{
m_pScreen->getDirection(m_cDirection);
if(!validateDirection(m_cDirection)){
Error myerror("Please enter 'h' or 'v'");
m_pScreen->printErrorMsg(myerror);
}
}while(!validateDirection(m_cDirection));
m_pScreen->clearErrorMsg();
do{
m_pScreen->getUserCoordinate(m_cStartX,m_iStartY);
if(!validateCoordinate(m_cStartX,m_iStartY)){
Error myerror("Invalid coordinate");
m_pScreen->printErrorMsg(myerror);
}
else{
m_iIndex=transformToBoardIndex(m_cStartX,m_iStartY);
if(!m_barrValidStart[m_iIndex]){
Error myerror("Coordinate already in use");
m_pScreen->printErrorMsg(myerror);
}
}
}while(!validateCoordinate(m_cStartX,m_iStartY)||!m_barrValidStart[m_iIndex]);
m_pScreen->clearErrorMsg();
switch(m_cDirection){
case 'H': if(validateCoordinate(static_cast<char>(m_cStartX+iOffset),m_iStartY)){
if(checkStartEnd(m_cStartX,m_iStartY,pShip->getLength(),1)){
m_iIndex=transformToBoardIndex(static_cast<char>(m_cStartX+iOffset),m_iStartY);
m_barrValidEnd[m_iIndex]=true;
}
}
if(validateCoordinate(static_cast<char>(m_cStartX-iOffset),m_iStartY)){
if(checkStartEnd(m_cStartX,m_iStartY,pShip->getLength(),2)){
m_iIndex=transformToBoardIndex(static_cast<char>(m_cStartX-iOffset),m_iStartY);
m_barrValidEnd[m_iIndex]=true;
}
}
break;
case 'V': if(validateCoordinate(m_cStartX,m_iStartY+iOffset)){
if(checkStartEnd(m_cStartX,m_iStartY,pShip->getLength(),3)){
m_iIndex=transformToBoardIndex(m_cStartX,m_iStartY+iOffset);
m_barrValidEnd[m_iIndex]=true;
}
}
if(validateCoordinate(m_cStartX,m_iStartY-iOffset)){
if(checkStartEnd(m_cStartX,m_iStartY,pShip->getLength(),4)){
m_iIndex=transformToBoardIndex(m_cStartX,m_iStartY-iOffset);
m_barrValidEnd[m_iIndex]=true;
}
}
break;
}
do{
m_pScreen->getUserCoordinate(m_cEndX,m_iEndY);
if(!validateCoordinate(m_cEndX,m_iEndY)){
Error myerror("Invalid coordinate");
m_pScreen->printErrorMsg(myerror);
}
else{
m_iIndex=transformToBoardIndex(m_cEndX,m_iEndY);
if(!m_barrValidEnd[m_iIndex]){
Error myerror("The endpoint entered is invalid");
m_pScreen->printErrorMsg(myerror);
resetEndpoints();
return false;
}
}
}while(!validateCoordinate(m_cEndX,m_iEndY)||!m_barrValidEnd[m_iIndex]);
m_pScreen->clearErrorMsg();
resetEndpoints();
return true;
}
void User::deployFleet(){
#ifdef WIN32
PlaySound("..\\sound\\alarm.wav",NULL,SND_SYNC);
#else
::playWave("../sound/alarm.wav",8,1,8000);
#endif
while(!readValidStartEnd(m_pFrigate));
placeShip(m_pFrigate);
while(!readValidStartEnd(m_pDestroyer));
placeShip(m_pDestroyer);
while(!readValidStartEnd(m_pCruiser));
placeShip(m_pCruiser);
while(!readValidStartEnd(m_pBattleship));
placeShip(m_pBattleship);
m_pScreen->printUserSucessfullyPlaced();
}
bool User::checkStartEnd(char cStartX,int iStartY,int iLength,int iDirection){
bool bResult=true;
int i=0;
while(i<iLength){
switch(iDirection){
case 1: m_iIndex=transformToBoardIndex(cStartX,iStartY);
if(!m_barrValidStart[m_iIndex])
return false;
cStartX++;
break;
case 2: m_iIndex=transformToBoardIndex(cStartX,iStartY);
if(!m_barrValidStart[m_iIndex])
return false;
cStartX--;
break;
case 3: m_iIndex=transformToBoardIndex(cStartX,iStartY);
if(!m_barrValidStart[m_iIndex])
return false;
iStartY++;
break;
case 4: m_iIndex=transformToBoardIndex(cStartX,iStartY);
if(!m_barrValidStart[m_iIndex])
return false;
iStartY--;
break;
}
i++;
}
return bResult;
}