/* 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 "class_list.h" #include "load_param.h" #include "factory.h" #include "list.h" #include "t_animation.h" #include "null_parent.h" using namespace std; /** * this initializes the weaponManager for a given nnumber of weapon slots * @param number of weapon slots of the model/ship <= 8 (limitied) */ WeaponManager::WeaponManager(PNode* parent) { this->init(); this->setParent(parent); } WeaponManager::WeaponManager(const TiXmlElement* root) { this->init(); this->loadParams(root); } /** * Destroys a WeaponManager */ WeaponManager::~WeaponManager() { // crosshair being a PNode it must not be deleted (this is because PNodes delete themselves.) //delete this->crosshair; } /** * initializes the WeaponManager */ void WeaponManager::init() { this->setClassID(CL_WEAPON_MANAGER, "WeaponManager"); this->parent = 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()) { tmpName = new char[strlen(this->getName()) + 10]; sprintf(tmpName, "%s_slot%d", this->getName(), i); } else { tmpName = new char[30]; sprintf(tmpName, "WeaponMan_slot%d", i); } this->currentSlotConfig[i].position.setName(tmpName); 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->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); this->targetIterator = NULL; } /** * loads the settings of the WeaponManager * @param root the XML-element to load from */ void WeaponManager::loadParams(const TiXmlElement* root) { static_cast(this)->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); } /** * 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); Weapon* newWeapon = dynamic_cast(Factory::getFirst()->fabricate(element)); /// @todo implement this !! LOAD_PARAM_END_CYCLE(element); } /** * 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::setParent(PNode* parent) { if (parent == NULL) parent = NullParent::getInstance(); this->parent = parent; if (this->parent != NULL) { for (int i = 0; i < WM_MAX_SLOTS; i++) this->parent->addChild(&this->currentSlotConfig[i].position); } } /** * 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; } /** * 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) { if (slot < this->slotCount) this->currentSlotConfig[slot].position.setRelCoor(position); } /** * 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); } /** * 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. */ void WeaponManager::addWeapon(Weapon* weapon, int configID, int slotID) { if (unlikely(configID >= WM_MAX_CONFIGS || slotID >= (int)this->slotCount)) { PRINTF(2)("Slot %d of config %d is not availiabe (max: %d)\n", slotID, configID, this->slotCount); return; } if (this->configs[configID][slotID] != NULL && configID > 0 && slotID > 0) PRINTF(3)("Weapon-slot %d/%d of %s already poulated, overwriting\n", configID, slotID, this->getName()); 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; } } 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; } //! @todo check if the weapon is already assigned to another config in another slot this->configs[configID][slotID] = weapon; if (this->parent != NULL) weapon->setParent(parent); PRINTF(3)("Added a new Weapon to the WeaponManager: config %i/ slot %i\n", configID, slotID); } /** * 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]; if (this->currentSlotConfig[i].currentWeapon != this->currentSlotConfig[i].nextWeapon) { if (this->currentSlotConfig[i].currentWeapon != NULL) (this->currentSlotConfig[i].currentWeapon->requestAction(WA_DEACTIVATE)); if (this->currentSlotConfig[i].nextWeapon != NULL && this->currentSlotConfig[i].nextWeapon->isActive()) this->currentSlotConfig[i].nextWeapon = NULL; } } } /** * Selects an Entity from the NPC-list, and returns the Target */ PNode* WeaponManager::getSomeTarget() { if (this->targetIterator == NULL) { std::list* npcList = ClassList::getList(CL_NPC); if (npcList != NULL) { this->targetIterator = npcList->begin(); } else return NULL; } ///FIXME // this->targetIterator++; PNode* retNode = dynamic_cast((*targetIterator)); // if (retNode == NULL && this->targetIterator->getList()->getSize() > 0) // retNode = dynamic_cast(targetIterator->firstElement()); return retNode; } /** * Selects an Entity from the Entity-List, that is near of the carrier PNode. * @param carrier: The PNode from which the distance should be measured * @param distance: The Maximum Distance to Return. */ PNode* WeaponManager::getDistanceTarget(const PNode* carrier, float distance) { std::list* npcList = ClassList::getList(CL_NPC); if (npcList != NULL) { list::iterator node; for (node= npcList->begin(); node != npcList->end(); node++) if ((carrier->getAbsCoor() - dynamic_cast(*node)->getAbsCoor()).len() < distance) return dynamic_cast(*node); } return this->getFixedTarget(); } /** * 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; // all weapons for(int i = 0; i < this->slotCount; i++) { tickWeapon = this->currentSlotConfig[i].currentWeapon; if (tickWeapon != this->currentSlotConfig[i].nextWeapon) // if no change occures { if (tickWeapon != NULL && tickWeapon->isActive()) { tickWeapon->requestAction(WA_DEACTIVATE); } else { tickWeapon = this->currentSlotConfig[i].currentWeapon = this->currentSlotConfig[i].nextWeapon; if (tickWeapon != NULL) { tickWeapon->requestAction(WA_ACTIVATE); tickWeapon->setParent(&this->currentSlotConfig[i].position); } } } if( tickWeapon != NULL && tickWeapon->isActive()) tickWeapon->tickW(dt); } crosshair->setRotationSpeed(5); } /** * triggers draw of all weapons in the current weaponconfig */ void WeaponManager::draw() const { 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 */ int WeaponManager::getNextFreeSlot(int configID, long capability) { 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; } /** * 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]->getClassName()); } } }