| [1505] | 1 | /* | 
|---|
|  | 2 | *   ORXONOX - the hottest 3D action shooter ever to exist | 
|---|
|  | 3 | *                    > www.orxonox.net < | 
|---|
|  | 4 | * | 
|---|
|  | 5 | * | 
|---|
|  | 6 | *   License notice: | 
|---|
|  | 7 | * | 
|---|
|  | 8 | *   This program is free software; you can redistribute it and/or | 
|---|
|  | 9 | *   modify it under the terms of the GNU General Public License | 
|---|
|  | 10 | *   as published by the Free Software Foundation; either version 2 | 
|---|
|  | 11 | *   of the License, or (at your option) any later version. | 
|---|
|  | 12 | * | 
|---|
|  | 13 | *   This program is distributed in the hope that it will be useful, | 
|---|
|  | 14 | *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
|  | 15 | *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
|  | 16 | *   GNU General Public License for more details. | 
|---|
|  | 17 | * | 
|---|
|  | 18 | *   You should have received a copy of the GNU General Public License | 
|---|
|  | 19 | *   along with this program; if not, write to the Free Software | 
|---|
|  | 20 | *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | 
|---|
|  | 21 | * | 
|---|
|  | 22 | *   Author: | 
|---|
|  | 23 | *      Fabian 'x3n' Landau | 
|---|
|  | 24 | *   Co-authors: | 
|---|
|  | 25 | *      ... | 
|---|
|  | 26 | * | 
|---|
|  | 27 | */ | 
|---|
|  | 28 |  | 
|---|
| [1480] | 29 | #include "OrxonoxStableHeaders.h" | 
|---|
| [1505] | 30 | #include "SpaceShipAI.h" | 
|---|
|  | 31 |  | 
|---|
|  | 32 | #include <OgreMath.h> | 
|---|
|  | 33 | #include "Projectile.h" | 
|---|
| [1552] | 34 | #include "ParticleSpawner.h" | 
|---|
| [1505] | 35 | #include "core/CoreIncludes.h" | 
|---|
|  | 36 | #include "core/Iterator.h" | 
|---|
|  | 37 | #include "core/Executor.h" | 
|---|
|  | 38 | #include "core/ConsoleCommand.h" | 
|---|
|  | 39 | #include "core/XMLPort.h" | 
|---|
| [1553] | 40 | #include "tools/ParticleInterface.h" | 
|---|
| [1505] | 41 |  | 
|---|
|  | 42 | #define ACTION_INTERVAL 1.0f | 
|---|
|  | 43 |  | 
|---|
|  | 44 | namespace orxonox | 
|---|
|  | 45 | { | 
|---|
|  | 46 | SetConsoleCommand(SpaceShipAI, createEnemy, true).setDefaultValue(0, 1); | 
|---|
|  | 47 | SetConsoleCommand(SpaceShipAI, killEnemies, true).setDefaultValue(0, 0); | 
|---|
|  | 48 |  | 
|---|
|  | 49 | CreateFactory(SpaceShipAI); | 
|---|
|  | 50 |  | 
|---|
|  | 51 | SpaceShipAI::SpaceShipAI() | 
|---|
|  | 52 | { | 
|---|
|  | 53 | RegisterObject(SpaceShipAI); | 
|---|
|  | 54 |  | 
|---|
|  | 55 | this->target_ = 0; | 
|---|
|  | 56 | this->bShooting_ = 0; | 
|---|
|  | 57 | this->bHasTargetPosition_ = false; | 
|---|
|  | 58 |  | 
|---|
|  | 59 | this->setTeamNr((int)rnd(NUM_AI_TEAMS) % NUM_AI_TEAMS + 1); | 
|---|
|  | 60 |  | 
|---|
|  | 61 | if (NUM_AI_TEAMS > 0) | 
|---|
|  | 62 | this->teamColours_[1] = ColourValue(1, 0, 0, 1); | 
|---|
|  | 63 | if (NUM_AI_TEAMS > 1) | 
|---|
|  | 64 | this->teamColours_[2] = ColourValue(0, 1, 0, 1); | 
|---|
|  | 65 | if (NUM_AI_TEAMS > 2) | 
|---|
|  | 66 | this->teamColours_[3] = ColourValue(0, 0, 1, 1); | 
|---|
|  | 67 |  | 
|---|
|  | 68 | for (int i = 4; i <= NUM_AI_TEAMS; ++i) | 
|---|
|  | 69 | this->teamColours_[i] = ColourValue(rnd(), rnd(), rnd(), 1); | 
|---|
|  | 70 | } | 
|---|
|  | 71 |  | 
|---|
| [1552] | 72 | SpaceShipAI::~SpaceShipAI() | 
|---|
|  | 73 | { | 
|---|
|  | 74 | for (Iterator<SpaceShipAI> it = ObjectList<SpaceShipAI>::begin(); it; ++it) | 
|---|
|  | 75 | it->shipDied(this); | 
|---|
|  | 76 | } | 
|---|
|  | 77 |  | 
|---|
| [1505] | 78 | void SpaceShipAI::XMLPort(Element& xmlelement, XMLPort::Mode mode) | 
|---|
|  | 79 | { | 
|---|
|  | 80 | SpaceShip::XMLPort(xmlelement, mode); | 
|---|
|  | 81 |  | 
|---|
|  | 82 | this->actionTimer_.setTimer(ACTION_INTERVAL, true, this, createExecutor(createFunctor(&SpaceShipAI::action))); | 
|---|
|  | 83 | } | 
|---|
|  | 84 |  | 
|---|
|  | 85 | void SpaceShipAI::createEnemy(int num) | 
|---|
|  | 86 | { | 
|---|
|  | 87 | for (int i = 0; i < num; ++i) | 
|---|
|  | 88 | { | 
|---|
|  | 89 | SpaceShipAI* newenemy = new SpaceShipAI(); | 
|---|
|  | 90 | newenemy->setMesh("assff.mesh"); | 
|---|
|  | 91 | //            newenemy->setPosition(0, 0, 0); | 
|---|
| [1552] | 92 | newenemy->setPosition(Vector3(rnd(-3000, 3000), rnd(-3000, 3000), rnd(-3000, 3000))); | 
|---|
| [1505] | 93 | newenemy->setScale(10); | 
|---|
|  | 94 | newenemy->setMaxSpeed(500); | 
|---|
|  | 95 | newenemy->setMaxSideAndBackSpeed(50); | 
|---|
|  | 96 | newenemy->setMaxRotation(1.0); | 
|---|
|  | 97 | newenemy->setTransAcc(200); | 
|---|
|  | 98 | newenemy->setRotAcc(3.0); | 
|---|
|  | 99 | newenemy->setTransDamp(75); | 
|---|
|  | 100 | newenemy->setRotDamp(1.0); | 
|---|
|  | 101 | Element xmlelement; | 
|---|
|  | 102 | newenemy->XMLPort(xmlelement, XMLPort::LoadObject); | 
|---|
| [1552] | 103 |  | 
|---|
| [1608] | 104 | ParticleSpawner* spawneffect = new ParticleSpawner("Orxonox/fairytwirl", LODParticle::normal, 2.0, 0, 0, newenemy->getOrth()); | 
|---|
| [1552] | 105 | spawneffect->setPosition(newenemy->getPosition() - newenemy->getOrth() * 50); | 
|---|
|  | 106 | spawneffect->create(); | 
|---|
| [1505] | 107 | } | 
|---|
|  | 108 | } | 
|---|
|  | 109 |  | 
|---|
|  | 110 | void SpaceShipAI::killEnemies(int num) | 
|---|
|  | 111 | { | 
|---|
|  | 112 | int i = 0; | 
|---|
|  | 113 | for (Iterator<SpaceShipAI> it = ObjectList<SpaceShipAI>::begin(); it; ) | 
|---|
|  | 114 | { | 
|---|
| [1552] | 115 | (it++)->kill(); | 
|---|
| [1505] | 116 | if (num && i >= num) | 
|---|
|  | 117 | break; | 
|---|
|  | 118 | } | 
|---|
|  | 119 | } | 
|---|
|  | 120 |  | 
|---|
|  | 121 | ColourValue SpaceShipAI::getProjectileColour() const | 
|---|
|  | 122 | { | 
|---|
|  | 123 | return this->teamColours_[this->getTeamNr()]; | 
|---|
|  | 124 | } | 
|---|
|  | 125 |  | 
|---|
|  | 126 | void SpaceShipAI::action() | 
|---|
|  | 127 | { | 
|---|
|  | 128 | float random; | 
|---|
|  | 129 | float maxrand = 100.0f / ACTION_INTERVAL; | 
|---|
|  | 130 |  | 
|---|
|  | 131 | // search enemy | 
|---|
|  | 132 | random = rnd(maxrand); | 
|---|
| [1608] | 133 | if (random < 15 && (!this->target_)) | 
|---|
| [1505] | 134 | this->searchNewTarget(); | 
|---|
|  | 135 |  | 
|---|
|  | 136 | // forget enemy | 
|---|
|  | 137 | random = rnd(maxrand); | 
|---|
|  | 138 | if (random < 5 && (this->target_)) | 
|---|
|  | 139 | this->forgetTarget(); | 
|---|
|  | 140 |  | 
|---|
|  | 141 | // next enemy | 
|---|
|  | 142 | random = rnd(maxrand); | 
|---|
|  | 143 | if (random < 10 && (this->target_)) | 
|---|
|  | 144 | this->searchNewTarget(); | 
|---|
|  | 145 |  | 
|---|
|  | 146 | // fly somewhere | 
|---|
|  | 147 | random = rnd(maxrand); | 
|---|
| [1608] | 148 | if (random < 50 && (!this->bHasTargetPosition_ && !this->target_)) | 
|---|
| [1505] | 149 | this->searchNewTargetPosition(); | 
|---|
|  | 150 |  | 
|---|
|  | 151 | // stop flying | 
|---|
|  | 152 | random = rnd(maxrand); | 
|---|
|  | 153 | if (random < 10 && (this->bHasTargetPosition_ && !this->target_)) | 
|---|
|  | 154 | this->bHasTargetPosition_ = false; | 
|---|
|  | 155 |  | 
|---|
|  | 156 | // fly somewhere else | 
|---|
|  | 157 | random = rnd(maxrand); | 
|---|
|  | 158 | if (random < 30 && (this->bHasTargetPosition_ && !this->target_)) | 
|---|
|  | 159 | this->searchNewTargetPosition(); | 
|---|
|  | 160 |  | 
|---|
|  | 161 | // shoot | 
|---|
|  | 162 | random = rnd(maxrand); | 
|---|
|  | 163 | if (random < 75 && (this->target_ && !this->bShooting_)) | 
|---|
|  | 164 | this->bShooting_ = true; | 
|---|
|  | 165 |  | 
|---|
|  | 166 | // stop shooting | 
|---|
|  | 167 | random = rnd(maxrand); | 
|---|
|  | 168 | if (random < 25 && (this->bShooting_)) | 
|---|
| [1552] | 169 | this->bShooting_ = false; | 
|---|
|  | 170 | } | 
|---|
|  | 171 |  | 
|---|
|  | 172 | void SpaceShipAI::damage(float damage) | 
|---|
|  | 173 | { | 
|---|
|  | 174 | this->health_ -= damage; | 
|---|
|  | 175 | if (this->health_ <= 0) | 
|---|
| [1505] | 176 | { | 
|---|
| [1552] | 177 | this->kill(); | 
|---|
|  | 178 | SpaceShipAI::createEnemy(1); | 
|---|
| [1505] | 179 | } | 
|---|
|  | 180 | } | 
|---|
|  | 181 |  | 
|---|
| [1552] | 182 | void SpaceShipAI::kill() | 
|---|
|  | 183 | { | 
|---|
| [1563] | 184 | ParticleSpawner* explosion = new ParticleSpawner("Orxonox/BigExplosion1part1", LODParticle::low, 3.0); | 
|---|
| [1552] | 185 | explosion->setPosition(this->getPosition()); | 
|---|
|  | 186 | explosion->getParticleInterface()->setKeepParticlesInLocalSpace(true); | 
|---|
|  | 187 | explosion->setScale(4); | 
|---|
|  | 188 | explosion->create(); | 
|---|
|  | 189 |  | 
|---|
| [1563] | 190 | explosion = new ParticleSpawner("Orxonox/BigExplosion1part2", LODParticle::normal, 3.0); | 
|---|
| [1552] | 191 | explosion->setPosition(this->getPosition()); | 
|---|
|  | 192 | explosion->getParticleInterface()->setKeepParticlesInLocalSpace(true); | 
|---|
|  | 193 | explosion->setScale(4); | 
|---|
|  | 194 | explosion->create(); | 
|---|
| [1563] | 195 | explosion = new ParticleSpawner("Orxonox/BigExplosion1part2", LODParticle::high, 3.0); | 
|---|
|  | 196 | explosion->setPosition(this->getPosition()); | 
|---|
|  | 197 | explosion->getParticleInterface()->setKeepParticlesInLocalSpace(true); | 
|---|
|  | 198 | explosion->setScale(4); | 
|---|
|  | 199 | explosion->create(); | 
|---|
| [1552] | 200 |  | 
|---|
|  | 201 | Vector3 ringdirection = Vector3(rnd(), rnd(), rnd()); | 
|---|
|  | 202 | ringdirection.normalise(); | 
|---|
| [1608] | 203 | explosion = new ParticleSpawner("Orxonox/BigExplosion1part3", LODParticle::normal, 3.0, 0.5, 0, ringdirection); | 
|---|
| [1552] | 204 | explosion->setPosition(this->getPosition()); | 
|---|
|  | 205 | explosion->getParticleInterface()->setKeepParticlesInLocalSpace(true); | 
|---|
|  | 206 | explosion->setScale(4); | 
|---|
|  | 207 | explosion->create(); | 
|---|
| [1608] | 208 | explosion = new ParticleSpawner("Orxonox/BigExplosion1part3", LODParticle::high, 3.0, 0.5, 0, ringdirection); | 
|---|
| [1563] | 209 | explosion->setPosition(this->getPosition()); | 
|---|
|  | 210 | explosion->getParticleInterface()->setKeepParticlesInLocalSpace(true); | 
|---|
|  | 211 | explosion->setScale(4); | 
|---|
|  | 212 | explosion->create(); | 
|---|
| [1552] | 213 |  | 
|---|
|  | 214 | delete this; | 
|---|
|  | 215 | } | 
|---|
|  | 216 |  | 
|---|
| [1505] | 217 | void SpaceShipAI::tick(float dt) | 
|---|
|  | 218 | { | 
|---|
| [1558] | 219 | if (!this->isActive()) | 
|---|
|  | 220 | return; | 
|---|
|  | 221 |  | 
|---|
| [1505] | 222 | if (this->target_) | 
|---|
|  | 223 | this->aimAtTarget(); | 
|---|
|  | 224 |  | 
|---|
|  | 225 | if (this->bHasTargetPosition_) | 
|---|
|  | 226 | this->moveToTargetPosition(dt); | 
|---|
|  | 227 |  | 
|---|
| [1564] | 228 | if (this->bShooting_ && this->isCloseAtTarget(2500) && this->isLookingAtTarget(Ogre::Math::PI / 20.0)) | 
|---|
| [1505] | 229 | this->doFire(); | 
|---|
|  | 230 |  | 
|---|
|  | 231 | SpaceShip::tick(dt); | 
|---|
|  | 232 | } | 
|---|
|  | 233 |  | 
|---|
|  | 234 | void SpaceShipAI::moveToTargetPosition(float dt) | 
|---|
|  | 235 | { | 
|---|
| [1564] | 236 | Vector2 coord = get2DViewdirection(this->getPosition(), this->getDir(), this->getOrth(), this->targetPosition_); | 
|---|
| [1505] | 237 |  | 
|---|
| [1608] | 238 | float distance = (this->targetPosition_ - this->getPosition()).length(); | 
|---|
|  | 239 | if (this->target_ || distance > 50) | 
|---|
| [1505] | 240 | { | 
|---|
| [1608] | 241 | // Multiply with 0.8 to make them a bit slower | 
|---|
|  | 242 | this->setMoveYaw(0.8 * sgn(coord.x) * coord.x*coord.x); | 
|---|
|  | 243 | this->setMovePitch(0.8 * sgn(coord.y) * coord.y*coord.y); | 
|---|
| [1505] | 244 | } | 
|---|
| [1608] | 245 |  | 
|---|
|  | 246 | if (this->target_ && distance < 1000 && this->getVelocity().squaredLength() > this->target_->getVelocity().squaredLength()) | 
|---|
|  | 247 | this->setMoveLongitudinal(-0.5); // They don't brake with full power to give the player a chance | 
|---|
|  | 248 | else if (!this->target_ && distance <= this->getVelocity().length() / (2 * this->getTransAcc())) | 
|---|
|  | 249 | this->setMoveLongitudinal(-1.0); | 
|---|
|  | 250 | else | 
|---|
|  | 251 | this->setMoveLongitudinal(0.8); | 
|---|
| [1505] | 252 | } | 
|---|
|  | 253 |  | 
|---|
|  | 254 | void SpaceShipAI::searchNewTargetPosition() | 
|---|
|  | 255 | { | 
|---|
|  | 256 | this->targetPosition_ = Vector3(rnd(-5000,5000), rnd(-5000,5000), rnd(-5000,5000)); | 
|---|
|  | 257 | this->bHasTargetPosition_ = true; | 
|---|
|  | 258 | } | 
|---|
|  | 259 |  | 
|---|
|  | 260 | void SpaceShipAI::searchNewTarget() | 
|---|
|  | 261 | { | 
|---|
|  | 262 | this->targetPosition_ = this->getPosition(); | 
|---|
|  | 263 | this->forgetTarget(); | 
|---|
|  | 264 |  | 
|---|
|  | 265 | for (Iterator<SpaceShip> it = ObjectList<SpaceShip>::begin(); it; ++it) | 
|---|
|  | 266 | { | 
|---|
|  | 267 | if (it->getTeamNr() != this->getTeamNr()) | 
|---|
|  | 268 | { | 
|---|
|  | 269 | float speed = this->getVelocity().length(); | 
|---|
|  | 270 | Vector3 distanceCurrent = this->targetPosition_ - this->getPosition(); | 
|---|
|  | 271 | Vector3 distanceNew = it->getPosition() - this->getPosition(); | 
|---|
|  | 272 | if (!this->target_ || it->getPosition().squaredDistance(this->getPosition()) * (1.5f + acos((this->getOrientation() * Ogre::Vector3::UNIT_X).dotProduct(distanceNew) / speed / distanceNew.length()) / (2 * Ogre::Math::PI)) | 
|---|
| [1552] | 273 | < this->targetPosition_.squaredDistance(this->getPosition()) * (1.5f + acos((this->getOrientation() * Ogre::Vector3::UNIT_X).dotProduct(distanceCurrent) / speed / distanceCurrent.length()) / (2 * Ogre::Math::PI)) + rnd(-250, 250)) | 
|---|
| [1505] | 274 | { | 
|---|
|  | 275 | this->target_ = (*it); | 
|---|
|  | 276 | this->targetPosition_ = it->getPosition(); | 
|---|
|  | 277 | } | 
|---|
|  | 278 | } | 
|---|
|  | 279 | } | 
|---|
| [1552] | 280 | } | 
|---|
| [1505] | 281 |  | 
|---|
|  | 282 | void SpaceShipAI::forgetTarget() | 
|---|
|  | 283 | { | 
|---|
|  | 284 | this->target_ = 0; | 
|---|
|  | 285 | this->bShooting_ = false; | 
|---|
|  | 286 | } | 
|---|
|  | 287 |  | 
|---|
|  | 288 | void SpaceShipAI::aimAtTarget() | 
|---|
|  | 289 | { | 
|---|
|  | 290 | if (!this->target_) | 
|---|
|  | 291 | return; | 
|---|
| [1552] | 292 |  | 
|---|
| [1566] | 293 | this->targetPosition_ = getPredictedPosition(this->getPosition(), Projectile::getSpeed(), this->target_->getPosition(), this->target_->getOrientation() * this->target_->getVelocity()); | 
|---|
|  | 294 | this->bHasTargetPosition_ = (this->targetPosition_ != Vector3::ZERO); | 
|---|
| [1505] | 295 | } | 
|---|
|  | 296 |  | 
|---|
|  | 297 | bool SpaceShipAI::isCloseAtTarget(float distance) | 
|---|
|  | 298 | { | 
|---|
| [1552] | 299 | if (!this->target_) | 
|---|
|  | 300 | return (this->getPosition().squaredDistance(this->targetPosition_) < distance*distance); | 
|---|
|  | 301 | else | 
|---|
|  | 302 | return (this->getPosition().squaredDistance(this->target_->getPosition()) < distance*distance); | 
|---|
| [1505] | 303 | } | 
|---|
|  | 304 |  | 
|---|
|  | 305 | bool SpaceShipAI::isLookingAtTarget(float angle) | 
|---|
|  | 306 | { | 
|---|
| [1564] | 307 | return (getAngle(this->getPosition(), this->getDir(), this->targetPosition_) < angle); | 
|---|
| [1505] | 308 | } | 
|---|
| [1552] | 309 |  | 
|---|
|  | 310 | void SpaceShipAI::shipDied(SpaceShipAI* ship) | 
|---|
|  | 311 | { | 
|---|
| [1608] | 312 | if (ship == this->target_) | 
|---|
|  | 313 | { | 
|---|
|  | 314 | this->forgetTarget(); | 
|---|
|  | 315 | this->searchNewTargetPosition(); | 
|---|
|  | 316 | } | 
|---|
| [1552] | 317 | } | 
|---|
| [1505] | 318 | } | 
|---|