/* * ORXONOX - the hottest 3D action shooter ever to exist * > www.orxonox.net < * * * License notice: * * 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. * * Author: * * Co-authors: * ... * *NACHRICHT: * * Hier empfehle ich euch die gesamte Spielogik unter zu bringen. Viele Funktionen werden automatisch * bei gewissen Ereignissen aufgerufen bzw. lösen Ereignisse aus * *Z.B: * start() //wird aufgerufen, bevor das Spiel losgeht * end() //wenn man diese Funktion aufruft wird * pawnKilled() // wird aufgerufen, wenn ein Pawn stirbt (z.B: wenn ) * playerScored() // kann man aufrufen um dem Spieler Punkte zu vergeben. * * *TIPP: Eclipse hilft euch schnell auf bereits vorhanden Funktionen zuzugreifen: * einfach "this->" eingeben und kurz warten. Dann tauch eine Liste mit Vorschlägen auf. Wenn ihr jetzt weiter * tippt, werden die Vorschläge entsprechend gefiltert. * * *TIPP: schaut euch mal Tetris::createStone() an. Dort wird ein TetrisStone-Objekt (ControllableEntity) erzeugt, * ihm ein Template zugewiesen (welches vorher im Level definiert wurde und dem CenterPoint übergeben wurde) * Ähnlich könnt ihr vorgehen, um einen Turm zu erzeugen. (Zusätzlich braucht ein Turm noch einen Controller) * z.B: WaypointPatrolController. Wenn kein Team zugewiesen wurde bekämpft ein WaypointPatrolController alles, * was in seiner Reichweite liegt. * * *HUD: * Ein Gametype kann ein HUD (Head up Display haben.) Z.B: hat Pong eine Anzeige welcher Spieler wieviele Punkte hat. * Generell kann man a) Grafiken oder b) Zeichen in einer HUD anzeigen. * Fuer den ersten Schritt reicht reiner Text. * * a) * PongScore.cc uebernehmen und eigene Klasse draus machen. * Wenn ihr bloss anzeigen wollt wieviele Punkte der Spieler bereits erspielt hat (Punkte = Kapital fuer neue Tuerme) dann orientiert ihr euch an * TetrisScore.cc (im pCuts branch): http://www.orxonox.net/browser/code/branches/pCuts/src/modules/tetris/TetrisScore.cc * Ich habe TetrisScore lediglich dazu gebraucht, um eine Variable auf dem HUD auszugeben. Ein Objekt fuer statischen Text gibt es bereits. * * b) * Im naesten Schritt erstellt man die Vorlage fuer das HUD-Objekt: siehe /data/overlays/pongHUD * OverlayText ist eine Vorlage fuer statischen text zb: "Points Scored:". Aus mir nicht erklaerlichen Gruenden sollte man die OverlayText * Objekte immer erst nach dem PongScore anlegen. * * c) Im TowerDefense gamtype muss im Constructor noch das HUD-Template gesetzt werden. * * d) in CMakeLists.txt noch das Module includen das fuer die Overlays zustaendig ist. Siehe das gleiche File im Pong module. * * * */ #include "TowerDefense.h" #include "TowerDefenseTower.h" #include "TowerDefenseCenterpoint.h" #include "worldentities/SpawnPoint.h" #include "controllers/WaypointController.h" #include "graphics/Model.h" #include "infos/PlayerInfo.h" #include "chat/ChatManager.h" #include "core/CoreIncludes.h" #include "Highscore.h" namespace orxonox { RegisterUnloadableClass(TowerDefense); TowerDefense::TowerDefense(Context* context) : TeamDeathmatch(context) { RegisterObject(TowerDefense); selecter = nullptr; this->player_ = nullptr; this->setHUDTemplate("TowerDefenseHUD"); this->waveNumber_ = 0; this->timeSinceLastSpawn_ = 0.0; this->timeUntilNextWave_ = 0.0; this->credit_ = 0; this->lifes_ = 0; this->waveSize_ = 0; offset_ = Vector3(0,0,10); } TowerDefense::~TowerDefense() { if (this->isInitialized()) { } } void TowerDefense::setCenterpoint(TowerDefenseCenterpoint *centerpoint) { this->center_ = centerpoint; } void TowerDefense::start() { if (center_ != nullptr) // There needs to be a TowerDefenseCenterpoint, i.e. the area the game takes place. { if (selecter == nullptr) { selecter = new TowerDefenseSelecter(this->center_->getContext()); } selecter->addTemplate(center_->getSelecterTemplate()); center_->attach(selecter); } else // If no centerpoint was specified, an error is thrown and the level is exited. { orxout(internal_error) << "Jump: No Centerpoint specified." << endl; return; } enemies_.clear(); createFields(); TeamDeathmatch::start(); //set initial credits, lifes and WaveNumber this->setCredit(1000); this->setLifes(100); this->timeSinceLastSpawn_ = 0.0; this->timeUntilNextWave_ = 5.0; this->waveSize_ = 0; this->setWaveNumber(0); } // Generates a TowerDefenseEnemy. Uses Template "enemytowerdefense". Sets position at first waypoint of path. void TowerDefense::addTowerDefenseEnemy(int templatenr) { TowerDefenseEnemy* en1 = new TowerDefenseEnemy(this->center_->getContext()); switch(templatenr) { case 1 : en1->addTemplate("enemytowerdefense1"); en1->setScale(3); en1->setHealth(en1->getHealth() + this->getWaveNumber()*4); break; case 2 : en1->addTemplate("enemytowerdefense2"); en1->setScale(2); en1->setHealth(en1->getHealth() + this->getWaveNumber()*4); break; case 3 : en1->addTemplate("enemytowerdefense3"); en1->setScale(1); en1->setHealth(en1->getHealth() + this->getWaveNumber()*4); break; } en1->setTeam(2); WaypointController* controller = (WaypointController*)(en1->getXMLController()); if (controller != nullptr && waypoints_.size() > 1) { en1->setPosition(waypoints_.at(0)->getPosition() + offset_); en1->setOrientation(Vector3(0,0,10), Degree(0)); en1->setDirection(Vector3(0,1,0)); en1->lookAt(waypoints_.at(1)->getPosition() + offset_); for (TowerDefenseField* field : waypoints_) { orxonox::WeakPtr waypoint = new MovableEntity(this->center_->getContext()); waypoint->setPosition(field->getPosition() + offset_); controller->addWaypoint(waypoint); } } enemies_.push_back(en1); } void TowerDefense::end() { if (Highscore::exists()) { int score = this->getWaveNumber(); Highscore::getInstance().storeScore("Tower Defense", score, this->getPlayer()); } TeamDeathmatch::end(); ChatManager::message("Match is over! Gameover!"); } void TowerDefense::spawnPlayer(PlayerInfo* player) { assert(player); player_ = player; if (selecter->getPlayer() == nullptr) { player_->startControl(selecter); players_[player].state_ = PlayerState::Alive; } } /** @brief Get the player. @return Returns a pointer to the player. If there is no player, nullptr is returned. */ PlayerInfo* TowerDefense::getPlayer(void) const { return this->player_; } //not working yet void TowerDefense::addTower(int x,int y) { TDCoordinate* coord = new TDCoordinate(x,y); x = coord->GetX(); y = coord->GetY(); int cost = center_->getTowerCost(1); if (fields_[x][y]->isFree() == true && getCredit() >= cost) { payCredit(cost); fields_[x][y]->createTower(1); } } /*adds Tower at Position (x,y) and reduces credit and adds the point to the towermatrix. template ("towerturret") so towers have ability if the turrets */ void TowerDefense::upgradeTower(int x, int y) { TDCoordinate* coord = new TDCoordinate(x,y); x = coord->GetX(); y = coord->GetY(); int cost = center_->getTowerCost(fields_[x][y]->getUpgrade() + 1); if (fields_[x][y]->isFree() == false && fields_[x][y]->canUpgrade() == true && getCredit() >= cost) { payCredit(cost); fields_[x][y]->upgrade(); } } void TowerDefense::tick(float dt) { SUPER(TowerDefense, tick, dt); if (hasStarted() == false || player_ == nullptr) { return; } timeUntilNextWave_ -= dt; timeSinceLastSpawn_ += dt; //build/upgrade tower at selecter position if (selecter != nullptr && selecter->buildTower_ == true) { selecter->buildTower_ = false; if (getField(selecter->selectedPos_)->canUpgrade() == true) { upgradeTower(selecter->selectedPos_->GetX(), selecter->selectedPos_->GetY()); } else { addTower(selecter->selectedPos_->GetX(), selecter->selectedPos_->GetY()); } } for (std::list>::iterator it = enemies_.begin(); it != enemies_.end(); ) { if (*it == nullptr) { // the enemy was destroyed by a tower - remove it from the list enemies_.erase(it++); addCredit(1); } else { //if ships are at the end they get destroyed Vector3 ship = (*it)->getRVWorldPosition(); float distance = ship.distance(endpoint_); if(distance < 40) { (*it)->destroy(); enemies_.erase(it++); this->reduceLifes(1); if (this->getLifes() == 0) { this->end(); } } else { ++ it; } } } // Add new enemy? if (timeSinceLastSpawn_ >= 1.0 && waveSize_ > 0) { // Add new enemy timeSinceLastSpawn_ -= 1.0; -- waveSize_; addTowerDefenseEnemy(this->getWaveNumber() % 3 + 1); } else if (timeUntilNextWave_ <= 0.0 && waveSize_ <= 0) { //New wave ++ waveNumber_; timeSinceLastSpawn_ = 0.0; timeUntilNextWave_ = waveNumber_+20.0f; waveSize_ = waveNumber_+5; } } void TowerDefense::createFields() { assert(center_); TDCoordinate coord(0,0); TDCoordinate startCoord(0,0); std::string fields = center_->getFields(); int pos = 0; for (int j = 15; j >= 0; --j) { for (int i = 0; i < 16; ++i) { coord.Set(i,j); fields_[i][j] = new TowerDefenseField(center_->getContext()); fields_[i][j]->setCenterpoint(center_); center_->attach(fields_[i][j]); fields_[i][j]->setPosition(coord.get3dcoordinate()); fields_[i][j]->create(fields.at(pos), fields.at(pos+1)); pos += 2; if (fields_[i][j]->getType() == TowerDefenseFieldType::START) { startCoord.Set(i,j); waypoints_.push_back(fields_[i][j]); } } } //Place waypoints along the street for the waypoint controllers of the enemies TDCoordinate* thisCoord = &startCoord; TDCoordinate* nextCoord; while ((nextCoord = getNextStreetCoord(thisCoord)) != nullptr) { waypoints_.push_back(fields_[nextCoord->GetX()][nextCoord->GetY()]); thisCoord = nextCoord; endpoint_ = nextCoord->get3dcoordinate(); } } TDCoordinate* TowerDefense::getNextStreetCoord(TDCoordinate* thisCoord) { TowerDefenseField* thisField = fields_[thisCoord->GetX()][thisCoord->GetY()]; if (thisField->getType() != TowerDefenseFieldType::STREET && thisField->getType() != TowerDefenseFieldType::START) { return nullptr; } TDCoordinate* nextCoord = new TDCoordinate(0, 0); if (thisField->getAngle() == 0) { nextCoord->Set(thisCoord->GetX(), thisCoord->GetY() - 1); } else if (thisField->getAngle() == 1) { nextCoord->Set(thisCoord->GetX() + 1, thisCoord->GetY()); } else if (thisField->getAngle() == 2) { nextCoord->Set(thisCoord->GetX(), thisCoord->GetY() + 1); } else if (thisField->getAngle() == 3) { nextCoord->Set(thisCoord->GetX() - 1, thisCoord->GetY()); } if (thisCoord->GetX() != nextCoord->GetX() || thisCoord->GetY() != nextCoord->GetY()) { return nextCoord; } delete nextCoord; return nullptr; } }