/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx 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, or (at your option) any later version. ### File Specific main-programmer: Patrick Boenzli co-programmer: Benjamin Grauer 2005-07-24: Benjamin Grauer: restructurate, so it can handle the new Weapons. */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_WEAPON #include "weapon_manager.h" #include "weapon.h" #include "crosshair.h" #include "playable.h" #include "util/loading/load_param_xml.h" #include "util/loading/factory.h" #include "t_animation.h" ObjectListDefinition(WeaponManager); /** * @brief this initializes the weaponManager for a given nnumber of weapon slots * @param number of weapon slots of the model/ship <= 8 (limitied) */ WeaponManager::WeaponManager(WorldEntity* parent) { this->init(); this->setParentEntity(parent); assert (parent != NULL); } WeaponManager::WeaponManager(const TiXmlElement* root) { this->init(); this->loadParams(root); } /** * @brief Destroys a WeaponManager */ WeaponManager::~WeaponManager() { // crosshair being a PNode it must not be deleted (this is because PNodes delete themselves.) // rennerc: crosshair seems not to delete itselve //if (Crosshair::objectList().exists(this->crosshair)) // delete this->crosshair; } /** * @brief initializes the WeaponManager */ void WeaponManager::init() { this->registerObject(this, WeaponManager::_objectList); this->parentNode = NULL; this->parentEntity = NULL; for (int i = 0; i < WM_MAX_CONFIGS; i++) for (int j = 0; j < WM_MAX_SLOTS; j++) this->configs[i][j] = NULL; for (int i = 0; i < WM_MAX_SLOTS; i++) { this->currentSlotConfig[i].capability = WTYPE_ALL; this->currentSlotConfig[i].currentWeapon = NULL; this->currentSlotConfig[i].nextWeapon = NULL; // NAMING char* tmpName; if (!this->getName().empty()) { tmpName = new char[this->getName().size() + 10]; sprintf(tmpName, "%s_slot%d", this->getCName(), i); } else { tmpName = new char[30]; sprintf(tmpName, "WeaponMan_slot%d", i); } this->currentSlotConfig[i].position.setName(tmpName); this->currentSlotConfig[i].position.deactivateNode(); delete[] tmpName; } for (int i = 0; i < WM_MAX_LOADED_WEAPONS; i++) this->availiableWeapons[i] = NULL; this->currentConfigID = 0; this->slotCount = 2; //this->weaponChange; // CROSSHAIR INITIALISATION this->crosshair = new Crosshair(); //this->crosshair->setRelCoor(1000,0,0); this->crossHairSizeAnim = new tAnimation(this->crosshair, &Crosshair::setSize); this->crossHairSizeAnim->setInfinity(ANIM_INF_REWIND); this->crossHairSizeAnim->addKeyFrame(50, .1, ANIM_LINEAR); this->crossHairSizeAnim->addKeyFrame(100, .05, ANIM_LINEAR); this->crossHairSizeAnim->addKeyFrame(50, .01, ANIM_LINEAR); } /** * @brief loads the settings of the WeaponManager * @param root the XML-element to load from */ void WeaponManager::loadParams(const TiXmlElement* root) { BaseObject::loadParams(root); LoadParam(root, "slot-count", this, WeaponManager, setSlotCount) .describe("how many slots(cannons) the WeaponManager can handle"); LOAD_PARAM_START_CYCLE(root, element); { // CHECK IF THIS WORKS.... LoadParamXML_CYCLE(element, "weapons", this, WeaponManager, loadWeapons) .describe("loads Weapons"); } LOAD_PARAM_END_CYCLE(element); } /** * @brief loads a Weapon onto the WeaponManager * @param root the XML-element to load the Weapons from */ void WeaponManager::loadWeapons(const TiXmlElement* root) { LOAD_PARAM_START_CYCLE(root, element); BaseObject* object = Factory::fabricate(element); if (object != NULL) { Weapon* newWeapon = dynamic_cast(object); if (newWeapon == NULL) delete object; } LOAD_PARAM_END_CYCLE(element); } /** * @brief sets the Parent of the WeaponManager. * @param parent the parent of the WeaponManager * * this is used, to identify to which ship/man/whatever this WeaponManager is connected. * also all the Slots will be subconnected to this parent. * * The reason this function exists is that the WeaponManager is neither a WorldEntity nor * a PNode. */ void WeaponManager::setParentEntity(WorldEntity* parent) { this->parentEntity = parent; if (this->parentNode == NULL) this->setParentNode(parent); } void WeaponManager::setParentNode(PNode* parent) { this->parentNode = parent; assert(parent != NULL); if (this->parentNode != NULL) { for (int i = 0; i < WM_MAX_SLOTS; i++) this->parentNode->addChild(&this->currentSlotConfig[i].position); } } /** * @brief sets the number of Slots the WeaponManager has * @param slotCount the number of slots */ void WeaponManager::setSlotCount(unsigned int slotCount) { if (slotCount <= WM_MAX_SLOTS) this->slotCount = slotCount; else this->slotCount = WM_MAX_SLOTS; } /** * @brief sets the position of the Slot relative to the parent * @param slot the slot to set-up * @param position the position of the given slot */ void WeaponManager::setSlotPosition(int slot, const Vector& position, PNode* parent) { if (slot < this->slotCount) { this->currentSlotConfig[slot].position.setRelCoor(position); if (parent != NULL) this->currentSlotConfig[slot].position.setParent(parent); } } /** * @brief sets the relative rotation of the slot to its parent * @param slot the slot to set-up * @param rotation the relative rotation of the given slot */ void WeaponManager::setSlotDirection(int slot, const Quaternion& rotation) { if (slot < this->slotCount) this->currentSlotConfig[slot].position.setRelDir(rotation); } /** * @brief adds a weapon to the selected weaponconfiguration into the selected slot * @param weapon the weapon to add * @param configID an identifier for the slot: number between 0..7 if not specified: slotID=next free slot * @param slotID an identifier for the weapon configuration, number between 0..3 * * if you add explicitly a weapon at config:n, slot:m, the weapon placed at this location will be * replaced by the weapon specified. if you use the WM_FREE_SLOT, the manager will look for a free * slot in this weaponconfiguration. if there is non, the weapon won't be added and there will be * a error message. */ bool WeaponManager::addWeapon(Weapon* weapon, int configID, int slotID) { if ( weapon == NULL ) return false; if (unlikely(configID >= WM_MAX_CONFIGS || slotID >= (int)this->slotCount)) { PRINTF(2)("Slot %d of config %d is not availiabe (max: %d) searching for suitable slot\n", slotID, configID, this->slotCount); if (configID >= WM_MAX_CONFIGS) configID = -1; if (slotID >= (int)this->slotCount) slotID = -1; } // if no ConfigID is supplied set to Current Config. if (configID <= -1) configID = this->currentConfigID; // if (configID > -1 && slotID == -1) { slotID = this->getNextFreeSlot(configID, weapon->getCapability()); if (slotID == -1) configID = -1; } if (configID > 0 && slotID > 0 && this->configs[configID][slotID] != NULL) { PRINTF(3)("Weapon-slot %d/%d of %s already poulated, remove weapon (%s::%s) first\n", configID, slotID, this->getCName(), weapon->getClassCName(), weapon->getCName()); return false; } if (slotID <= -1) // WM_FREE_SLOT { slotID = this->getNextFreeSlot(configID, weapon->getCapability()); if( slotID < 0 || slotID >= this->slotCount) { PRINTF(1)("There is no free slot in this WeaponConfig to dock this weapon at! Aborting\n"); return false; } } if (!(this->currentSlotConfig[slotID].capability & weapon->getCapability() & WTYPE_ALLKINDS) && this->currentSlotConfig[slotID].capability & weapon->getCapability() & WTYPE_ALLDIRS) { PRINTF(2)("Unable to add Weapon with wrong capatibility to Slot %d (W:%d M:%d)\n", slotID, weapon->getCapability(), this->currentSlotConfig[slotID].capability); return false; } //! @todo check if the weapon is already assigned to another config in another slot if (this->configs[configID][slotID] != NULL) return false; this->configs[configID][slotID] = weapon; weapon->setAmmoContainer(this->getAmmoContainer(weapon->getProjectileType())); if(configID == this->currentConfigID) this->currentSlotConfig[slotID].nextWeapon = weapon; //if (this->parent != NULL) { this->parentNode->addChild(weapon); if (this->parentEntity->isA(Playable::staticClassID())) dynamic_cast(this->parentEntity)->weaponConfigChanged(); weapon->setDefaultTarget(this->crosshair); } PRINTF(4)("Added a new Weapon (%s::%s) to the WeaponManager: config %i/ slot %i\n", weapon->getClassCName(), weapon->getCName(), configID, slotID); return true; } /** * @brief increases the Energy of the WeaponContainer of type (projectileType) * @param projectileType the type of weapon to increase Energy from * @param ammo the ammo to increase */ float WeaponManager::increaseAmmunition(const ClassID& projectileType, float ammo) { return this->getAmmoContainer(projectileType)->increaseEnergy(ammo); } /** * @brief does the same as the funtion inclreaseAmmunition, added four your convenience * @param weapon, the Weapon to read the ammo-info about. * @param ammo how much ammo to add. */ float WeaponManager::inclreaseAmmunition(const Weapon* weapon, float ammo) { assert (weapon != NULL); return this->increaseAmmunition(weapon->getClassID(), ammo); } /** * sets the capabilities of a Slot * @param slot the slot to set the capability * @param slotCapability the capability @see WM_SlotCapability */ void WeaponManager::setSlotCapability(int slot, long slotCapability) { if (slot > slotCount) return; this->currentSlotConfig[slot].capability = slotCapability; } /** * removes a Weapon from the WeaponManager * * !! The weapon must be inactive before you can delete it, !! * !! because it will still be deactivated (if it is selected) !! */ void WeaponManager::removeWeapon(Weapon* weapon, int configID) { if (weapon == NULL) return; if (configID < 0) { for (int j = 0; j < WM_MAX_SLOTS; j++) { for (int i = 0; i < WM_MAX_CONFIGS; i++) { if (this->configs[i][j] == weapon) this->configs[i][j] = NULL; } if (this->currentSlotConfig[j].currentWeapon == weapon) { this->currentSlotConfig[j].nextWeapon = NULL; } } } } /** * changes to the next weapon configuration */ void WeaponManager::nextWeaponConfig() { ++this->currentConfigID; if (this->currentConfigID >= WM_MAX_CONFIGS) this->currentConfigID = 0; this->changeWeaponConfig(this->currentConfigID); } /** * changes to the previous configuration */ void WeaponManager::previousWeaponConfig() { --this->currentConfigID; if (this->currentConfigID < 0) this->currentConfigID = WM_MAX_CONFIGS -1; this->changeWeaponConfig(this->currentConfigID); } /** * change to a desired configuration * @param weaponConfig the configuration to jump to. */ void WeaponManager::changeWeaponConfig(int weaponConfig) { this->currentConfigID = weaponConfig; PRINTF(4)("Changing weapon configuration: to %i\n", this->currentConfigID); for (int i = 0; i < WM_MAX_SLOTS; i++) this->currentSlotConfig[i].nextWeapon = this->configs[currentConfigID][i]; } /** * triggers fire of all weapons in the current weaponconfig */ void WeaponManager::fire() { Weapon* firingWeapon; for(int i = 0; i < this->slotCount; i++) { firingWeapon = this->currentSlotConfig[i].currentWeapon; if( firingWeapon != NULL) firingWeapon->requestAction(WA_SHOOT); } this->crosshair->setRotationSpeed(500); this->crossHairSizeAnim->replay(); } /** * triggers tick of all weapons in the current weaponconfig * @param second passed since last tick */ void WeaponManager::tick(float dt) { Weapon* tickWeapon; for(int i = 0; i < this->slotCount; i++) { /* NICE LITTLE DEBUG FUNCTION if (this->currentSlotConfig[i].currentWeapon != NULL || this->currentSlotConfig[i].nextWeapon != NULL) printf("%p %p\n", this->currentSlotConfig[i].currentWeapon, this->currentSlotConfig[i].nextWeapon);*/ // current Weapon in Slot i tickWeapon = this->currentSlotConfig[i].currentWeapon; // On A change (current != next) if (tickWeapon != this->currentSlotConfig[i].nextWeapon) { // if a Weapon is Active in slot i, deactivate it. if (tickWeapon != NULL ) { if (tickWeapon->isActive()) { tickWeapon->requestAction(WA_DEACTIVATE); continue; } else { tickWeapon->toList(OM_NULL); this->currentSlotConfig[i].currentWeapon = NULL; } } // switching to next Weapon tickWeapon = this->currentSlotConfig[i].currentWeapon = this->currentSlotConfig[i].nextWeapon; if (tickWeapon != NULL) { // if (this->parent != NULL) tickWeapon->toList(this->parentEntity->getOMListNumber()); tickWeapon->requestAction(WA_ACTIVATE); this->currentSlotConfig[i].position.activateNode(); tickWeapon->setParent(&this->currentSlotConfig[i].position); } else this->currentSlotConfig[i].position.deactivateNode(); if (this->parentEntity != NULL && this->parentEntity->isA(Playable::staticClassID())) dynamic_cast(this->parentEntity)->weaponConfigChanged(); } else if (unlikely(tickWeapon != NULL && tickWeapon->getCurrentState() == WS_DEACTIVATING)) this->currentSlotConfig[i].nextWeapon = NULL; } } /** * triggers draw of all weapons in the current weaponconfig */ void WeaponManager::draw() const { assert(false || "must not be called"); Weapon* drawWeapon; for (int i = 0; i < this->slotCount; i++) { drawWeapon = this->currentSlotConfig[i].currentWeapon; if( drawWeapon != NULL && drawWeapon->isVisible()) drawWeapon->draw(); } } /** * private gets the next free slot in a certain weaponconfig * @param the selected weaponconfig -1 if none found */ int WeaponManager::getNextFreeSlot(int configID, long capability) { if (configID == -1) { for (configID = 0; configID < WM_MAX_CONFIGS; configID++) for( int i = 0; i < this->slotCount; ++i) { if( this->configs[configID][i] == NULL && (this->currentSlotConfig[i].capability & capability & WTYPE_ALLKINDS) && (this->currentSlotConfig[i].capability & capability & WTYPE_ALLDIRS)) return i; } } else { for( int i = 0; i < this->slotCount; ++i) { if( this->configs[configID][i] == NULL && (this->currentSlotConfig[i].capability & capability & WTYPE_ALLKINDS) && (this->currentSlotConfig[i].capability & capability & WTYPE_ALLDIRS)) return i; } } return -1; } CountPointer& WeaponManager::getAmmoContainer(const ClassID& projectileType) { for (unsigned int i = 0; i < this->ammo.size(); i++) { if (this->ammo[i]->getProjectileType() == projectileType) return this->ammo[i]; } this->ammo.push_back(CountPointer(new AmmoContainer(projectileType))); return this->ammo.back(); } CountPointer& WeaponManager::getAmmoContainer(const Weapon* weapon) { assert (weapon != NULL); return (this->getAmmoContainer(weapon->getClassID())); } /** * outputs some nice debug information about the WeaponManager */ void WeaponManager::debug() const { PRINT(3)("WeaponManager Debug Information\n"); PRINT(3)("-------------------------------\n"); PRINT(3)("current Config is %d\n", this->currentConfigID); for (int i = 0; i < WM_MAX_CONFIGS; i++) { PRINT(3)("Listing Weapons in Configuration %d\n", i); for (int j = 0; j < WM_MAX_SLOTS; j++) { if (this->configs[i][j] != NULL) PRINT(3)("Slot %d loaded a %s\n", j, this->configs[i][j]->getClassCName()); } } }