/* * 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: * Julien Kindle * Co-authors: * Noah Zarro * Theo von Arx * * */ /** @file SOBFigure.cc @brief This class represents your figure when you play the minigame. Here the movement of the figure, activating items, ... are handled. */ #include "SOBFigure.h" #include "core/CoreIncludes.h" #include "core/XMLPort.h" #include "graphics/Model.h" #include "graphics/Camera.h" #include "graphics/ParticleSpawner.h" #include #include "SOBTube.h" #include "SOBMushroom.h" #include "SOBGumba.h" #include "SOBCactus.h" #include "SOBGumbaBoss.h" #include "SOBFireball.h" #include "SOB.h" #include "SOBFlagstone.h" #include "SOBCastlestone.h" #include "Highscore.h" #include namespace orxonox { RegisterClass(SOBFigure); SOBFigure::SOBFigure(Context* context) : ControllableEntity(context) { RegisterObject(SOBFigure); // initialize variables gravityAcceleration_ = 350.0; //Vars for movement of player moveUpPressed_ = false; moveDownPressed_ = false; moveLeftPressed_ = false; moveRightPressed_ = false; firePressed_ = false; collDisZ_ = 0; tube = NULL; //Variables for Action in Tube tcol_ = false; tubing = false; kkk = 0; //Times and turning timeSinceLastFire_ = 0.0; lastSpeed_z = 0.0; timeCounter_ = 0; //Properties of player isColliding_ = true; particlespawner_ = NULL; //Properties of players life predead_ = false; dead_ = false; lvlEnded_ = false; reachedLvlEndState_ = 0; positiontube_=10000; // Properties concerning PowerUps and items PowerUpCounter_ = 0; maxPowerUp_ = 2; FireballPower = 2; //Properties of fireing Fireballs, NOTE! fireballs are fired with the moveUP Key, not with the fire key fireallowed_ = true; firecooldown_ = 0; setAngularFactor(0.0); //Means player doesn't turn on collision, so he doesn't fall over while walking over the ground this->enableCollisionCallback(); // Turns on that on every collision function collidesAgainst is executed } bool SOBFigure::collidesAgainst(WorldEntity* otherObject, const btCollisionShape* ownCollisionShape, btManifoldPoint& contactPoint) { //Inform tick fct that player is colliding and tell him how far away the collision point is from player middle point in z dir isColliding_ = true; collDisZ_ = getPosition().z - contactPoint.getPositionWorldOnB().getZ(); //Orxocast returns object with casted type if otherObject has that class, and if not a nullptr //SOBTube* test; SOBMushroom* mush = orxonox_cast (otherObject); tube = orxonox_cast (otherObject); SOBGumba* gumba = orxonox_cast (otherObject); SOBCactus* cactus = orxonox_cast (otherObject); SOBGumbaBoss* gumbaBoss = orxonox_cast (otherObject); SOBFlagstone* flagstone = orxonox_cast (otherObject); SOBCastlestone* castlestone = orxonox_cast (otherObject); SOBFireball* fireball = orxonox_cast (otherObject); SOB* SOBGame = orxonox_cast (getGametype()); //Check if otherObject is a powerup-mushroom if (mush != nullptr && !(mush->hasCollided_)) { otherObject->destroyLater(); PowerUpCounter_++; if(PowerUpCounter_ > maxPowerUp_) PowerUpCounter_ = maxPowerUp_; // you had already the max else this->changeClothes(); SOBGame->addMushroom(); // Tell the gametype to increase points mush->hasCollided_ = true; // needed because of destroyLater takes some time and player should receive points only once }else if(tube != nullptr && !(tube->hasCollided_)){ //Check if other Object is a tube and set position wher tube is in positiontube_ tube->hasCollided_=true;positiontube_=getPosition().x;tcol_=true; //tcol_ used for movedown function in SOBfigure.cc }else if (gumba != nullptr && gumbaBoss == nullptr && !(gumba->hasCollided_)) { //Check if otherObject is a Gumba (that walking enemies) //If player jumps on its head, kill the Gumba, else, kill the player if (getVelocity().z >= -20) { // If player hasn't a power up, he dies. Else he shrinks and the gumba dies. if(PowerUpCounter_ == 0){ this->die(); } else{ PowerUpCounter_--; this->changeClothes(); gumba->destroyLater(); gumba->hasCollided_ = true; } } else { gumba->destroyLater(); gumba->hasCollided_ = true; SOBGame->addGumba(); } } else if (cactus != nullptr && !(cactus ->hasCollided_)) { if(PowerUpCounter_ == 0){ this->die(); } else { PowerUpCounter_--; this->changeClothes(); } } else if (gumbaBoss != nullptr && !(gumbaBoss->hasCollided_)) { if (getVelocity().z >= -20) { // If player hasn't a power up, he dies. Else he dies directly. this->die(); } else { gumbaBoss->destroyLater(); gumbaBoss->hasCollided_ = true; SOBGame->addGumbaBoss(); } } else if (fireball != nullptr && !(fireball->hasCollided_)){ if(PowerUpCounter_ == 0){ this->die(); } PowerUpCounter_--; this->changeClothes(); fireball->destroyLater(); } //Purpose is that if player hits the flag, he should walk into the castle at the end of the level. For that we use SOBCastlestone if (reachedLvlEndState_ == 0 && flagstone != nullptr && !(flagstone->hasCollided_)) { flagstone->hasCollided_ = true; reachedLvlEndState_ = 1; SOBGame->setDone(true); SOBGame->addPoints(flagstone->getPoints()); } if (castlestone != nullptr && !(castlestone->hasCollided_)) { castlestone->hasCollided_ = true; reachedLvlEndState_++; } return true; } //Self implemented sign function that returns either 1 or -1 (and never 0) int SOBFigure::sgn(float x) { if (x < 0.0) return -1; return 1; } /*bool SOBFigure::tubeAction(){ return true; }*/ //Function to spawn the Fireball void SOBFigure::spawnFireball() { SOBCenterpoint* center_ = ((SOB*)getGametype())->center_; SOBFireball* ball = new SOBFireball(center_->getContext()); Vector3 spawnpos = this->getWorldPosition(); spawnpos.z += 0; if (ball != nullptr && center_ != nullptr) { ball->addTemplate("fireball"); bool direction = ((this->getWorldOrientation().getRoll().valueRadians())>-1.6&&(this->getWorldOrientation().getRoll().valueRadians()<1.6)); ball->setDirection(direction); if(direction) { spawnpos.x+=10; } else { spawnpos.x-=10; } ball->setPosition(spawnpos); } } //For those of you who don't have an idea: the tick function is called about 50 times/sec void SOBFigure::tick(float dt) { SUPER(SOBFigure, tick, dt); bool inputAllowed = true; //the particle spawner that generates the fire from the backpack when pressed if (particlespawner_ == NULL) { for (WorldEntity* object : this->getAttachedObjects()) { if (object->isA(Class(ParticleSpawner))) particlespawner_ = object; } } //Behavior on level end - this is like described above for the movement from the player when hit the flag. He moves then into the castle if (reachedLvlEndState_ != 0) { timeCounter_+= dt; inputAllowed = false; } if (reachedLvlEndState_ == 1 && timeCounter_ >= 1.5) { timeCounter_ = 0; reachedLvlEndState_ = 2; } //if input blocked, then cancel every movement operation if (!inputAllowed) { moveUpPressed_ = false; moveDownPressed_ = false; moveLeftPressed_ = false; moveRightPressed_ = false; } //set the gravityto standard 350 if (firePressed_ == false) { gravityAcceleration_ = 350.0; } if (hasLocalController()) { SOB* SOBGame = orxonox_cast(getGametype()); Vector3 velocity = getVelocity(); Vector3 position = getPosition(); if (!predead_) velocity.y = 0; //If player falls in a hole if (position.z < -250) { dead_ = true; SOBGame->setDone(true); } if (dead_) { velocity.x = 0; velocity.z = 0; setVelocity(velocity); if (firePressed_) SOBGame->restart(); return; } int maxvelocity_x = 100; int speedAddedPerTick = 5; int camMaxOffset = 25; int camMaxOffsetz = 25; timeSinceLastFire_ += dt; lastSpeed_z = velocity.z; //Handle the rocket fire from the jetpack if (velocity.z > 40) particlespawner_->setVisible(true); else particlespawner_->setVisible(false); //If player hits space and collides against an object under him then jump if (inputAllowed && firePressed_ && isColliding_ && (collDisZ_ >= 0 && collDisZ_ <+ 10)) { gravityAcceleration_ = 350; velocity.z = 175; } //Left-right movement with acceleration and rotation float rot = getOrientation().getRoll().valueDegrees(); if (moveRightPressed_) { if (!(rot < 5.0 && -5.0 < rot)) setOrientation(Vector3::UNIT_Z, getOrientation().getRoll() - sgn(rot)*dt*Radian(6)); if (std::abs(velocity.x) < maxvelocity_x) { velocity.x += speedAddedPerTick; } } else if (moveLeftPressed_) { if (!(abs(rot) > 175.0 )) setOrientation(Vector3::UNIT_Z, getOrientation().getRoll() + sgn(rot)*dt*Radian(6)); if (std::abs(velocity.x) < maxvelocity_x) { velocity.x -= speedAddedPerTick; } } else { velocity.x /= 1.1; } //If moveUp pressed, fire a fireball if(moveUpPressed_ && inputAllowed) //if pressed up jump through tube if you are underground { Vector3 position1 = getPosition(); if(position1.z>=-100 && position1.z<=-40){ position1.z=40; if(460<=position1.x && position1.x<480){setPosition(position1);} //little hack works without this but first tube must be touched //position where second tube is if(positiontube_-10<=position1.x && position1.x= FireballPower) && fireallowed_){ spawnFireball(); fireallowed_ = false; firecooldown_ = 0;} } if(moveDownPressed_ && inputAllowed ){ //if movedownpressed change collisiontype of tube and slip through tube Vector3 position1 = getPosition(); if(tube != nullptr && !(tube->movedown)){ //Check if other Object is a tube if(positiontube_-10movedown=true;} } } //Increase the firecooldown if(firecooldown_> 0.5) { fireallowed_ = true; } if(!fireallowed_) { firecooldown_ += dt; } //Again another EndOfLevel behavior if (reachedLvlEndState_ == 1) velocity.x = -2; if (reachedLvlEndState_ == 2) velocity.x = 30; if (reachedLvlEndState_ == 3) { velocity.x = 0; velocity.y = 20; setOrientation(Vector3::UNIT_Z, Degree(90)); } if (reachedLvlEndState_ == 4) { //Highscore if (Highscore::exists()) { int score = SOBGame->getPoints(); bool isHighScore = Highscore::getInstance().storeScore("Super Orxo Bros.", score, this->getPlayer()); SOBGame->newHighscore = isHighScore; } lvlEnded_ = true; dead_ = true; } //velocity = acc. * time velocity.z -= gravityAcceleration_*dt; setVelocity(velocity); //Vector3 pos = this->getPosition(); // pos.y = 0; // this->setPosition(pos); //Camera operation - the camera should always follow the player in a specific region Camera* cam = getCamera(); Vector3 campos = cam->getPosition(); if (campos.x + camMaxOffset < position.x) { campos.x = position.x - camMaxOffset; cam->setPosition(campos); } if (campos.x - camMaxOffset > position.x) { campos.x = position.x + camMaxOffset; cam->setPosition(campos); } if(campos.y + camMaxOffsetz < position.z) { campos.y = position.z - camMaxOffset; cam -> setPosition(campos); } if(campos.y - camMaxOffsetz > position.z) { campos.y = position.z + camMaxOffset; cam ->setPosition(campos); } } // Reset key variables moveUpPressed_ = false; moveDownPressed_ = false; moveLeftPressed_ = false; moveRightPressed_ = false; isColliding_ = false; collDisZ_ = 0; } //The following functions read the input of the player and then set the bools for the movement void SOBFigure::moveFrontBack(const Vector2& value) { if (value.x > 0) { moveUpPressed_ = true; moveDownPressed_ = false; } else { moveUpPressed_ = false; moveDownPressed_ = true; } } void SOBFigure::moveRightLeft(const Vector2& value) { if (value.x > 0) { moveLeftPressed_ = false; moveRightPressed_ = true; } else { moveLeftPressed_ = true; moveRightPressed_ = false; } } void SOBFigure::boost(bool boost) { firePressed_ = boost; } // PRE: name is an existing name of a material. Example orxo_material for orxo_material.material in data_extern/materials // !!! PowerUpCounter_ has to be modified before changing the clothes!!! // POST: clothes of body of player are changed to name void SOBFigure::changeClothes(){ // clothes: white (basic), red (one PowerUp), orange (Fireball enabled) std::string clothes[] = {"orxo_material", "orxo_material_gross", "orxo_material_fire"}; std::set attachedObjects = this->getAttachedObjects(); std::set::iterator it; for (it = attachedObjects.begin(); it != attachedObjects.end(); ++it) { Model* FiguresModel = orxonox_cast(*it); if (FiguresModel != nullptr) { FiguresModel->setSubMaterial(clothes[PowerUpCounter_] , 4); // 4 is the body } } } // PRE: // POST: Player jumps out of the game, game is finished and can be restarted. void SOBFigure::die(){ Vector3 vel = getVelocity(); vel.y = -80; vel.z = 200; setVelocity(vel); predead_= true; SOB* SOBGame = orxonox_cast(getGametype()); SOBGame->setDone(true); } }