/* 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: 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. 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 "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(){ if (this->size <=1){return;} // Absicherung trivialer Fall // Spawn smaller Children int massRem = this->size-1; //some mass is lost int num = round((massRem-1)*(double)rand() / (double)RAND_MAX)+1; // random number of children, at least one // Tweak towards bigger junks? if(num > 10){num = 10;} // no max function in C! // num = 1; // zum Testen mal nur einen generieren. // // TODO: Check this draft for edge cases etc. // // 'Triangular', discrete probability density with max at massRem/num // // Max at a, value 1, subintervals a+b = c // int c = massRem-num; // float probDensity = [c] // int a = round(massRem/num); // int b = c-a; // for(int z = 0; z<=a; z++){probDensity[z] = m*z/a; } // rising part // for(z = a+1; zprobSum){ // probSum = probSum+probDensity[result]; // result++: // } massRem = massRem-num; int extra = 0; //orxout() << "Number of Children: " << num << endl; for(int fisch=num; fisch>=1; --fisch){ // to distribute remaining mass //orxout() << "AsteroidMining::spawnChildren(): Fisch-Variable: " << fisch << endl; if(fisch==1){ extra = massRem; }else{ extra = round(massRem*(double)rand() / (double)RAND_MAX); massRem = massRem-extra; } //orxout() << "Mass chosen: " << extra+1 << endl; //orxout() << "AsteroidMining::spawnChildren(): Inside for-loop." << endl; //Spawn this child Game crashes! //AsteroidMinable* child = new AsteroidMinable(this->context); int newMass = extra+1; AsteroidMinable* child = new AsteroidMinable(this->context, newMass, this->getPosition() + Vector3(fisch*newMass*2.5, 0, 0)); // if(!(child == nullptr)){//Errorgebastel // Position zu spaet gesetzt? Tick nicht atomar -> falsche Groesse evtl? // child->setSize(extra + 1); // //OFFEN:Kollision der Kinder verhindern // //Relativ zu Elternteil automatisch? // //Typ position:rand()*Vektoriwas? // //pawn->getWorldPosition() + Vector3(30,0,-30); // child->setPosition(this->getPosition() + Vector3(num*5, 0, 0)); // //child->setPosition(Vector3(0,0,0)); if(child == nullptr){ orxout(internal_error, context::pickups) << "Weird, can't create new AsteroidMinable." << endl; } //orxout() << "Done: Creating new Asteroid" << endl; // } // 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()); } // 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(); } } } */