Commit 937937b9 authored by Andreas Zehender's avatar Andreas Zehender
Browse files

Imported kspaceduel sources

svn path=/trunk/kdegames/kspaceduel/; revision=20527
parents
Author of KSpaceduel:
Andreas Zehender <azehende@ba-stuttgart.de>
version 1.0
* [Andreas Zehender] Portet to Qt-2.0
* [Andreas Zehender] Changed option dialogs
version 0.2.2
* [Andreas Zehender] fixed error in ExplosionSprite::setSequence
version 0.2.1
* [Andreas Zehender] Implemented movements, bullets, mines
* [Andreas Zehender] First released version
# this 10 paths are KDE specific. Use them:
# kde_htmldir Where your docs should go to. (contains lang subdirs)
# kde_appsdir Where your application file (.kdelnk) should go to.
# kde_icondir Where your icon should go to.
# kde_sounddir Where system sounds should go to.
# kde_datadir Where you install application data. (Use a subdir)
# kde_locale Where translation files should go to.(contains lang subdirs)
# kde_cgidir Where cgi-bin executables should go to.
# kde_confdir Where config files should go to.
# kde_mimedir Where mimetypes should go to.
# kde_toolbardir Where general toolbar icons should go to.
# kde_wallpaperdir Where general wallpapers should go to.
# just set the variable
APPSDIR = $(kde_appsdir)/Games
# set the include path for X, qt and KDE
INCLUDES= $(all_includes)
# claim, which subdirectories you want to install
# if you don't have subdirectories, comment it
SUBDIRS = sprites html
# you can add here more. This one gets installed
bin_PROGRAMS = kspaceduel
# Which sources should be compiled for kspaceduel.
kspaceduel_SOURCES = mathroutines.cpp topwidget.cpp duelwidget.cpp playerinfo.cpp \
sprites.cpp mainview.cpp dialogs.cpp ai.cpp main.cpp
kspaceduel_METASOURCES = USE_AUTOMOC
EXTRA_DIST = kspaceduel.kdelnk kspaceduel.xpm mini-kspaceduel.xpm
# the library search path.
kspaceduel_LDFLAGS = $(all_libraries) $(KDE_RPATH)
# the libraries to link against. Be aware of the order. First the libraries,
# that depend on the following ones.
kspaceduel_LDADD = -lkdeui -lkdecore -lQwSpriteField -lqt -lXext -lX11 $(LIBSOCKET)
# this option you can leave out. Just, if you use "make dist", you need it
noinst_HEADERS = mathroutines.h topwidget.h duelwidget.h playerinfo.h \
sprites.h dialogs.h mainview.h defines.h ai.h structs.h version.h
# if you "make distclean", this files get removed. If you want to remove
# them while "make clean", use CLEANFILES
DISTCLEANFILES = $(kspaceduel_METASOURCES)
# make messages.po. Move this one to ../po/ and "make merge" in po
# the -x is for skipping messages already translated in kdelibs
messages:
$(XGETTEXT) -C -ki18n -x $(includedir)/kde.pot $(kspaceduel_SOURCES) && mv messages.po ../po/kspaceduel.pot
# just install datas here. Use install-exec-local for scripts and etc.
# the binary itself is already installed from automake
# use mkinstalldirs, not "install -d"
# don't install a list of file. Just one file per install.
# if you have more of them, create a subdirectory with an extra Makefile
install-data-local:
$(mkinstalldirs) $(APPSDIR)
$(INSTALL_DATA) kspaceduel.kdelnk $(APPSDIR)
$(mkinstalldirs) $(kde_icondir)
$(INSTALL_DATA) kspaceduel.xpm $(kde_icondir)
$(mkinstalldirs) $(kde_minidir)
$(INSTALL_DATA) mini-kspaceduel.xpm $(kde_minidir)/kspaceduel.xpm
# remove ALL you have installed in install-data-local or install-exec-local
uninstall-local:
-rm -f $(APPSDIR)/kspaceduel.kdelnk
-rm -f $(kde_icondir)/kspaceduel.xpm
-rm -f $(kde_minidir)/kspaceduel.xpm
This diff is collapsed.
KSpaceduel
----------
KSpaceduel is an arcade two-player space game for KDE.
Two ships fly around the sun and have to shoot the other ship.
Look at the html manual for further information.
Andreas Zehender <azehende@ba-stuttgart.de>
31 Oct 1998
todo:
-----
sound
bonus items (more mines, more bullets, shield, energy)
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include "ai.h"
#include "sprites.h"
#include "structs.h"
#include "defines.h"
#include "mathroutines.h"
int Ai::calcFrameIncrement[DNUM]={10,5,3,2};
int Ai::calcPositionNumber[DNUM]={15,30,40,80};
int Ai::calcShotDirections[DNUM]={4,7,10,12};
int Ai::calcCollisions[DNUM]={30,15,10,10};
int Ai::calcNextShot[DNUM]={300,200,90,60};
double Ai::calcShotRandom[DNUM]={M_PI/4,M_PI/18,M_PI/36,0.0};
Ai::Ai(int pn,ShipSprite* s[2],QList<BulletSprite>* b[2],
QList<MineSprite>* m[2],SConfig *c,SOptions *o)
{
int i;
srand(time(NULL));
playerNumber=pn;
opponentNumber=(pn+1)%2;
cfg=c;
opt=o;
for(i=0;i<2;i++)
{
ship[i]=s[i];
bullets[i]=b[i];
mines[i]=m[i];
shipsNextPositions[i]=new QArray<AiSprite>
((int)(calcPositionNumber[opt->aiDifficulty[playerNumber]]/cfg->gamespeed));
aiMines[i]=new QArray<AiSprite>(cfg->maxMines);
mineNumber[i]=0;
}
myShots.setAutoDelete(true);
objectsHitByShip.setAutoDelete(true);
minesHitByShot.setAutoDelete(true);
}
void Ai::newRound()
{
accelerateFramesNumber=0;
rotateFramesNumber=0;
shoot=false;
score=1e10;
rotation=RNONE;
acc=false;
bullet=false;
mine=false;
borderTime=-1;
sunTime=-1;
calculateCollisions=(int)(calcCollisions[opt->aiDifficulty[playerNumber]]
/cfg->gamespeed);
waitShot=(int)((rand() % calcNextShot[opt->aiDifficulty[playerNumber]])
/cfg->gamespeed);
myShots.clear();
objectsHitByShip.clear();
minesHitByShot.clear();
}
void Ai::think()
{
setSpriteFieldSize();
myShots.clear();
borderTime=-1;
sunTime=-1;
score--;
if(waitShot>0)
waitShot--;
calculateNextPositions();
if(opt->aiDifficulty[playerNumber]!=DTRAINEE)
testForHits();
tryShots();
shotScores();
chooseAction();
if(rotateFramesNumber<=0)
{
rotation=RNONE;
if(accelerateFramesNumber<=0)
{
acc=false;
if(shoot)
{
bullet=true;
shoot=false;
}
else
bullet=false;
score=1e10;
}
else
{
acc=true;
accelerateFramesNumber--;
}
}
else
rotateFramesNumber--;
}
AiSprite Ai::nextPosition(AiSprite sp,double mult)
{
double abs_2,nx,ny,sq,eg;
if(!sp.sun)
{
abs_2=sp.x*sp.x+sp.y*sp.y;
if(abs_2<1)
abs_2=1;
sq=sqrt(abs_2);
nx=sp.x/sq;
ny=sp.y/sq;
eg=cfg->gravity*mult;
sp.dx-=eg*nx/abs_2;
sp.dy-=eg*ny/abs_2;
sp.x+=sp.dx*mult;
sp.y+=sp.dy*mult;
if(sp.x*sp.x+sp.y*sp.y<1600)
sp.sun=true;
else
{
//simple bounds actions
if(sp.x>sfwidth_2)
{
sp.x-=sfwidth;
sp.border=true;
}
else if(sp.x<-sfwidth_2)
{
sp.x+=sfwidth;
sp.border=true;
}
if(sp.y>sfheight_2)
{
sp.y-=sfheight;
sp.border=true;
}
else if(sp.y<-sfheight_2)
{
sp.y+=sfheight;
sp.border=true;
}
}
}
return sp;
}
void Ai::nextPositions(AiSprite sp,QArray<AiSprite> *a,int frames)
{
int i,num;
double fmult=cfg->gamespeed*frames;
(*a)[0]=nextPosition(sp,cfg->gamespeed);
num=a->size();
for(i=1;i<num;i++)
(*a)[i]=nextPosition((*a)[i-1],fmult);
}
void Ai::calculateNextPositions()
{
unsigned int i,j;
MineSprite *ms;
j=(int)(calcPositionNumber[opt->aiDifficulty[playerNumber]]/cfg->gamespeed);
if(shipsNextPositions[0]->size() != j)
for(i=0;i<2;i++)
shipsNextPositions[i]->resize(j);
for(i=0;i<2;i++)
nextPositions(ship[i]->toAiSprite(),shipsNextPositions[i],
calcFrameIncrement[opt->aiDifficulty[playerNumber]]);
if(cfg->maxMines > aiMines[0]->size())
for(i=0;i<2;i++)
aiMines[i]->resize(cfg->maxMines);
for(i=0;i<2;i++)
{
j=0;
ms=mines[i]->first();
while(ms)
{
(*(aiMines[i]))[j]=ms->toAiSprite();
ms=mines[i]->next();
j++;
}
mineNumber[i]=j;
}
}
void Ai::tryShots()
{
AiSprite shot,me;
double rot,nr,nx,ny;
int i,f,frameIncrement,frameNum;
Hit hit;
Shot *goodShot;
me=ship[playerNumber]->toAiSprite();
rot=ship[playerNumber]->getRotation();
//Each 'frameIncrement' frames a shot is tried
frameIncrement=(int)((2*M_PI/calcShotDirections[opt->aiDifficulty[playerNumber]])
/cfg->rotationSpeed);
if(frameIncrement==0)
frameIncrement=1;
//Number of frames needed to rotate 180 degrees
frameNum=(int)(M_PI/(frameIncrement*cfg->rotationSpeed));
//if too much bullets are on the playfield, no shot is tried
if(bullets[playerNumber]->count()<cfg->maxBullets)
{
for(f=0;f<=frameNum;f++)
{
if(f!=0)
for(i=0;i<frameIncrement;i++)
me=nextPosition(me,cfg->gamespeed);
else
me=nextPosition(me,cfg->gamespeed);
if(!ship[playerNumber]->reloadsBullet(f*frameIncrement*cfg->gamespeed))
{
for(i=0;i<2;i++)
{
if((f==0)&&(i==1))
continue;
if(i==0)
nr=rot+frameIncrement*f*cfg->rotationSpeed;
else
nr=rot-frameIncrement*f*cfg->rotationSpeed;
nx=cos(nr);
ny=sin(nr);
shot.x=me.x+nx*SHOTDIST;
shot.y=me.y+ny*SHOTDIST;
shot.dx=me.dx+nx*cfg->shotSpeed;
shot.dy=me.dy+ny*cfg->shotSpeed;
shot.sun=false;
hit=firstObject(shot,f*frameIncrement,
calcFrameIncrement[opt->aiDifficulty[playerNumber]]);
if((hit.object!=HNOTHING) &&
!((hit.object==HSHIP)&&(hit.playerNumber==playerNumber)))
{
goodShot=new Shot;
goodShot->hit=hit;
goodShot->rotation=(i==0?RLEFT:RRIGHT);
goodShot->rotationFrames=f*frameIncrement;
goodShot->score=1e10;
myShots.append(goodShot);
}
}
}
}
}
}
Hit Ai::firstObject(AiSprite shot,int time,int frames)
{
int optime,i,num,rtime,basetime,t,m;
double dist,distx,disty,shiplastdist=0;
bool shipdistgreater=true,hitfound=false;
Hit hit={HNOTHING,0,0,0,1e10};
basetime=time/frames;
if((time%frames)>0)
basetime++;
rtime=basetime*frames-time;
optime=shipsNextPositions[0]->size();
num=optime-basetime;
if(num>0)
{
for(t=0;(t<num)&&(!hitfound)&&(!shot.sun);t++)
{
if(t==0)
shot=nextPosition(shot,cfg->gamespeed*rtime);
else
shot=nextPosition(shot,cfg->gamespeed*frames);
//distance to other objects
for(i=0;i<2;i++)
{
distx=(*(shipsNextPositions[i]))[basetime].x-shot.x;
disty=(*(shipsNextPositions[i]))[basetime].y-shot.y;
dist=distx*distx+disty*disty;
//own ship
if(i==playerNumber)
{
if(dist<shiplastdist)
shipdistgreater=false;
if((!shipdistgreater)&&(dist<hit.distance))
{
hit.object=HSHIP;
hit.objectNumber=0;
hit.playerNumber=i;
hit.hitTime=basetime*frames;
hit.distance=dist;
}
shiplastdist=dist;
}
//other ship
else if(dist<hit.distance)
{
hit.object=HSHIP;
hit.objectNumber=0;
hit.playerNumber=i;
hit.hitTime=basetime*frames;
hit.distance=dist;
}
//mines
for(m=0;m<mineNumber[i];m++)
{
distx=(*(aiMines[i]))[m].x-shot.x;
disty=(*(aiMines[i]))[m].y-shot.y;
dist=distx*distx+disty*disty;
if(dist<hit.distance)
{
hit.object=HMINE;
hit.playerNumber=i;
hit.objectNumber=m;
hit.hitTime=basetime*frames;
hit.distance=dist;
}
}
}
if(hit.distance<100)
hitfound=true;
basetime++;
}
}
return hit;
}
void Ai::testForHits()
{
AiSprite shot;
unsigned int i;
int m,p;
BulletSprite *bullet;
Hit *h;
Hit hit;
bool hitfound=false;
double distance,dx,dy;
if(calculateCollisions>0)
{
calculateCollisions--;
h=objectsHitByShip.first();
while(h)
{
if(h->hitTime>0)
{
h->hitTime--;
h=objectsHitByShip.next();
}
else
{
objectsHitByShip.remove();
h=objectsHitByShip.current();
}
}
h=minesHitByShot.first();
while(h)
{
if(h->hitTime>0)
{
h->hitTime--;
h=minesHitByShot.next();
}
else
{
minesHitByShot.remove();
h=minesHitByShot.current();
}
}
}
else
{
objectsHitByShip.clear();
minesHitByShot.clear();
for(i=0;i<2;i++)
{
for(bullet=bullets[i]->first();bullet;bullet=bullets[i]->next())
{
shot=bullet->toAiSprite();
hit=firstObject(shot,0,calcFrameIncrement[opt->aiDifficulty[playerNumber]]);
if(hit.object==HMINE)
{
h=new Hit(hit);
minesHitByShot.append(h);
}
if((hit.object==HSHIP)&&(hit.playerNumber==playerNumber))
{
h=new Hit(hit);
h->object=HSHOT;
objectsHitByShip.append(h);
}
}
}
hit.object=HNOTHING;
hit.distance=400;
for(i=0;(i<shipsNextPositions[0]->size()) &&
!(*shipsNextPositions[playerNumber])[i].sun;i++)
{
if((borderTime<0) && (*shipsNextPositions[playerNumber])[i].border)
borderTime=i*calcFrameIncrement[opt->aiDifficulty[playerNumber]];
dx=(*shipsNextPositions[playerNumber])[i].x;
dy=(*shipsNextPositions[playerNumber])[i].y;
distance=dx*dx+dy*dy;
if((distance<3025)&&(sunTime<0))
sunTime=i*calcFrameIncrement[opt->aiDifficulty[playerNumber]];
if(!hitfound)
for(p=0;p<2;p++)
for(m=0;m<mineNumber[p];m++)
{
dx=(*shipsNextPositions[playerNumber])[i].x-(*aiMines[p])[m].x;
dy=(*shipsNextPositions[playerNumber])[i].y-(*aiMines[p])[m].y;
distance=dx*dx+dy*dy;
if(hit.distance>distance)
{
hit.object=HMINE;
hit.playerNumber=p;
hit.objectNumber=m;
hit.hitTime=i*calcFrameIncrement[opt->aiDifficulty[playerNumber]];
hit.distance=distance;
if(distance<100)
hitfound=true;
}
}
}
if(hit.object!=HNOTHING)
{
h=new Hit(hit);
objectsHitByShip.append(h);
}
calculateCollisions=(int)(calcCollisions[opt->aiDifficulty[playerNumber]]/cfg->gamespeed);
}
}
void Ai::shotScores()
{
Shot *s;
Hit *h,*mh;
bool found,foundmh;
double dist,dx,dy,fuel;
dx=(*shipsNextPositions[playerNumber])[0].x-(*shipsNextPositions[opponentNumber])[0].x;
dy=(*shipsNextPositions[playerNumber])[0].y-(*shipsNextPositions[opponentNumber])[0].y;
dist=dx*dx+dy*dy;
for(s=myShots.first();s;s=myShots.next())
{
fuel=(100-(ship[playerNumber]->getEnergy()-cfg->shotEnergyNeed));
s->score=fuel*fuel/10 + s->hit.distance+s->hit.hitTime;
if(dist > (75*75))
s->score+=waitShot*8;
else
s->score+=waitShot*4;
if(s->hit.object==HMINE)
{
found=false;
for(h=objectsHitByShip.first();h && !found;h=objectsHitByShip.next())
{
if((h->object==HMINE)&&(h->playerNumber==s->hit.playerNumber)
&&(h->objectNumber==s->hit.objectNumber))
//ship will hit a mine that will be hitten by the shot
{
found=true;
//ship hits earlier then shot
if(h->hitTime<s->hit.hitTime)
s->score+=1000;
else
{
foundmh=false;
for(mh=minesHitByShot.first();mh && !foundmh;mh=minesHitByShot.next())
{
if((mh->playerNumber==s->hit.playerNumber)
&&(mh->objectNumber==s->hit.objectNumber))
//another shot will hit the mine
{
if(mh->hitTime<s->hit.hitTime)
s->score+=500;
else
s->score-=300;
}
}
}
}