/* 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: * Fabian 'x3n' Landau * Co-authors: * Simon Miescher * */ /* * * * An asteroid which can be destroyed. Some smaller asteroids are created and a pickup * spawns. * * * */ /* Veraenderungstagebuch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ KNACKPUNKTE: o Floating point exception o math.pi richtig einbinden. OFFEN: o Add custom pickup 'resources'. Weird template stuff -> Problems setting 'size' and other properties? setNumber of Resources. --> PickupTemplateName_ in PickupSpawner ist ein string. Wird via getBaseClassIdentifier ausgelesen, daraus wird ein Pickupable fabriziert. --> MunitionPickup erbt von Pickup erbt von collectiblePickup erbt von Pickupable ----> im MineralsPickup die isPickedUp()-Methode überschreiben? --> data_extern/images/effects: PNG's für die Pickups und GUI-Dinger. o Explosion parts o custom HUD o asteroidField.lua anpassen, mit 'Mineraliendichten-Parameter'. o Dokumentieren mit @brief? o replace ugly random stuff with math.rnd() o set collisionDamage? Just for static entities? HANDBUCH: o im Level-File includes/pickups.oxi importieren. o Bei der XML-Variante wird beim ersten Aufruf der Tick-Methode ein neuer Asteroid generiert, damit die Groesse der Collision Shape korrekt initialisiert wird. FREMDANPASSUNGEN: Pickup-Zeug: o Pickup.h: Zugriffsänderung createSpawner o PickupSpawner.h: Zugriffsrechte setPickupTemplateName() und setMaxSpawnedItems() o PickupSpawner.h: In Tick() zwei Testbedingungen eingefuegt. o Pawn.h: Attribut acceptsPickups_ inklusive get/set. ERLEGT: o Rand() geht bis zu riesigen Nummern! o Pickupgenerierung: vgl. Fremdanpassungen. o Minimalbegrenzung fuer Masse vergessen. o Dynamische Definition -> putStuff-Methode, Werte noch nicht durchgesickert. o Error-Nachricht: Einfach Argumente leer lassen. o Pickups: setMaxSpawned funktioniert nicht -> Bastelloesung: Auf 2 statt eins setzen, erstes wird wohl direkt zerstoert. o setHealth: Wird mit Max verwurstelt, war 0. o Position nicht gesetzt -> alles im Ursprung, Kollision -> fliegt davon. o nimmt zuviel Schaden. -> wird mit maxHealth verwurstelt -> einfach auch setzen. o Groessenabhaengige Collison shape: Umweg ueber zweiten Konstruktor. o Absturz beim Asteroidengenerieren: Health war auf 0 gesetzt! Wurde nur bei der xml-Variante definiert. o Asteroiden fressen Pickups: Argument in Pawn, Test darauf in Tick() von PickupSpawner. o Man kann keine Arrays der Groesse 0 initialisieren, aber auch nicht per IF 2x definieren. o i++ einfach ganz verhindern, ++i stattdessen. NOTIZEN: o SUPER entspricht ueberladen, andere Argumente der Methode. o Warnungsverhinderung anderswo: (void)pickedUp; // To avoid compiler warning. Was ist das? friend class Pickupable; */ #include "../../orxonox/worldentities/pawns/Pawn.h" #include "../../orxonox/worldentities/WorldEntity.h" #include "AsteroidMinable.h" #include #include "core/CoreIncludes.h" #include "core/GameMode.h" #include "core/XMLPort.h" #include "core/EventIncludes.h" #include "network/NetworkFunction.h" #include "util/Math.h" // #include "infos/PlayerInfo.h" // #include "controllers/Controller.h" // #include "gametypes/Gametype.h" // #include "graphics/ParticleSpawner.h" // #include "worldentities/ExplosionChunk.h" // #include "worldentities/ExplosionPart.h" // #include "core/object/ObjectListIterator.h" // #include "controllers/FormationController.h" #include "../pickup/items/HealthPickup.h" #include "../pickup/PickupSpawner.h" #include "../pickup/Pickup.h" //#include "../pickup/pickup ..... pickupable #include "../objects/collisionshapes/SphereCollisionShape.h" #include "../../orxonox/graphics/Model.h" namespace orxonox { RegisterClass(AsteroidMinable); // This constructor is for XML access only. AsteroidMinable::AsteroidMinable(Context* context) : Pawn(context){ // Da auch noetig? Wegen set in XML-Code? RegisterObject(AsteroidMinable); this->context = context; this->initialised = false; //Noetig, damit nicht sofort zerstoert? this->setCollisionType(WorldEntity::CollisionType::Dynamic); // Old from Pawn this->registerVariables(); orxout() << "AsteroidMining:: Pseudo-Konstruktor passiert!" << endl; } // Call this one from other C-files. Takes arguments. AsteroidMinable::AsteroidMinable(Context* c, float size, Vector3 position) : Pawn(c){ RegisterObject(AsteroidMinable); // Old stuff from pawn this->setRadarObjectColour(ColourValue(1.0f, 1.0f, 0.0f, 1.0f)); this->setRadarObjectShape(RadarViewable::Shape::Dot); this->setCollisionType(WorldEntity::CollisionType::Dynamic); this->enableCollisionCallback(); // Default Values this->generateSmaller = true; //this->setHealth(50); this->context = c; this->size = size; // customSize this->health_ = 15*size;// capped at 200 in pawn or smth? this->maxHealth_ = this->health_; this->acceptsPickups_ = false; this->setPosition(position); //this->roll = rand()*5; //etwas Drehung. richtige Variable // // Fliegt davon, irgendwieso. Dies scheint auch nicht zu nuetzen. // this->setVelocity(Vector3(0,0,0)); // Add Model // Model* hull = new Model(this->context); // random one of the 6 shapes char meshThingy[] = ""; sprintf(meshThingy, "ast%.0f.mesh", (round((5*(double)rand()/(double)RAND_MAX))+1)); // sprintf(str, "Value of Pi = %f", M_PI); hull->setMeshSource(meshThingy); hull->setScale(this->size); this->attach(hull); // Collision shape SphereCollisionShape* cs = new SphereCollisionShape(this->context); cs->setRadius((this->size)*2); //OFFEN: Feinabstimmung der Radien. 2.5 in AsteroidField.lua this->attachCollisionShape(cs); // Old from Pawn this->registerVariables(); this->initialised=true; orxout() << "AsteroidMining:: Initialisierung Zweitkonstruktor abgeschlosssssen." << endl; } AsteroidMinable::~AsteroidMinable(){ } void AsteroidMinable::putStuff(){ // Just create a new asteroid to avoid Collision shape scale problems etc. AsteroidMinable* reborn = new AsteroidMinable(this->context, this->size, this->getPosition()); reborn->toggleShattering(true); // mainly here to avoid 'unused' warning. this->~AsteroidMinable(); // seems dangerous. Necessary for efficiency? } void AsteroidMinable::XMLPort(Element& xmlelement, XMLPort::Mode mode) { SUPER(AsteroidMinable, XMLPort, xmlelement, mode); // XMLPortParam(PickupSpawner, "pickup", setPickupTemplateName, getPickupTemplateName, xmlelement, mode); XMLPortParam(AsteroidMinable, "size", setSize, getSize, xmlelement, mode); } void AsteroidMinable::XMLEventPort(Element& xmlelement, XMLPort::Mode mode) { SUPER(AsteroidMinable, XMLEventPort, xmlelement, mode); XMLPortEventState(AsteroidMinable, BaseObject, "vulnerability", setVulnerable, xmlelement, mode); } void AsteroidMinable::registerVariables() { // registerVariable(this->bAlive_, VariableDirection::ToClient); // registerVariable(this->bVulnerable_, VariableDirection::ToClient); // registerVariable(this->health_, VariableDirection::ToClient); // registerVariable(this->maxHealth_, VariableDirection::ToClient); registerVariable(this->size, VariableDirection::ToClient); registerVariable(this->generateSmaller, VariableDirection::ToClient); registerVariable(this->initialised, VariableDirection::ToClient); //registerVariable(this->context, VariableDirection::ToClient); // can't link that since it's a context // float size; // bool generateSmaller; // bool initialised; // Context* context; } void AsteroidMinable::tick(float dt) { if(!(this->initialised)){this->putStuff();} if(this->health_ <=0){this->death();} // if(this->initialised){ // // this->setVelocity(Vector3(0,0,0)); // Funktioniert, scheint aber unsinnig. // orxout() << "Flying at speed: " << this->getVelocity() << endl; // } } void AsteroidMinable::setSize(float s){ this->size = s; } float AsteroidMinable::getSize(){ return this->size; } void AsteroidMinable::toggleShattering(bool b){ this->generateSmaller = b; } void AsteroidMinable::death() //ueberschreiben { // orxout() << "AsteroidMinable::Death() aufgerufen." << endl; // OFFEN: Sauber kapputten // just copied other stuff: // this->setHealth(1); this->bAlive_ = false; this->destroyLater(); this->setDestroyWhenPlayerLeft(false); // pawn -> addExplosionPart // this->goWithStyle(); // Stuff that can be harvested. PickupSpawner* thingy = new PickupSpawner(this->context); // OFFEN: more precise size relation in custom resource pickup. char tname[] = ""; // can-t overwrite strings easily in C (strcat etc.) if(this->size <= 5){ strcat(tname, "smallmunitionpickup"); }else if(this->size <= 20){ strcat(tname, "mediummunitionpickup"); }else{ strcat(tname, "hugemunitionpickup"); } thingy->setPickupTemplateName(tname); thingy->setPosition(this->getPosition()); thingy->setMaxSpawnedItems(1); // Would be default anyways thingy->setRespawnTime(0.2f); // orxout() << "AsteroidMining::Death(): Passed Pickup stuff!" << endl; // Smaller Parts = 'Children' if(this->generateSmaller){ this->spawnChildren(); } // orxout() << "Wieder retour in death() geschafft. " << endl; } void AsteroidMinable::spawnChildren(){// Spawn smaller Children if (this->size <=1){return;} // Absicherung trivialer Fall int massRem = this->size-1; //some mass is lost int num = round(rnd()*(massRem-1)) + 1; // random number of children, at least one if(num > 10){num = 10;} // no max function in C! int masses[num] = {1}; // Masses of the asteroids, at least one. massRem = massRem-num; orxout() << "SpawnChildren(): passed basic stuff. num = " << num << endl; // Randomnised spawning points for the new asteroids float phi[num] = {0.0}; // assuming that it gets initialised to 0. Add (= {0.0})? float theta[num] = {0.0}; float pi = 3.14159;//math.pi; float d_p = 2*pi/num; float d_t = pi/num; float p = d_p/2; float t = d_t/2; // float phiOffset = rnd()*2*pi; // Added everywhere to become independent of the coordinate system // float thetaOffset = rnd()*pi; float rScaling = tan(t); // scale radius to prevent asteroids from touching. (distance=AsteroidRadius/tan(sector/2)) int pos; for(int it = 0; it0){ // Required to avoid array of size 0 or access problems int c = massRem; float probDensity[c] = {0.0}; int a = round(massRem/num); int b = c-a; int m = 2/c; // Peak value, is reached at the average size. int z = 0; for(z = 0; z<=a; ++z){probDensity[z] = m*z/a; } // rising part for(z = a+1; z0; ++trav){ int result = 0; float rVal = rnd();// between 0 and 1 float probSum = probDensity[0]; while(rVal>probSum){ probSum = probSum+probDensity[result+1]; ++result; } if(result>massRem){result = massRem;} massRem = massRem-result; masses[trav] = masses[trav]+result; } } orxout() << "SpawnChildren(): passed Mass stuff. " << endl; for(int fisch = 0; fischcontext, masses[fisch], this->getPosition() + *pos); if(child == nullptr){ orxout(internal_error, context::pickups) << "Weird, can't create new AsteroidMinable." << endl; } } orxout() << "Leaving spawnChildren() method. " << endl; } void AsteroidMinable::hit(Pawn* originator, const Vector3& force, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage){ orxout() << "AsteroidMining::Hit(Variante 1) Dings aufgerufen. " << endl; // Kollision mit anderem Asteroid oder Pickup (OFFEN: evtl. Spawner oder irgendwas?) verhindern. In diesem Fall einfach nichts tun. // Wird staending aufgerufen -> Rechenleistung? if(orxonox_cast(originator) || orxonox_cast(originator)){return;} this->damage(damage, healthdamage, shielddamage, originator, cs); this->setVelocity(this->getVelocity() + force); // if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator))// && (!this->getController() || !this->getController()->getGodMode()) ) // { // this->damage(damage, healthdamage, shielddamage, originator, cs); // this->setVelocity(this->getVelocity() + force); // } } void AsteroidMinable::hit(Pawn* originator, btManifoldPoint& contactpoint, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage){ //orxout() << "AsteroidMining::Hit(Variante 2) Dings aufgerufen. " << endl; // Kollision mit anderem Asteroid oder Pickup (OFFEN: evtl. Spawner oder irgendwas?) verhindern. In diesem Fall einfach nichts tun. // Wird staending aufgerufen -> Rechenleistung? if(orxonox_cast(originator) || orxonox_cast(originator)){return;} //orxout() << "Schaden. HP: " << this->health_ << " Dmg: " << damage << " hDmg: " << healthdamage << " sDmg: " << shielddamage << endl; this->damage(damage, healthdamage, shielddamage, originator, cs); // if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator))// && (!this->getController() || !this->getController()->getGodMode()) ) // { // this->damage(damage, healthdamage, shielddamage, originator, cs); // //if ( this->getController() ) // // this->getController()->hit(originator, contactpoint, damage); // changed to damage, why shielddamage? // } } //Pawn: // void Pawn::hit(Pawn* originator, const Vector3& force, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage) // { // if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) ) // { // this->damage(damage, healthdamage, shielddamage, originator, cs); // this->setVelocity(this->getVelocity() + force); // } // } // void Pawn::hit(Pawn* originator, btManifoldPoint& contactpoint, const btCollisionShape* cs, float damage, float healthdamage, float shielddamage) // { // if (this->getGametype() && this->getGametype()->allowPawnHit(this, originator) && (!this->getController() || !this->getController()->getGodMode()) ) // { // this->damage(damage, healthdamage, shielddamage, originator, cs); // if ( this->getController() ) // this->getController()->hit(originator, contactpoint, damage); // changed to damage, why shielddamage? // } // } // /** // @brief // Virtual function that gets called when this object collides with another. // @param otherObject // The object this one has collided into. // @param ownCollisionShape // The collision shape of the other object // @param contactPoint // Contact point provided by Bullet. Holds more information and can me modified. See return value. // @return // Returning false means that no modification to the contactPoint has been made. Return true otherwise! // @note // Condition is that enableCollisionCallback() was called. // */ // virtual inline bool collidesAgainst(WorldEntity* otherObject, const btCollisionShape* ownCollisionShape, btManifoldPoint& contactPoint) // { return false; } /* With false, Bullet assumes no modification to the collision objects. */ // //! Enables the collidesAgainst(.) function. The object doesn't respond to collision otherwise! // inline void enableCollisionCallback() // { this->bCollisionCallbackActive_ = true; this->collisionCallbackActivityChanged(); } // //! Disables the collidesAgainst(.) function. @see enableCollisionCallback() // inline void disableCollisionCallback() // { this->bCollisionCallbackActive_ = false; this->collisionCallbackActivityChanged(); } // //! Tells whether there could be a collision callback via collidesAgainst(.) // inline bool isCollisionCallbackActive() const // { return this->bCollisionCallbackActive_; } // //! Enables or disables collision response (default is of course on) // inline void setCollisionResponse(bool value) // { this->bCollisionResponseActive_ = value; this->collisionResponseActivityChanged(); } // //! Tells whether there could be a collision response // inline bool hasCollisionResponse() // { return this->bCollisionResponseActive_; } } /* void DronePickup::changedUsed(void) { SUPER(DronePickup, changedUsed); // If the pickup has transited to used. if(this->isUsed()) { Pawn* pawn = this->carrierToPawnHelper(); if(pawn == nullptr) // If the PickupCarrier is no Pawn, then this pickup is useless and therefore is destroyed. this->Pickupable::destroy(); //Attach to pawn Drone* drone = new Drone(pawn->getContext()); // this is neccessary because the projectiles fired need a valid creator for the particlespawner (when colliding against something) drone->addTemplate(this->getDroneTemplate()); Controller* controller = drone->getController(); DroneController* droneController = orxonox_cast(controller); if(droneController != nullptr) { droneController->setOwner(pawn); } Vector3 spawnPosition = pawn->getWorldPosition() + Vector3(30,0,-30); drone->setPosition(spawnPosition); // The pickup has been used up. this->setUsed(false); } else { // If either the pickup can only be used once or it is continuous and used up, it is destroyed upon setting it to unused. if(this->isOnce() || (this->isContinuous() )) { this->Pickupable::destroy(); } } } */