/*************************************************************************** creature.cpp - description ------------------- begin : ven fév 22 22:27:00 CET 2002 copyright : (C) 2002 by Romain Vinot email : vinot@aist.enst.fr ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifdef _WIN32 #pragma warning (disable : 4786) #endif #include #include "creature.h" #include "sound.h" #include "board.h" #include "gamemanager.h" #include "guicreature.h" #include "guicommandline.h" #include "rand.h" Object::Object (GUICreature *guic, GUICommandLine *guicmdline, int p) : guicrea(guic), guicmd(guicmdline), position(p) {} Object::Object(const Object& copy) { guicrea = copy.guicrea; guicmd = copy.guicmd; position = copy.position; type = copy.type; } Object::~Object(void) {} void Object::setPosition(int p) { position = p; } ObjectType Object::Type(void) {return type;} int Object::getXPosition(void) {return position%board->Xmax;} int Object::getYPosition(void) {return position/board->Xmax;} int Object::getPosition(void) {return position;} int Object::getNeighborCase(Direction dir) { switch (dir) { case NORTH: if (position < board->Xmax) return -1; return position - board->Xmax; case SOUTH: if (position >= (board->Xmax-1)*board->Ymax) return -1; return position + board->Xmax; case WEST: if (position%board->Xmax == 0) return -1; return position - 1; case EAST: if (position%board->Xmax == board->Xmax-1) return -1; return position + 1; case NODIRECTION: return -1; } return -1; } int Object::getNeighborCaseFull(FullDirection d) { switch (d) { case NORTHNORTH: if (position < board->Xmax) return -1; return position - board->Xmax; case NORTHEAST: if (position < board->Xmax && position%board->Xmax == board->Xmax-1) return -1; return position - board->Xmax + 1; case NORTHWEST: if (position < board->Xmax && position%board->Xmax == 0) return -1; return position - board->Xmax - 1; case SOUTHSOUTH: if (position >= (board->Xmax-1)*board->Ymax) return -1; return position + board->Xmax; case SOUTHEAST: if (position >= (board->Xmax-1)*board->Ymax && position%board->Xmax == board->Xmax-1) return -1; return position + board->Xmax + 1; case SOUTHWEST: if (position >= (board->Xmax-1)*board->Ymax && position%board->Xmax == 0) return -1; return position + board->Xmax - 1; case WESTWEST: if (position%board->Xmax == 0) return -1; return position - 1; case EASTEAST: if (position%board->Xmax == board->Xmax-1) return -1; return position + 1; } return -1; } void Object::getNeighbors(set *ret) { int n=getNeighborCase(NORTH); int w=getNeighborCase(WEST); int e=getNeighborCase(EAST); int s=getNeighborCase(SOUTH); if (n>=0 && board->isNotWall(n)) ret->insert(n); if (w>=0 && board->isNotWall(w)) ret->insert(w); if (e>=0 && board->isNotWall(e)) ret->insert(e); if (s>=0 && board->isNotWall(s)) ret->insert(s); if (n>=0 && w>=0 && (board->isNotWall(n) || board->isNotWall(w))) { int nw = position - board->Xmax -1; if (board->isNotWall(nw)) ret->insert(nw); } if (n>=0 && e>=0 && (board->isNotWall(n) || board->isNotWall(e))) { int ne = position - board->Xmax +1; if (board->isNotWall(ne)) ret->insert(ne); } if (s>=0 && w>=0 && (board->isNotWall(s) || board->isNotWall(w))) { int sw = position + board->Xmax -1; if (board->isNotWall(sw)) ret->insert(sw); } if (s>=0 && e>=0 && (board->isNotWall(s) || board->isNotWall(e))) { int se = position + board->Xmax +1; if (board->isNotWall(se)) ret->insert(se); } } void Object::getEmptyNeighbors(set *ret) { getNeighbors(ret); list temp; for (set::iterator it=ret->begin(); it!=ret->end(); it++) { if (!board->isEmpty(*it)) temp.push_back(*it); } for (list::iterator ite=temp.begin(); ite!=temp.end(); ite++) ret->erase(*ite); } void Object::getAdjacentGenestealers(set *lst, Creature *shooter) { int n=getNeighborCase(NORTH); int w=getNeighborCase(WEST); int e=getNeighborCase(EAST); int s=getNeighborCase(SOUTH); set *objs; objs=board->getObject(n); if (objs) for (set::iterator it=objs->begin(); it!=objs->end(); it++) if (n>=0 && ((*it)->Type()==GENESTEALER || (*it)->Type()==DOOR) && (*it)->isAttackable() && shooter->canShootAt(n) && shooter->NoObstacleTo(n)) lst->insert(*it); objs=board->getObject(e); if (objs) for (set::iterator it=objs->begin(); it!=objs->end(); it++) if (e>=0 && ((*it)->Type()==GENESTEALER || (*it)->Type()==DOOR) && (*it)->isAttackable() && shooter->canShootAt(e) && shooter->NoObstacleTo(e)) lst->insert(*it); objs=board->getObject(w); if (objs) for (set::iterator it=objs->begin(); it!=objs->end(); it++) if (w>=0 && ((*it)->Type()==GENESTEALER || (*it)->Type()==DOOR) && (*it)->isAttackable() && shooter->canShootAt(w) && shooter->NoObstacleTo(w)) lst->insert(*it); objs=board->getObject(s); if (objs) for (set::iterator it=objs->begin(); it!=objs->end(); it++) if (s>=0 && ((*it)->Type()==GENESTEALER || (*it)->Type()==DOOR) && (*it)->isAttackable() && shooter->canShootAt(s) && shooter->NoObstacleTo(s)) lst->insert(*it); int nw = position - board->Xmax -1; int ne = position - board->Xmax +1; int sw = position + board->Xmax -1; int se = position + board->Xmax +1; objs=board->getObject(nw); if (objs) for (set::iterator it=objs->begin(); it!=objs->end(); it++) if (n>=0 && w>=0 && ((*it)->Type()==GENESTEALER ||(*it)->Type()==DOOR) && (*it)->isAttackable() && shooter->canShootAt(nw) && shooter->NoObstacleTo(nw)) lst->insert((*it)); objs=board->getObject(ne); if (objs) for (set::iterator it=objs->begin(); it!=objs->end(); it++) if (n>=0 && e>=0 && ((*it)->Type()==GENESTEALER ||(*it)->Type()==DOOR) && (*it)->isAttackable() && shooter->canShootAt(ne) && shooter->NoObstacleTo(ne)) lst->insert(*it); objs=board->getObject(sw); if (objs) for (set::iterator it=objs->begin(); it!=objs->end(); it++) if (s>=0 && w>=0 && ((*it)->Type()==GENESTEALER ||(*it)->Type()==DOOR) && (*it)->isAttackable() && shooter->canShootAt(sw) && shooter->NoObstacleTo(sw)) lst->insert(*it); objs=board->getObject(se); if (objs) for (set::iterator it=objs->begin(); it!=objs->end(); it++) if (s>=0 && e>=0 && ((*it)->Type()==GENESTEALER ||(*it)->Type()==DOOR) && (*it)->isAttackable() && shooter->canShootAt(se) && shooter->NoObstacleTo(se)) lst->insert(*it); } bool Object::isEmpty(void) {return false;} int Object::getId(void) {return 0;} bool Object::Open(void) {return false;} bool Object::Close(void) {return false;} bool Object::canBeOpened(void) {return false;} bool Object::canBeClosed(void) {return false;} int Object::canBeTaken(void) {return 0;} int Object::canBeDropped(void) {return 0;} int Object::canBeTransfered(void) {return 0;} Direction Object::getOrientation(void) {return NODIRECTION;} void Object::turnOrientation(Direction dir) {} //void Object::putOnView(void) {} bool Object::canTurn(void) {return false;} bool Object::isAttackable(void) {return false;} bool Object::isFireable(void) {return false;} bool Object::isShootable(void) {return false;} bool Object::isMarinePlayer(void) {return false;} bool Object::isSelectable(void) {return false;} bool Object::canAttack(Direction dir) {return false;} int Object::ThrowDice(bool reThrow, int value) {return 0;} // default reThrow=0, value=0 bool Object::onOverwatch(void) {return false;} bool Object::isJammed(void) {return false;} void Object::isAttacked(void) {} bool Object::shootHasKilledObject(void) {return true;} // default: one shot kill is enough to kill any object. void Object::hasBeenTaken(void) {} void Object::hasBeenFree(void) {} bool Object::dropTakenObject(bool fromDead) { return false; } bool Object::hasAnObject(void) {return false;} void Object::automaticTakeObject(Object *obj) {} ExtensibleObject::ExtensibleObject (GUICreature *guic, GUICommandLine *guicmd, int pos) : Object(guic, guicmd, pos), stateIsEmpty(false), stateIsAttackable(false), stateIsShootable(0), stateIsFireable(false), stateCanBeTaken(0), stateDropable(0), stateTransferable(0), stateShootWith(true), stateUseDoorWith(true), isTaken(false) {type = EXTENSIBLE;} ExtensibleObject::ExtensibleObject(const ExtensibleObject& copy) : Object(copy) { desc = copy.desc; graphic = copy.graphic; stateIsEmpty = copy.stateIsEmpty; stateIsAttackable = copy.stateIsAttackable; stateIsShootable = copy.stateIsShootable; stateIsFireable = copy.stateIsFireable; stateCanBeTaken = copy.stateCanBeTaken; stateDropable = copy.stateDropable; stateTransferable = copy.stateTransferable; stateShootWith = copy.stateShootWith; stateUseDoorWith = copy.stateUseDoorWith; isTaken = copy.isTaken; } Object* ExtensibleObject::getCopy() const { return new ExtensibleObject(*this); } ExtensibleObject::~ExtensibleObject() {} void ExtensibleObject::printInfo(int loc) {guicrea->printInfo(this,false,loc);} char ExtensibleObject::getChar(void) { return '*'; } void *ExtensibleObject::getPixmap(bool big) {return guicrea->getPixmap(this,big);} bool ExtensibleObject::isAttackable(void) {return stateIsAttackable;} bool ExtensibleObject::isFireable(void) {return stateIsFireable;} bool ExtensibleObject::isShootable(void) {return stateIsShootable;} bool ExtensibleObject::shootHasKilledObject(void) { if (stateIsShootable>0) { stateIsShootable--; return (stateIsShootable==0); } return false; } int ExtensibleObject::canBeTaken(void) { if (isTaken) return 0; else return stateCanBeTaken; } int ExtensibleObject::canBeDropped(void) { return stateDropable; } int ExtensibleObject::canBeTransfered(void) { return stateTransferable; } void ExtensibleObject::hasBeenTaken(void) {isTaken=true;} void ExtensibleObject::hasBeenFree(void) {isTaken=false;} bool ExtensibleObject::isEmpty(void) {return stateIsEmpty;} int ExtensibleObject::ThrowDice(bool reThrow, int value) {return 5;} void ExtensibleObject::setDesc(QString &d) {desc = d;} QString ExtensibleObject::getDesc(void) {return desc;} void ExtensibleObject::setGraphic(QString &g) {graphic = g;} void ExtensibleObject::setEmpty(bool e) {stateIsEmpty=e;} void ExtensibleObject::setAttackable(bool a) {stateIsAttackable=a;} void ExtensibleObject::setShootable(int s) {stateIsShootable=s;} void ExtensibleObject::setFireable(bool f) {stateIsFireable=f;} void ExtensibleObject::setTaken(int t) {stateCanBeTaken=t;} void ExtensibleObject::setDropable(int d) {stateDropable=d;} void ExtensibleObject::setTransferable(int t) {stateTransferable=t;} void ExtensibleObject::setShootWith(bool sw) {stateShootWith=sw;} void ExtensibleObject::setUseDoorWith(bool udw) {stateUseDoorWith=udw;} EntryZone::EntryZone (GUICreature *guic, GUICommandLine *guicmd, int p, Direction d, int maxb) : Object(guic, guicmd, p), graphicalDir(d), maxblip (maxb) {type = ENTRYZONE;} EntryZone::EntryZone(const EntryZone& copy) : Object(copy) { graphicalDir = copy.graphicalDir; maxblip = copy.maxblip; } EntryZone::~EntryZone(void) {} Object* EntryZone::getCopy() const { return new EntryZone(*this); } void EntryZone::printInfo(int loc) {guicrea->printInfo(this,false,loc);} char EntryZone::getChar(void) { set *objs = board->getObject(position); switch (objs->size()) { case 1: return 'X'; case 2: return '1'; case 3: return '2'; case 4: return '3'; } return '4'; } void *EntryZone::getPixmap(bool big) {return guicrea->getPixmap(this,big);} int EntryZone::getMaxBlip(void) {return maxblip;} Exit::Exit (GUICreature *guic, GUICommandLine *guicmd, int p, Turn t, Direction dir) : Object(guic, guicmd, p), graphicalDir(dir), passingTurn(t) {type = EXIT;} Exit::Exit(const Exit& copy) : Object(copy) { passingTurn = copy.passingTurn; graphicalDir = copy.graphicalDir; } Exit::~Exit(void) {} Object* Exit::getCopy() const { return new Exit(*this); } bool Exit::isEmpty(void) { if (man->getTurn() == passingTurn) return true; else return false; } void Exit::addCreatureOut(ObjectType ty, int id) { out[ty].push_back(id); } void Exit::printInfo(int loc) {guicrea->printInfo(this,false,loc);} char Exit::getChar(void) { return 'x';} void *Exit::getPixmap(bool big) {return guicrea->getPixmap(this,big);} Door::Door (GUICreature *xcrea, GUICommandLine *guicmd, int p, bool v, DoorState s) : Object(xcrea, guicmd, p), state(s), isVertical(v) // default : s=CLOSE {type = DOOR;} Door::Door(const Door& copy) : Object(copy) { state = copy.state; isVertical = copy.isVertical; }; Door::~Door(void) {} Object* Door::getCopy() const { return new Door(*this); } bool Door::isEmpty(void) { switch (state) { case OPEN: return true; case CLOSE: return false; } return false; } bool Door::isAttackable(void) { if (state==OPEN) return false; return true; } bool Door::isShootable(void) { return isAttackable(); } bool Door::isFireable(void) { if (state==OPEN) return true; return false; } int Door::ThrowDice(bool reThrow, int value) {return 5;} // default reThrow=0, value=0 bool Door::Open(void) { if (state==OPEN) return false; sound->playSound(SoundSystem::DoorOpen); state = OPEN; return true; } bool Door::Close(void) { if (state==CLOSE) return false; sound->playSound(SoundSystem::DoorClose); state = CLOSE; return true; } bool Door::canBeOpened(void) { return (state==CLOSE); } bool Door::canBeClosed(void) { return (state==OPEN); } void Door::printInfo(int loc) {guicrea->printInfo(this,false,loc);} char Door::getChar(void) {if (state==CLOSE) return 'd'; return 'D';} void *Door::getPixmap(bool big) {return guicrea->getPixmap(this, big);} Bulkhead::Bulkhead (GUICreature *xcrea, GUICommandLine *guicmd, int p, DoorState s, bool ver, bool op, bool cl) : Object(xcrea, guicmd, p), state(s), isVertical(ver), stateCanBeOpened(op), stateCanBeClosed(cl) // default : s=OPEN, canBeOpened=false, canBeClosed=true {type = BULKHEAD;} Bulkhead::Bulkhead(const Bulkhead& copy) : Object(copy) { state = copy.state; isVertical = copy.isVertical; stateCanBeOpened = copy.stateCanBeOpened; stateCanBeClosed = copy.stateCanBeClosed; }; Bulkhead::~Bulkhead(void) {} Object* Bulkhead::getCopy() const { return new Bulkhead(*this); } bool Bulkhead::isEmpty(void) { switch (state) { case OPEN: return true; case CLOSE: return false; } return false; } bool Bulkhead::Open(void) { if (state==OPEN || !stateCanBeOpened) return false; sound->playSound(SoundSystem::DoorOpen); state = OPEN; return true; } bool Bulkhead::Close(void) { if (state==CLOSE || !stateCanBeClosed) return false; sound->playSound(SoundSystem::DoorClose); state = CLOSE; return true; } bool Bulkhead::canBeOpened(void) { return (state==CLOSE && stateCanBeOpened); } bool Bulkhead::canBeClosed(void) { return (state==OPEN && stateCanBeClosed); } void Bulkhead::setCanBeOpened(bool op) { stateCanBeOpened = op; } void Bulkhead::setCanBeClosed(bool cl) { stateCanBeClosed = cl; } void Bulkhead::printInfo(int loc) {guicrea->printInfo(this,false,loc);} char Bulkhead::getChar(void) {if (state==CLOSE) return 'd'; return 'D';} void *Bulkhead::getPixmap(bool big) {return guicrea->getPixmap(this, big);} Flame::Flame (GUICreature *xcrea, GUICommandLine *guicmd, int p) : Object(xcrea, guicmd, p), nbFlames(1) {type = FLAME;} Flame::Flame(const Flame& copy) : Object(copy) { nbFlames = copy.nbFlames; } Flame::~Flame(void) {} Object* Flame::getCopy() const { return new Flame(*this); } bool Flame::isEmpty(void) {return isTransparent;} void Flame::addBigFlame(void) {nbFlames = 2;} bool Flame::reduceFlame(void) {nbFlames--; return (nbFlames<=0);} void Flame::setTransparency(bool yes) {isTransparent=yes;} bool Flame::isFireable(void) {return true;} void Flame::printInfo(int loc) {guicrea->printInfo(this,false,loc);} char Flame::getChar(void) {return '*';} void *Flame::getPixmap(bool big) {return guicrea->getPixmap(this, big);} Creature::Creature (GUICreature *xcrea, GUICommandLine *guicmd, int p, int i) : Object(xcrea, guicmd, p), id(i), initTurn(man->getTurnNumber()), selected(false), actionPts(0), lastActionPts(0), taken(0) {} Creature::Creature(const Creature& copy) : Object(copy) { id = copy.id; initTurn = copy.initTurn; selected = copy.selected; actionPts = copy.actionPts; lastActionPts = copy.actionPts; } Creature::~Creature (void) {} int Creature::getId (void) {return id;} bool Creature::MoveToCase(int pos) { if (board->isEmpty(pos)) { board->move(this, position, pos); position = pos; if (taken) { board->move(taken,taken->getPosition(),pos); taken->setPosition(pos); } return true; } else return false; } bool Creature::Move(FullDirection d) { Direction first=NORTH,second=EAST; switch(d) { case NORTHNORTH: return MoveToCase(getNeighborCase(NORTH)); case SOUTHSOUTH: return MoveToCase(getNeighborCase(SOUTH)); case EASTEAST: return MoveToCase(getNeighborCase(EAST)); case WESTWEST: return MoveToCase(getNeighborCase(WEST)); case NORTHEAST: first=NORTH; second=EAST; break; case NORTHWEST: first=NORTH; second=WEST; break; case SOUTHEAST: first=SOUTH; second=EAST; break; case SOUTHWEST: first=SOUTH; second=WEST; break; } int oldPos = position; if (MoveToCase(getNeighborCase(first))) { if (MoveToCase(getNeighborCase(second))) return true; else { MoveToCase(oldPos); return false; } } else if (MoveToCase(getNeighborCase(second))) { if (MoveToCase(getNeighborCase(first))) return true; else { MoveToCase(oldPos); return false; } } guicmd->Write("You can't move in diagonal, if no radial square is empty. Same goes for line of sight."); return false; } void Creature::beginNewTurn(Turn turn) { currentTurn = turn; lastActionPts = actionPts; } void Creature::begin(Turn turn) { currentTurn = turn; } void Creature::setInitTurn(int turn) { initTurn = turn; } int Creature::getInitTurn(void) {return initTurn;} int Creature::getActionPts(void) {return actionPts;} void Creature::setActionPts(int aps) {actionPts=aps;} Direction Creature::getOrientation(void) {return NODIRECTION;} void Creature::setOrientation(Direction d) {} bool Creature::TurnOrientation(Direction d) {return false;} bool Creature::Enter(void) {return false;} bool Creature::Revert(void) {return false;} AttackRes Creature::Attack(void) {return AttackRes(false,0,0);} bool Creature::canViewAt(int c) {return false;} bool Creature::canShootAt(int c) {return 0;} AttackRes Creature::Shoot(int c) {return AttackRes(false,0,0);} bool Creature::OverwatchShoot(int c) {return false;} bool Creature::Overwatch(void) {return false;} bool Creature::Unjam(void) {return false;} bool Creature::Fire(int c) {return false;} bool Creature::Reload(void) {return false;} bool Creature::takeObject(void) {return false;} Object * Creature::transferObject(int newpos) {return 0;} bool Creature::hasAnObject(void) {return (taken!=0); } bool Creature::NoObstacleTo(int targetPos) { int xdist = targetPos%board->Xmax - position%board->Xmax; int ydist = targetPos/board->Xmax - position/board->Xmax; int xdir = (xdist>=0) ? 1 : -1; int ydir = (ydist>=0) ? 1 : -1; xdist = abs(xdist); ydist = abs(ydist); float xcurr = 0.5, ycurr = 0.5; while (xcurr < xdist || ycurr < ydist) { if (xcurr != 0.5 || ycurr != 0.5) { int currPos = (int) (position%board->Xmax + xdir*(xcurr-0.5) + (position/board->Xmax + ydir*(ycurr-0.5))*board->Xmax); if (!board->isEmpty(currPos)) return false; } if (xcurr*ydist > ycurr*xdist) ycurr++; else if (xcurr*ydist < ycurr*xdist) xcurr++; else { // Egalité int latPos1 = (int) (position%board->Xmax + xdir*(xcurr+0.5) + (position/board->Xmax + ydir*(ycurr-0.5))*board->Xmax); int latPos2 = (int) (position%board->Xmax + xdir*(xcurr-0.5) + (position/board->Xmax + ydir*(ycurr+0.5))*board->Xmax); if (!board->isEmpty(latPos1) && !board->isEmpty(latPos2)) return false; xcurr++; ycurr++; } } return true; } bool Creature::GenestealerEnter (void) { if (!board->isEntryZoneCase(position)) return false; if (initTurn==man->getTurnNumber() && man->isWaitingEntry(position)) { guicmd->Write("A marine within six squares of the entry prevents" "your unit to enter during this turn."); return false; } set *obj = board->getObject(position); for (set::iterator it=obj->begin(); it!=obj->end(); it++) if ((*it)->Type()==BULKHEAD && !(*it)->isEmpty()) return false; set ls; getEmptyNeighbors(&ls); if (ls.size()==1) { int targetCase = *(ls.begin()); if (type==BLIP && man->CaseIsVisibleFromMarine(targetCase)!=-1) return false; return MoveToCase(targetCase); } else if (ls.size() >= 2) { int caseId = guicmd->GetCase(&ls, "There is more than one empty corridor.\nChoose the one you want to enter in.\n"); man->recordCommand(QString("get case ") + QString::number(caseId)); if (type==BLIP && man->CaseIsVisibleFromMarine(caseId)!=-1) return false; return MoveToCase(caseId); } return false; } int Creature::EndTurnActionPts() { return lastActionPts; } NonOrientedCreature::NonOrientedCreature(GUICreature *gc,GUICommandLine *gcmd, int pos, int id) : Creature(gc, gcmd, pos, id) {} NonOrientedCreature::NonOrientedCreature(const NonOrientedCreature& copy) : Creature (copy) {} NonOrientedCreature::~NonOrientedCreature(void) {} bool NonOrientedCreature::OpenDoor(int cId) { set doorCases; getNeighbors(&doorCases); if (cId!=-1) { if (doorCases.find(cId)!=doorCases.end()) { doorCases.clear(); doorCases.insert(cId); } else return false; } set doors; for (set::iterator it=doorCases.begin(); it!=doorCases.end(); it++) { set *objs = board->getObject(*it); if (objs) for (set::iterator ite=objs->begin(); ite!=objs->end(); ite++) if ((*ite)->canBeOpened()) doors.insert(*ite); } if (doors.size() == 1) { (*doors.begin())->Open(); return true; } if (doors.size() >= 2) { set cases; for (set::iterator it1=doors.begin(); it1!=doors.end(); it1++) cases.insert((*it1)->getPosition()); int caseId = guicmd->GetCase(&cases, "There is more than one door.\nChoose the one you want to open.\n"); man->recordCommand(QString("get case ") + QString::number(caseId)); for (set::iterator it2=doors.begin(); it2!=doors.end(); it2++) if ((*it2)->getPosition()==caseId) { (*it2)->Open(); return true; } } return false; } bool NonOrientedCreature::CloseDoor(int cId) { set doorCases; getNeighbors(&doorCases); if (cId!=-1) if (doorCases.find(cId)!=doorCases.end()) { doorCases.clear(); doorCases.insert(cId); } else return false; set doors; for (set::iterator it=doorCases.begin(); it!=doorCases.end(); it++) { set *objs = board->getObject(*it); if (objs && (board->isEntryZoneCase(*it) || board->isEmpty(*it))) for (set::iterator ite=objs->begin(); ite!=objs->end(); ite++) if ((*ite)->canBeClosed()) doors.insert(*ite); } if (doors.size() == 1) { (*doors.begin())->Close(); return true; } if (doors.size() >= 2) { set cases; for (set::iterator it1=doors.begin(); it1!=doors.end(); it1++) cases.insert((*it1)->getPosition()); int caseId = guicmd->GetCase(&cases, "There is more than one door.\nChoose the one you want to close.\n"); man->recordCommand(QString("get case ") + QString::number(caseId)); for (set::iterator it2=doors.begin(); it2!=doors.end(); it2++) if ((*it2)->getPosition()==caseId) { (*it2)->Close(); return true; } } return false; } ModuleCAT::ModuleCAT(GUICreature *guic, GUICommandLine *guicmd, int p, int i) : NonOrientedCreature(guic, guicmd, p, i), isTaken(false) {type=CAT;} ModuleCAT::ModuleCAT(const ModuleCAT& copy) : NonOrientedCreature(copy) {} ModuleCAT::~ModuleCAT(void) {} Object* ModuleCAT::getCopy() const { return new ModuleCAT(*this); } /*void ModuleCAT::getInfo(QDomElement &unitlist) { QDomElement unit = unitlist.createElement("unitelem"); unitlist.appendChild(unit); QDomElement }*/ void ModuleCAT::printInfo(int loc) {guicrea->printInfo(this,selected,loc);} char ModuleCAT::getChar(void) {return 'a';} void *ModuleCAT::getPixmap(bool big) {return guicrea->getPixmap(this,big);} bool ModuleCAT::isMarinePlayer(void) {return false;} // ModuleCAT can not view genestealer. bool ModuleCAT::isEmpty(void) {return true;} bool ModuleCAT::isAttackable(void) {return true;} bool ModuleCAT::isShootable(void) {return false;} int ModuleCAT::canBeTaken(void) { if (isTaken) return 0; return 1; } int ModuleCAT::canBeDropped(void) { return 0; } int ModuleCAT::canBeTransfered(void) { return 1; } void ModuleCAT::hasBeenTaken(void) { isTaken=true; } void ModuleCAT::hasBeenFree(void) { isTaken=false; } bool ModuleCAT::isSelectable (void) { return (currentTurn==CAT_TURN && actionPts != 0); } bool ModuleCAT::select(void) { if (isSelectable()) { selected = true; hasDoneSomething=false; return true; } return false; } void ModuleCAT::deselect(void) { lastActionPts = actionPts; selected=false; if (hasDoneSomething) actionPts=0; } bool ModuleCAT::Move(FullDirection d) { if (actionPts < 1) return false; if (Creature::Move(d)) { sound->playSound(SoundSystem::BlipMove); actionPts--; hasDoneSomething=true; return true; } return false; } bool ModuleCAT::OpenDoor(int cId) { if (actionPts < 1) return false; if (NonOrientedCreature::OpenDoor(cId)) { actionPts--; hasDoneSomething=true; return true; } return false; } bool ModuleCAT::CloseDoor(int cId) { if (actionPts < 1) return false; if (NonOrientedCreature::CloseDoor(cId)) { actionPts--; hasDoneSomething=true; return true; } return false; } void ModuleCAT::beginNewTurn(Turn turn) { if (turn==CAT_TURN) actionPts = 3; else actionPts = 0; Creature::beginNewTurn(turn); } Blip::Blip(GUICreature *guic, GUICommandLine *guicmd, int p, int i, int nb) : NonOrientedCreature(guic, guicmd, p, i), nbGen(nb) {type=BLIP;} Blip::Blip(const Blip& copy) : NonOrientedCreature(copy) { type = BLIP; nbGen = copy.nbGen; } Blip::~Blip(void) {} Object* Blip::getCopy() const { return new Blip(*this); } bool Blip::select(void) { if (currentTurn == GENESTEALER_TURN && actionPts != 0) { selected = true; hasDoneSomething = false; return true; } return false; } void Blip::deselect(void) { selected = false; lastActionPts = actionPts; if (hasDoneSomething) actionPts = 0; } void Blip::beginNewTurn(Turn turn) { if (turn == GENESTEALER_TURN) { actionPts = 6; } Creature::beginNewTurn(turn); } bool Blip::shootHasKilledObject(void) { nbGen--; return (nbGen==0); } bool Blip::Move(FullDirection d) { if (board->isEntryZoneCase(position)) { guicmd->Write("You can't \"move\" when your unit is on an entry zone.\n" "You must use the button \"enter the board\" first."); return false; } if (actionPts < 1) return false; if (man->CaseIsVisibleFromMarine(getNeighborCaseFull(d))!=-1) { guicmd->Write("You can't move in a square visible by a marine."); return false; } if (Creature::Move(d)) { hasDoneSomething=true; sound->playSound(SoundSystem::BlipMove); actionPts--; return true; } return false; } bool Blip::Enter(void) { if (actionPts>1 && Creature::GenestealerEnter()) { actionPts--; return true; } return false; } bool Blip::Revert(void) { if (actionPts != 6) { guicmd->Write("Reversion must be done before any movement."); return false; } return true; } bool Blip::OpenDoor(int cId) { if (board->isEntryZoneCase(position)) return false; if (actionPts < 1) return false; if (NonOrientedCreature::OpenDoor(cId)) { hasDoneSomething=true; actionPts--; return true; } return false; } bool Blip::CloseDoor(int cId) { if (board->isEntryZoneCase(position)) return false; if (actionPts < 1) return false; if (NonOrientedCreature::CloseDoor(cId)) { hasDoneSomething=true; actionPts--; return true; } return false; } AttackRes Blip::Attack(void) { if (board->isEntryZoneCase(position)) return AttackRes(false,0,0); if (actionPts < 1) return AttackRes(false,0,0); Object *definitiveTarget=0; set targetCases; set targets; targetCases.insert (getNeighborCase(NORTH)); targetCases.insert (getNeighborCase(WEST)); targetCases.insert (getNeighborCase(EAST)); targetCases.insert (getNeighborCase(SOUTH)); for (set::iterator it=targetCases.begin(); it!=targetCases.end(); it++){ set *objs = board->getObject(*it); if (objs) for (set::iterator ite=objs->begin(); ite!=objs->end(); ite++) if ((*ite)->Type() == DOOR) targets.insert(*ite); } if (targets.size() == 1) definitiveTarget = *targets.begin(); if (targets.size() >= 2) { set cases; for(set::iterator it1=targets.begin(); it1!=targets.end(); it1++) cases.insert((*it1)->getPosition()); int caseId = guicmd->GetCase(&cases, "There is more than one door.\nChoose the one you want to attack.\n"); man->recordCommand(QString("get case") + QString::number(caseId)); for(set::iterator it2=targets.begin(); it2!=targets.end(); it2++) if ((*it2)->getPosition()==caseId) definitiveTarget = *it2; } if (definitiveTarget) { hasDoneSomething=true; actionPts--; int val = 1+Rand::roll(6); if (val>5) return AttackRes(true,1, definitiveTarget); else return AttackRes(true,0, definitiveTarget); } return AttackRes(false,0,0); } int Blip::getNbGen(void) {return nbGen;} void Blip::printInfo(int loc) {guicrea->printInfo(this,selected,loc);} char Blip::getChar(void) {return 'b';} void *Blip::getPixmap(bool big) {return guicrea->getPixmap(this,big);} bool Blip::isMarinePlayer (void) {return false;} int Blip::getActionPts(void) {return actionPts;} bool Blip::isSelectable (void) { return (currentTurn==GENESTEALER_TURN && actionPts!=0); } RealCreature::RealCreature(GUICreature *x, GUICommandLine *guicmd, int p, int i, Direction ori) : Creature(x, guicmd, p, i), orientation(ori), history(NONE) {} RealCreature::RealCreature(const RealCreature& copy) : Creature(copy) { orientation = copy.orientation; history = copy.history; } RealCreature::~RealCreature() {} Direction RealCreature::getOrientation(void) {return orientation;} void RealCreature::setOrientation(Direction d) {orientation=d;} void RealCreature::turnOrientation(Direction dir) {orientation=dir;} bool RealCreature::canTurn(void) {return true;} RelativeDirection RealCreature::CardinalToRelativeDirection(FullDirection dir) { switch(orientation) { case NORTH: switch(dir) { case NORTHNORTH: case NORTHEAST: case NORTHWEST: return FRONT; case SOUTHSOUTH: case SOUTHEAST: case SOUTHWEST: return BACK; case WESTWEST: case EASTEAST: return SIDE; default: return FRONT; } case SOUTH: switch(dir) { case SOUTHSOUTH: case SOUTHEAST: case SOUTHWEST: return FRONT; case NORTHNORTH: case NORTHEAST: case NORTHWEST: return BACK; case WESTWEST: case EASTEAST: return SIDE; default: return FRONT; } case WEST: switch(dir) { case NORTHWEST: case SOUTHWEST: case WESTWEST: return FRONT; case NORTHEAST: case SOUTHEAST: case EASTEAST: return BACK; case NORTHNORTH: case SOUTHSOUTH: return SIDE; default: return FRONT; } case EAST: switch(dir) { case NORTHEAST: case SOUTHEAST: case EASTEAST: return FRONT; case NORTHWEST: case SOUTHWEST: case WESTWEST: return BACK; case NORTHNORTH: case SOUTHSOUTH: return SIDE; default: return FRONT; } case NODIRECTION: return FRONT; } return FRONT; } void RealCreature::getFrontalCases(set *cases) { set all; getNeighbors(&all); int frontCase = getNeighborCase(orientation); if (all.find(frontCase) != all.end()) cases->insert(frontCase); int toAdd=0;; switch(orientation) { case NORTH: case SOUTH: toAdd = 1; break; case EAST: case WEST: toAdd = board->Xmax; break; case NODIRECTION: break; } if (all.find(frontCase-toAdd) != all.end()) cases->insert(frontCase-toAdd); if (all.find(frontCase+toAdd) != all.end()) cases->insert(frontCase+toAdd); } bool RealCreature::OpenDoor(int cId) { set doorCases; getFrontalCases(&doorCases); if (cId!=-1) { if (doorCases.find(cId)!=doorCases.end()) { doorCases.clear(); doorCases.insert(cId); } else return false; } set doors; for (set::iterator it=doorCases.begin(); it!=doorCases.end(); it++) { set *objs = board->getObject(*it); if (objs) for (set::iterator ite=objs->begin(); ite!=objs->end(); ite++) if ((*ite)->canBeOpened()) doors.insert(*ite); } if (doors.size() == 1) { (*doors.begin())->Open(); return true; } if (doors.size() >= 2) { set cases; for (set::iterator it1=doors.begin(); it1!=doors.end(); it1++) cases.insert((*it1)->getPosition()); int caseId = guicmd->GetCase(&cases, "There is more than one door.\nChoose the one you want to open.\n"); man->recordCommand(QString("get case ") + QString::number(caseId)); for (set::iterator it2=doors.begin(); it2!=doors.end(); it2++) if ((*it2)->getPosition()==caseId) { (*it2)->Open(); return true; } } return false; } bool RealCreature::CloseDoor(int cId) { set doorCases; getFrontalCases(&doorCases); if (cId!=-1) { if (doorCases.find(cId)!=doorCases.end()) { doorCases.clear(); doorCases.insert(cId); } else return false; } set doors; for (set::iterator it=doorCases.begin(); it!=doorCases.end(); it++) { set *objs = board->getObject(*it); if (objs && (board->isEntryZoneCase(*it) || board->isEmpty(*it))) for (set::iterator ite=objs->begin(); ite!=objs->end(); ite++) if ((*ite)->canBeClosed()) doors.insert(*ite); } if (doors.size() == 1) { (*doors.begin())->Close(); return true; } if (doors.size() >= 2) { set cases; for (set::iterator it1=doors.begin(); it1!=doors.end(); it1++) cases.insert((*it1)->getPosition()); int caseId = guicmd->GetCase(&cases, "There is more than one door.\nChoose the one you want to close.\n"); man->recordCommand(QString("get case ") + QString::number(caseId)); for (set::iterator it2=doors.begin(); it2!=doors.end(); it2++) if ((*it2)->getPosition()==caseId) { (*it2)->Close(); return true; } } return false; } AttackRes RealCreature::Attack(void) { pair res = RealCreature::PreliminaryAttack(); if (res.first) { Object *target = res.second; target->isAttacked(); int myValue, targetValue; if (type == SERGEANT) { myValue = ThrowDice(); targetValue = target->ThrowDice(true, myValue); } else if (target->Type() == SERGEANT) { targetValue = target->ThrowDice(); myValue = ThrowDice(true, targetValue); } else { myValue = ThrowDice(); targetValue = target->ThrowDice(); } if (myValue>targetValue) { return AttackRes (true,1,target); } else if (myValueXmax; int ycoord = caseId/board->Xmax; switch (orientation) { case NORTH: return (ycoord <= position/board->Xmax); case SOUTH: return (ycoord >= position/board->Xmax); case EAST: return (xcoord >= position%board->Xmax); case WEST: return (xcoord <= position%board->Xmax); case NODIRECTION: return false; } return false; } bool RealCreature::canShootAt(int caseId) { int xcoord = caseId%board->Xmax; int ycoord = caseId/board->Xmax; int xcurr = position%board->Xmax; int ycurr = position/board->Xmax; switch (orientation) { case NORTH: return ((ycoord <= ycurr) && (ycurr-ycoord >= abs(xcurr-xcoord))); case SOUTH: return ((ycoord >= ycurr) && (ycoord-ycurr >= abs(xcurr-xcoord))); case EAST: return ((xcoord >= xcurr) && (xcoord-xcurr >= abs(ycurr-ycoord))); case WEST: return ((xcoord <= xcurr) && (xcurr-xcoord >= abs(ycurr-ycoord))); case NODIRECTION: return false; } return false; } pair RealCreature::PreliminaryAttack(void) { if (actionPts < 1) return pair (false,0); int targetCase = getNeighborCase(orientation); if (targetCase == -1 || board->isEntryZoneCase(targetCase)) return pair (false,0); set *tgts = board->getObject(targetCase); if (tgts) { for (set::iterator it=tgts->begin(); it!=tgts->end(); it++) if ((*it)->isAttackable()) return pair (true, *it); guicmd->Write("There is no attackable unit in front of you."); } return pair (false,0); } bool RealCreature::canAttack (Direction dir) { if ((dir == NORTH && orientation == SOUTH) || (dir == SOUTH && orientation == NORTH) || (dir == EAST && orientation == WEST) || (dir == WEST && orientation == EAST)) return true; else return false; } Genestealer::Genestealer (GUICreature *x, GUICommandLine *c, int p, int i, Direction ori,int apts) : RealCreature(x, c, p, i, ori) { type=GENESTEALER; actionPts=apts; } Genestealer::Genestealer(const Genestealer& copy) : RealCreature(copy) { type=GENESTEALER; actionPts=copy.actionPts; } Genestealer::~Genestealer (void) {} Object* Genestealer::getCopy() const { return new Genestealer(*this); } bool Genestealer::select(void) { if (currentTurn == GENESTEALER_TURN && actionPts != 0) { selected = true; hasDoneSomething=false; return true; } return false; } void Genestealer::deselect(void) { selected=false; lastActionPts = actionPts; if (hasDoneSomething) actionPts = 0; } bool Genestealer::TurnOrientation(Direction dir) { RelativeDirection rel=CardinalToRelativeDirection(Dir2FullDir(dir)); int actPtsToDel=-1; switch (rel) { case SIDE: if (history==TURN90) actPtsToDel=1; else actPtsToDel=0; break; case BACK: actPtsToDel=1; break; default: actPtsToDel=-1; break; } if (actPtsToDel==-1 || actionPts < actPtsToDel) return false; actionPts-=actPtsToDel; hasDoneSomething=true; orientation = dir; if (rel==SIDE && actPtsToDel==1) history = NONE; else history = TURN90; return true; } bool Genestealer::Move(FullDirection dir) { if (board->isEntryZoneCase(position)) { guicmd->Write("You can't \"move\" when your unit is on an entry zone.\n" "You must use the button \"enter the board\" first."); return false; } RelativeDirection rel=CardinalToRelativeDirection(dir); int actPtsToDel=0; switch(rel) { case FRONT: actPtsToDel=1; break; case BACK: actPtsToDel=2; break; case SIDE: actPtsToDel=1; break; } if (actPtsToDel==0 || actionPts < actPtsToDel) return false; if (RealCreature::Move(dir)) { sound->playSound(SoundSystem::GenestealerMove); hasDoneSomething=true; actionPts-=actPtsToDel; history=NONE; return true; } return false; } bool Genestealer::Enter(void) { if (actionPts>1 && Creature::GenestealerEnter()) { actionPts--; return true; } return false; } bool Genestealer::OpenDoor(int cId) { if (board->isEntryZoneCase(position)) return false; if (actionPts < 1) return false; bool res = RealCreature::OpenDoor(cId); if (res) { hasDoneSomething=true; actionPts--; history = NONE; return true; } return false; } bool Genestealer::CloseDoor(int cId) { if (board->isEntryZoneCase(position)) return false; if (actionPts < 1) return false; bool res = RealCreature::CloseDoor(cId); if (res) { hasDoneSomething=true; actionPts--; history = NONE; return true; } return false; } void Genestealer::beginNewTurn(Turn turn) { if (turn == GENESTEALER_TURN) { actionPts = 6; } history = NONE; Creature::beginNewTurn(turn); } bool Genestealer::isAttackable(void) {return true;} bool Genestealer::isShootable(void) {return true;} int Genestealer::ThrowDice(bool reThrow, int value) { QString str; QTextStream msg(&str,IO_WriteOnly); int val1, val2; // First dice val1 = 1+Rand::roll(6); // Second dice int currVal = 1+Rand::roll(6); if (currVal > val1) { val2 = val1; val1 = currVal; } else val2 = currVal; // Third dice currVal = 1+Rand::roll(6); if (currVal > val1) { val2 = val1; val1 = currVal; } else if (currVal > val2) val2 = currVal; if (reThrow && (val1>value || (val1==value && value>=3))) { // Fourth dice which replace val1. currVal = 1+Rand::roll(6); msg << "Genestealer attack, best 2 dices : " << val1 << " " << val2 << ", rethrow the " << val1 << " and get a " << currVal; guicrea->Message(str); return (currVal > val2) ? currVal : val2; } msg << "Genestealer attack, best 2 dices : " << val1 << " " << val2; guicrea->Message(str); return val1; } AttackRes Genestealer::Attack(void) { AttackRes res = RealCreature::Attack(); if (res.attackEnable) { actionPts--; hasDoneSomething=true; history = NONE; } return res; } void Genestealer::printInfo(int loc) {guicrea->printInfo(this,selected,loc);} char Genestealer::getChar(void) {return 'g';} void *Genestealer::getPixmap(bool big) {return guicrea->getPixmap(this,big);} bool Genestealer::isMarinePlayer(void) {return false;} bool Genestealer::isFireable(void) {return true;} bool Genestealer::isSelectable (void) { return (currentTurn==GENESTEALER_TURN && actionPts!=0); } Marine::Marine (GUICreature *x, GUICommandLine *c, int p, int i, Direction ori, int *cmd) : RealCreature(x, c, p, i, ori), cmdPts(cmd), previousTargetCase(-1) {} Marine::Marine(const Marine& copy) : RealCreature(copy) { cmdPts = copy.cmdPts; previousTargetCase = copy.previousTargetCase; } Marine::~Marine (void) {} bool Marine::isMarinePlayer(void) {return true;} bool Marine::isSelectable (void) { return (currentTurn==MARINE_TURN && actionPts+*cmdPts != 0); } int Marine::EndTurnActionPts() { return lastActionPts + *cmdPts; } bool Marine::select(void) { if (currentTurn == MARINE_TURN) { actionPts += *cmdPts; selected = true; hasDoneSomething = false; return true; } return false; } void Marine::deselect(void) { selected=false; lastActionPts = actionPts - *cmdPts; if (hasDoneSomething) actionPts = 0; else actionPts = actionPts - *cmdPts; } bool Marine::TurnOrientation(Direction dir) { RelativeDirection rel=CardinalToRelativeDirection(Dir2FullDir(dir)); int actPtsToDel=-1; switch (rel) { case SIDE: actPtsToDel=1; break; case BACK: actPtsToDel=2; break; case FRONT: actPtsToDel=-1; break; } if (actPtsToDel==-1 || actionPts < actPtsToDel) return false; hasDoneSomething=true; actionPts-=actPtsToDel; CheckCmdPts(); orientation = dir; if (rel==SIDE) history = TURN90; else history = TURN180; return true; } bool Marine::Move(FullDirection dir) { RelativeDirection rel=CardinalToRelativeDirection(dir); int actPtsToDel=0; switch(rel) { case FRONT: actPtsToDel=1; break; case BACK: actPtsToDel=2; break; case SIDE: actPtsToDel=0; guicmd->Write("Marines can't move sideways."); break; } if (actPtsToDel==0 || actionPts < actPtsToDel) return false; if (RealCreature::Move(dir)) { sound->playSound(SoundSystem::MarineMove); hasDoneSomething=true; actionPts-=actPtsToDel; CheckCmdPts(); if (rel==FRONT) history = FORWARD; else if (rel==BACK) history = BACKWARD; else history = NONE; return true; } return false; } bool Marine::OpenDoor(int cId) { if (actionPts < 1) return false; bool res = RealCreature::OpenDoor(cId); if (res) { hasDoneSomething=true; actionPts--; CheckCmdPts(); history = NONE; return true; } return false; } bool Marine::CloseDoor(int cId) { if (actionPts < 1) return false; bool res = RealCreature::CloseDoor(cId); if (res) { hasDoneSomething=true; actionPts--; CheckCmdPts(); history = NONE; return true; } return false; } void Marine::beginNewTurn(Turn turn) { if (turn == MARINE_TURN) { actionPts = 4; history = NONE; } Creature::beginNewTurn(turn); } void Marine::CheckCmdPts(void) { if (actionPts - *cmdPts < 0) *cmdPts = actionPts; } bool Marine::takeObject(void) { // Check if there is an object dropped in the case set *objs = board->getObject(position); if (objs) for (set::iterator ite=objs->begin(); ite!=objs->end(); ite++) if ((*ite)->canBeTaken() && (*ite)->canBeTaken()<=actionPts) { hasDoneSomething=true; actionPts -= (*ite)->canBeTaken(); CheckCmdPts(); (*ite)->hasBeenTaken(); taken = *ite; history=NONE; return true; } // Check if an adjacent marine has taken an object and transfer it if any. set adj; getNeighbors(&adj); for (set::iterator it=adj.begin(); it!=adj.end(); it++) { objs = board->getObject(*it); for (set::iterator it1=objs->begin(); it1!=objs->end(); it1++) if ((*it1)->isMarinePlayer() && ((Creature *) *it1)->hasAnObject()) { taken = ((Creature *) *it1)->transferObject(getPosition()); if (actionPts >= taken->canBeTransfered()) { actionPts -= taken->canBeTransfered(); CheckCmdPts(); history=NONE; int val = 1+Rand::roll(6); if (val>1) { taken->hasBeenTaken(); return true; } else guicrea->Message("Object has fallen during transfer from another marine."); } } } return false; } void Marine::automaticTakeObject(Object *obj) { taken=obj; obj->hasBeenTaken(); } Object * Marine::transferObject(int newpos) { Object *obj = taken; if (obj) { obj->hasBeenFree(); board->move(obj,obj->getPosition(),newpos); obj->setPosition(newpos); hasDoneSomething=true; } taken=0; return obj; } bool Marine::dropTakenObject(bool fromDead) { if (taken) { if (fromDead) { taken->hasBeenFree(); int val = 1+Rand::roll(6); if (val==1) man->deleteCreature(taken); taken=0; return true; } else if (taken->canBeDropped()<=actionPts) { taken->hasBeenFree(); actionPts -= taken->canBeDropped(); CheckCmdPts(); history = NONE; taken=0; return true; } } return false; } bool Marine::isAttackable(void) {return currentTurn==GENESTEALER_TURN;} bool Marine::isShootable(void) {return false;} int Marine::ThrowDice(bool reThrow, int value) { // You can not be attack by a sergeant, so no need to reThrow. int val = 1+Rand::roll(6); QString msg; msg.sprintf("Marine attack, dice %d",val); guicrea->Message(msg); return val; } AttackRes Marine::Attack(void) { AttackRes res = RealCreature::Attack(); if (res.attackEnable) { hasDoneSomething=true; actionPts--; CheckCmdPts(); history = NONE; } return res; } void Marine::isAttacked(void) {history=NONE;} BolterMarine::BolterMarine(GUICreature *x,GUICommandLine *guicmd, int p,int i, Direction ori, int *cmd) : Marine(x, guicmd, p, i, ori, cmd), jammed(false) { type = BOLTERMARINE; } BolterMarine::BolterMarine(const BolterMarine& copy) : Marine(copy) { jammed = copy.jammed; } BolterMarine::~BolterMarine() {} Object* BolterMarine::getCopy() const { return new BolterMarine(*this); } AttackRes BolterMarine::Shoot(int caseId) { if (jammed || board->isEntryZoneCase(caseId)) return AttackRes(false,-1,0); if (actionPts < 1 && history != TURN90 && history != TURN180 && history != FORWARD && history != BACKWARD) return AttackRes(false,-1,0); set *targets = board->getObject(caseId); if (targets) for(set::iterator it=targets->begin();it!=targets->end();it++) { if (canShootAt((*it)->getPosition()) && NoObstacleTo((*it)->getPosition()) && (*it)->isShootable()) { if (history != TURN90 && history != TURN180 && history != FORWARD && history != BACKWARD) { hasDoneSomething=true; actionPts--; CheckCmdPts(); } int res = ThrowShootDice(caseId); previousTargetCase = caseId; history = SHOOT; return AttackRes(true, res, *it); } } return AttackRes(false, -1, 0); } bool BolterMarine::OverwatchShoot(int caseId) { if (history==OVERWATCH && !jammed && canShootAt(caseId) && NoObstacleTo(caseId) && board->Distance(position, caseId)<=12) { int shootdice = ThrowShootDice(caseId); if (ThrowJammedDice()) { guicrea->Message("Your marine has jammed his weapon."); sound->playSound(SoundSystem::MarineJam); jammed=true; history=NONE; } if (shootdice>0) { guicrea->Message("Overwatch fire is successful."); sound->playSound(SoundSystem::MarineShoot); return true; } guicrea->Message("Overwatch fire has failed."); } return false; } int BolterMarine::ThrowShootDice (int caseId) { int val1 = 1+Rand::roll(6); int val2 = 1+Rand::roll(6); QString msg; if (history == SHOOT && previousTargetCase == caseId) { msg.sprintf("Marine shoot, dice (%d+1)-(%d+1)",val1,val2); val1++; val2++; } else msg.sprintf("Marine shoot, dice %d-%d", val1, val2); guicrea->Message(msg); if (val1 > 5 && val2 > 5) return 2; else if (val1 > 5 || val2 > 5) return 1; else return 0; } bool BolterMarine::ThrowJammedDice (void) { int val = 1+Rand::roll(6); if (val==6) { sound->playSound(SoundSystem::MarineJam); return true; } return false; } bool BolterMarine::Overwatch (void) { if (actionPts < 2) return false; hasDoneSomething=true; actionPts-=2; CheckCmdPts(); history = OVERWATCH; return true; } bool BolterMarine::Unjam (void) { if (actionPts < 1 || !jammed) return false; sound->playSound(SoundSystem::MarineUnjam); hasDoneSomething=true; actionPts -= 1; CheckCmdPts(); history=NONE; jammed=false; return true; } void BolterMarine::printInfo(int loc) { guicrea->printInfo(this,selected,loc);} char BolterMarine::getChar(void) { return 'm'; } void *BolterMarine::getPixmap(bool big) {return guicrea->getPixmap(this,big);} bool BolterMarine::onOverwatch(void) {return (history==OVERWATCH);} bool BolterMarine::isJammed(void) {return jammed;} Sergeant::Sergeant (GUICreature *x, GUICommandLine *guicmd, int p, int i, Direction ori, int *cmd) : BolterMarine(x, guicmd, p, i, ori, cmd) {type=SERGEANT;} Sergeant::Sergeant(const Sergeant& copy) : BolterMarine(copy) { } Sergeant::~Sergeant (void) {} Object* Sergeant::getCopy() const { return new Sergeant(*this); } int Sergeant::ThrowDice(bool reThrow, int value) { // You can not be attack by another sergeant, so no need to reThrow. int val = 2+Rand::roll(6); QString msg; msg.sprintf("Sergeant attack, dice %d",val); guicrea->Message(msg); return val; } void Sergeant::printInfo(int loc) { guicrea->printInfo(this,selected,loc);} char Sergeant::getChar(void) { return 's'; } void *Sergeant::getPixmap(bool big) {return guicrea->getPixmap(this,big);} FlamerMarine::FlamerMarine (GUICreature *x,GUICommandLine *guicmd,int p,int i, Direction ori, int *cmd, int ch) : Marine(x, guicmd, p, i, ori, cmd), ammunition(12), charger(ch) { type=FLAMER; } FlamerMarine::FlamerMarine(const FlamerMarine& copy) : Marine(copy) { ammunition = copy.ammunition; charger = copy.charger; } FlamerMarine::~FlamerMarine (void) {} Object* FlamerMarine::getCopy() const { return new FlamerMarine(*this); } void FlamerMarine::printInfo(int loc) { guicrea->printInfo(this,selected,loc);} char FlamerMarine::getChar(void) { return 'f'; } void *FlamerMarine::getPixmap(bool big) {return guicrea->getPixmap(this,big);} bool FlamerMarine::Fire(int caseId) { int ptsUsed=0; // Need to know if this is an new firing or continuation of the previous one. if (history==FIRE && board->areNeighbors(currentFiring.back()->getPosition(), caseId)) { ptsUsed = 0; for (list::iterator it=currentFiring.begin(); it!=currentFiring.end(); it++) (*it)->setTransparency(true); } else ptsUsed = 2; bool res=false; set *targets = board->getObject(caseId); if (targets) if (actionPts >= ptsUsed && ammunition!=0 && canShootAt(caseId) && NoObstacleTo(caseId) && board->Distance(position, caseId)<=12 && targets) { bool ok=true; for (set::iterator it=targets->begin(); it!=targets->end(); it++) if (!(*it)->isFireable()) ok=false; if (ok) { hasDoneSomething=true; actionPts-=ptsUsed; CheckCmdPts(); // If this is a new firing, need to clear the previous one. (can't do it // before because we do it only if fire is successful). if (ptsUsed==2) currentFiring.clear(); for (set::iterator it=targets->begin();it!=targets->end();it++) if ((*it)->Type()==GENESTEALER) { if (ThrowFireDice()) { guicrea->Message("Genestealer dies in the flame."); man->informKilled(GENESTEALER, caseId); man->deleteCreature(*it); man->CheckNonVisibleBlip(); break; } else guicrea->Message("Genestealer escapes the flame."); } currentFiring.push_back(man->addFlame(caseId)); history = FIRE; ammunition--; res = true; } } for (list::iterator it=currentFiring.begin(); it!=currentFiring.end(); it++) (*it)->setTransparency(false); return res; } bool FlamerMarine::ThrowFireDice (void) { int val = 1+Rand::roll(6); if (val<6) return true; return false; } bool FlamerMarine::Reload (void) { if (actionPts < 4 || !charger) return false; hasDoneSomething=true; actionPts-=4; CheckCmdPts(); history=NONE; charger--; ammunition=12; return true; } int FlamerMarine::getAllAmmunition(void) { return ammunition+12*charger; }