//Equipable.cpp #include "Equipable.h" #include "ServerManager.h" #include "PlayerClass.h" #include "MOBClass.h" #include "ObjectManager.h" Equipable::Equipable() { m_Player = 0; m_ItemBase = 0; m_Slot = 0; m_ReadyTime = GetNet7TickCount(); m_UsesAmmo = false; m_AuxEquipItem = 0; m_AuxAmmoItem = 0; m_MaxID = 0; m_EEffectID = 0; memset(&m_ItemInstance, 0, sizeof(ItemInstance)); memset(&m_AmmoInstance, 0, sizeof(AmmoInstance)); memset(&m_TimeNode, 0, sizeof(TimeNode)); m_AmmoBase = 0; } Equipable::~Equipable() { } void Equipable::Init(Player * Owner, int SlotNum) { m_Player = Owner; m_Slot = SlotNum; if (SlotNum == 0) { m_Type = EQUIP_SHIELD; } else if (SlotNum == 1) { m_Type = EQUIP_REACTOR; } else if (SlotNum == 2) { m_Type = EQUIP_ENGINE; } else if (SlotNum >= 3 && SlotNum <= 8) { m_Type = EQUIP_WEAPON; } else if (SlotNum >= 9 && SlotNum <= 14) { m_Type = EQUIP_DEVICE; } m_AmmoBase = 0; m_UsesAmmo = false; m_AuxEquipItem = &Owner->ShipIndex()->Inventory.EquipInv.EquipItem[SlotNum]; m_AuxAmmoItem = &Owner->ShipIndex()->Inventory.AmmoInv.Item[SlotNum]; m_ReadyTime = m_AuxEquipItem->GetReadyTime(); // Make sure we are calculating quality on item Owner->QualityCalculator(m_AuxEquipItem->GetItemData()); } /* We need to initialize this class with Aux data */ void Equipable::PullAuxData() { m_ItemBase = g_ItemBaseMgr->GetItem(m_AuxEquipItem->GetItemTemplateID()); if (!m_ItemBase) { return; } m_UsesAmmo = (m_ItemBase->SubCategory() == 101 || m_ItemBase->SubCategory() == 102); m_ItemInstance = m_ItemBase->GetItemInstance(m_AuxEquipItem->GetInstanceInfo()); if (m_UsesAmmo && m_AuxAmmoItem->GetItemTemplateID() > 0) { if (m_AmmoBase = g_ItemBaseMgr->GetItem(m_AuxAmmoItem->GetItemTemplateID())) { m_AmmoInstance = m_AmmoBase->GetAmmoInstance(m_AuxAmmoItem->GetInstanceInfo()); } } //TODO: Once server is stable, this needs to check if ReadyTime < GetNet7TickCount() first m_AuxEquipItem->SetReadyTime(GetNet7TickCount()); /* Only reactors, engines and shields have stats. Other items only have effects */ //if (m_Slot < 3) //{ SetStats(); //} //AddEffects(); } /* Checks if an item can be equiped in this slot */ bool Equipable::CanEquip(_Item * NewItem) { /* If this is a weapon/device then we can unequip it */ if (m_Slot >= 3 && m_Slot <= 14 && NewItem->ItemTemplateID == -1) { return true; } ItemBase * myItemBase = g_ItemBaseMgr->GetItem(NewItem->ItemTemplateID); /* If we fail to find an itembase - exit */ if (!myItemBase) { return false; } int SubCat = myItemBase->SubCategory(); /* Now check to see if this is ammo for current item */ if (SubCat == 103 && m_UsesAmmo && !CorrectAmmo(NewItem)) { m_Player->SendPriorityMessageString("The ammo does not fit here","MessageLine",2000,4); //printf("CanEquip - Wrong ammo\n"); return false; } /* Cannot equip ammo without a launcher */ if (SubCat == 103 && !m_UsesAmmo) { m_Player->SendPriorityMessageString("Weapon doesn't require ammo","MessageLine",2000,4); //m_Player->SendVaMessage("Weapon doesn't use ammo"); //printf("CanEquip - Ammo with no launcher\n"); return false; } /* Now check that the item matches the slot type */ if ((m_Slot == 0 && SubCat != 122) || // Shield (m_Slot == 1 && SubCat != 120) || // Reactor (m_Slot == 2 && SubCat != 121) || // Engine (m_Slot >= 3 && m_Slot <= 8 && SubCat != 100 && SubCat != 101 && SubCat != 102 && SubCat != 103) || //Weapon/Ammo (m_Slot >= 9 && m_Slot <= 15 && SubCat != 110)) //Device { m_Player->SendPriorityMessageString("Item does not fit here","MessageLine",2000,4); //printf("CanEquip - Wrong item for slot\n"); return false; } AuxSkill * Skills = &m_Player->PlayerIndex()->RPGInfo.Skills.Skill[0]; /* Now check skill requirements */ if ((SubCat == 100 && Skills[SKILL_BEAM_WEAPON].GetLevel() < myItemBase->TechLevel()) || (SubCat == 101 && Skills[SKILL_PROJECTILE_WEAPON].GetLevel() < myItemBase->TechLevel()) || (SubCat == 102 && Skills[SKILL_MISSILE_WEAPON].GetLevel() < myItemBase->TechLevel()) || (SubCat == 110 && Skills[SKILL_DEVICE_TECH].GetLevel() < myItemBase->TechLevel()) || (SubCat == 120 && Skills[SKILL_REACTOR_TECH].GetLevel() < myItemBase->TechLevel()) || (SubCat == 121 && Skills[SKILL_ENGINE_TECH].GetLevel() < myItemBase->TechLevel()) || (SubCat == 122 && Skills[SKILL_SHIELD_TECH].GetLevel() < myItemBase->TechLevel())) { m_Player->SendPriorityMessageString("You need more skill to equip this item","MessageLine",2000,4); //printf("CanEquip - bad skill\n"); return false; } ItemRequirements Req = myItemBase->GetItemRequirements(); /* Now check for race restrictions */ if (Req.RaceRestriction & (0x01 << m_Player->Race())) { //printf("CanEquip - Race restriction\n"); m_Player->SendPriorityMessageString("Your Race can not equip this item","MessageLine",2000,4); return false; } /* Also check for race lore restrictions */ if ((m_Player->Race() == 1 && Req.LoreRestriction == 0x02) || (m_Player->Race() == 2 && Req.LoreRestriction == 0x01)) { //printf("CanEquip - Lore restriction\n"); m_Player->SendPriorityMessageString("Your Lore can not equip this item","MessageLine",2000,4); return false; } /* Now check for profession restrictions */ if (Req.ProfessionRestriction & (0x01 << m_Player->Profession())) { m_Player->SendPriorityMessageString("Your profession can not equip this item","MessageLine",2000,4); //printf("CanEquip - Profession restriction\n"); return false; } /* Now check for level requirements */ if ((Req.CombatRequirement > m_Player->CombatLevel()) || (Req.ExploreRequirement > m_Player->ExploreLevel()) || (Req.TradeRequirement > m_Player->TradeLevel()) || (Req.OverallRequirement > m_Player->TotalLevel())) { m_Player->SendPriorityMessageString("You can not equip this item","MessageLine",2000,4); //printf("CanEquip - level restriction\n"); //printf("Combat Req %d Act %d\n",Req.CombatRequirement,m_Player->CombatLevel()); //printf("Explore Req %d Act %d\n",Req.CombatRequirement,m_Player->ExploreLevel()); //printf("Trade Req %d Act %d\n",Req.CombatRequirement,m_Player->TradeLevel()); //printf("Overall Req %d Act %d\n",Req.CombatRequirement,m_Player->TotalLevel()); return false; } return true; } bool Equipable::CorrectAmmo(_Item * Ammo) { /* If our current item is empty, cant equip ammo */ if (!m_ItemBase) { return false; } /* If our current item does not use ammo, then we cannot equip any */ if (!m_UsesAmmo) { return false; } /* If we are removing ammo and have ammo to remove, return true */ if (Ammo->ItemTemplateID == -1 && m_AuxAmmoItem->GetItemTemplateID() != -1) { return true; } /* If for some reason the ammo field is null, print an error and exit */ if (m_ItemInstance.WeaponAmmo == 0) { LogMessage("Item ID: [%d] uses ammo but has null ammo field\n", m_AuxEquipItem->GetItemTemplateID()); return false; } ItemBase * newItemBase = g_ItemBaseMgr->GetItem(Ammo->ItemTemplateID); /* If we fail to find an itembase - exit */ if (!newItemBase) { return false; } /* Check to see if this is ammo */ if (newItemBase->SubCategory() != 103) { return false; } /* Now see if the ammo matches */ if (!strstr(newItemBase->Fields(1)->sData, m_ItemInstance.WeaponAmmo)) { m_Player->SendVaMessage("Wrong ammo type. Trying to install '%s'. Weapon takes '%s'", newItemBase->Fields(1)->sData, m_ItemInstance.WeaponAmmo); return false; } return true; } _Item *Equipable::GetItem() { if (m_AuxEquipItem) { return (m_AuxEquipItem->GetItemData()); } else { return (0); } } /* This returns the item thats un-equiped as its possible to unequip ammo and leave launcher */ _Item Equipable::Equip(_Item * NewItem) { /* NOTE: This item has already passed equip checks */ if (NewItem->ItemTemplateID == -2) { return *NewItem; } /* Check to see if we are equiping ammo */ if (CorrectAmmo(NewItem)) { return EquipAmmo(NewItem); } /* Unequip item */ if (NewItem->ItemTemplateID == -1) { RemoveEffects(); SetStats(true); // Remove old Stats _Item OldItem = *m_AuxEquipItem->GetItemData(); m_AuxEquipItem->SetItemData(NewItem); m_ItemBase = 0; m_AmmoBase = 0; m_UsesAmmo = false; /* If this was a weapon, remove the Asset */ if (m_Type == EQUIP_WEAPON) { m_Player->ShipIndex()->Lego.Attachments.Attachment[m_Slot-3].Clear(); } m_Player->SaveEquipmentChange(m_Slot, NewItem); return OldItem; } /* If we have ammo, it gets removed with the launcher */ if (m_UsesAmmo && m_AuxAmmoItem->GetItemTemplateID() > 0) { m_Player->CargoAddItem(m_AuxAmmoItem->GetData()); m_AuxAmmoItem->Clear(); m_AmmoBase = 0; m_Player->SaveAmmoChange(m_Slot, m_AuxAmmoItem->GetData()); } // Make sure we are calculating quality on item m_Player->QualityCalculator(NewItem); ItemBase * NewItemBase = g_ItemBaseMgr->GetItem(NewItem->ItemTemplateID); /* Make sure we have an itembase, if not, return the item back */ if (!NewItemBase) { LogMessage("Could not find ItemBase for ItemID %d\n",NewItem->ItemTemplateID); return *NewItem; } /* Begin equip process */ RemoveEffects(); SetStats(true); // Remove old Stats m_ItemBase = NewItemBase; m_ItemInstance = m_ItemBase->GetItemInstance(NewItem->InstanceInfo); /* If this is a launcher set the Ammo flag */ m_UsesAmmo = (m_ItemBase->SubCategory() == 101 || m_ItemBase->SubCategory() == 102); /* For now, make equip time be 10 seconds for each activatable effect, plus weapon reload time */ unsigned long EquipTime = unsigned long(m_ItemBase->ActivatableCount() * 10000) + unsigned long(m_ItemInstance.WeaponReload * 1000.0f); _Item OldItem = *m_AuxEquipItem->GetItemData(); /* Install this item */ m_AuxEquipItem->SetItemData(NewItem); /* If this was a weapon, set the Asset */ if (m_Type == EQUIP_WEAPON) { m_Player->ShipIndex()->Lego.Attachments.Attachment[m_Slot-3].Clear(); m_Player->ShipIndex()->Lego.Attachments.Attachment[m_Slot-3].SetAsset(m_ItemBase->GameBaseAsset()); m_Player->ShipIndex()->Lego.Attachments.Attachment[m_Slot-3].SetType(2); m_Player->ShipIndex()->Lego.Attachments.Attachment[m_Slot-3].SetBoneName(m_Player->ShipIndex()->Inventory.MountBones.GetMountBoneName(m_Slot)); /* Turn on autofire for this item */ m_AuxEquipItem->SetItemState(m_AuxEquipItem->GetItemState() | ITEM_STATE_AUTO_FIRE_ENABLE); } Install(EquipTime); m_Player->SaveEquipmentChange(m_Slot, NewItem); return OldItem; } _Item Equipable::EquipAmmo(_Item * NewAmmo) { _Item OldAmmo = *m_AuxAmmoItem->GetData(); if (m_AmmoBase = g_ItemBaseMgr->GetItem(NewAmmo->ItemTemplateID)) { m_AmmoInstance = m_AmmoBase->GetAmmoInstance(NewAmmo->InstanceInfo); UpdateRange(); } m_AuxAmmoItem->SetData(NewAmmo); m_Player->SaveAmmoChange(m_Slot, NewAmmo); if (m_AuxAmmoItem->GetItemTemplateID() < 0) { AddItemStateFlag(ITEM_STATE_NO_AMMO); AddItemStateFlag(ITEM_STATE_NO_TARGETING); AddItemStateFlag(ITEM_STATE_DISABLED); } else if (m_AuxEquipItem->GetItemState() & ITEM_STATE_NO_AMMO) { //TODO: Check activation requirements RemoveItemStateFlag(ITEM_STATE_NO_AMMO); RemoveItemStateFlag(ITEM_STATE_NO_TARGETING); /* If the item is read, remove the disabled flag aswell */ if (m_ReadyTime < GetNet7TickCount()) { RemoveItemStateFlag(ITEM_STATE_DISABLED); } } return OldAmmo; } void Equipable::Install(unsigned long InstallDelay) { m_ReadyTime = GetNet7TickCount() + InstallDelay; m_AuxEquipItem->SetReadyTime(m_ReadyTime); /* Disable the item since its being installed */ AddItemStateFlag(ITEM_STATE_DISABLED); /* If item has no equip time, install it immediately */ if (InstallDelay == 0) { FinishInstall(); } else { RemoveTimeNode(); m_TimeNode.player_id = m_Player->GameID(); m_TimeNode.func = B_ITEM_INSTALL; m_TimeNode.i1 = m_Slot; m_Player->m_SectorMgr->AddTimedCallPNode(&m_TimeNode, InstallDelay); //m_TimeNode = m_Player->m_SectorMgr->AddTimedCall(m_Player, B_ITEM_INSTALL, InstallDelay, 0, m_Slot); } } void Equipable::Hack(unsigned long InstallDelay) { } void Equipable::FinishInstall() { /* Remove our time node */ RemoveTimeNode(); /* Set the ItemState */ if (!(m_AuxEquipItem->GetItemState() & ITEM_STATE_NO_AMMO)) { RemoveItemStateFlag(ITEM_STATE_DISABLED); } /* Add the item's stats, if needed */ SetStats(); // only send effects if in space if (m_Player->InSpace()) { AddEffects(); } m_Player->SendAuxShip(); } void Equipable::ManualActivate() { /* If we have no item, exit */ if (m_AuxEquipItem->GetItemTemplateID() < 0) { return; } /* If they use the item with it auto firing, it cancels autofire */ if (m_AuxEquipItem->GetItemState() & ITEM_STATE_AUTO_FIRE) { RemoveItemStateFlag(ITEM_STATE_AUTO_FIRE); m_Player->SendAuxShip(); return; } /* Check if item is ready */ if (!ItemReady()) { return; } Activate(); m_Player->SendAuxShip(); } //TODO: Add checks to activate item when enough energy present void Equipable::AutoActivate() { //LogMessage("Autofire Item: %d\n", m_Slot); Activate(); } void Equipable::Activate() { Object * Target = (0); if (m_Player->m_ObjectMgr) { Target = m_Player->m_ObjectMgr->GetObjectFromID(m_Player->ShipIndex()->GetTargetGameID()); } else { return; } switch (m_Type) { case EQUIP_WEAPON: if (!UseWeapon(Target)) { return; } break; default: return; }; /* Set Ready time and send Aux */ m_AuxEquipItem->SetReadyTime(m_ReadyTime); AddItemStateFlag(ITEM_STATE_DISABLED); if (m_ReadyTime < GetNet7TickCount()) //sometimes cooldown didn't happen because readytime was before current tick { LogMessage("Instant Cooldown\n"); m_Player->m_Equip[m_Slot].CoolDown(); } else { RemoveTimeNode(); m_TimeNode.player_id = m_Player->GameID(); m_TimeNode.func = B_ITEM_COOLDOWN; m_TimeNode.i1 = m_Slot; m_Player->m_SectorMgr->AddTimedCallPNode(&m_TimeNode, m_ReadyTime - GetNet7TickCount()); } } void Equipable::CancelAutofire() { RemoveItemStateFlag(ITEM_STATE_AUTO_FIRE); m_Player->SendAuxShip(); } void Equipable::CoolDown() { //LogMessage("Cooldown Item: %d\n", m_Slot); /* Remove disable flag incase were not autofiring or firing fails */ RemoveItemStateFlag(ITEM_STATE_DISABLED); /* Check if we are autofiring */ if (m_AuxEquipItem->GetItemState() & ITEM_STATE_AUTO_FIRE) { RemoveItemStateFlag(ITEM_STATE_AUTO_FIRE); //FOR NOW AutoActivate(); } m_Player->SendAuxShip(); } bool Equipable::UseWeapon(Object * Target) { bool Ignored = false, OnAction = false; if (m_ItemBase->SubCategory() == 99) // TODO: check for this weapon properly { if (m_Player->FireEnergyCannon(&m_ItemInstance)) { m_ReadyTime = GetNet7TickCount() + unsigned long(m_ItemInstance.WeaponReload * 1000.0f); return true; } else { return false; } } // Make sure we are not warping if (m_Player && m_Player->m_WarpDrive) { m_Player->SendPriorityMessageString("Can not use while in warp!","MessageLine",2000,4); return false; } if (Target && Target->ObjectType() == OT_MOB && !m_Player->ShipIndex()->GetIsIncapacitated()) { if (!CheckRange(Target)) { m_Player->SendPriorityMessageString("Out of range","MessageLine",2000,4); return false; } if (!CheckOrientation(Target)) { m_Player->SendPriorityMessageString("You must face target","MessageLine",2000,4); return false; } if (m_Player->GetEnergyValue() < m_ItemInstance.EnergyUse) { m_Player->SendPriorityMessageString("Not enough energy!","MessageLine",2000,4); return false; } // Make sure we have ammo if ((m_ItemBase->SubCategory() == 101 || m_ItemBase->SubCategory() == 102)) { if (m_AuxAmmoItem->GetItemTemplateID() < 0) { AddItemStateFlag(ITEM_STATE_NO_AMMO); AddItemStateFlag(ITEM_STATE_NO_TARGETING); AddItemStateFlag(ITEM_STATE_DISABLED); m_Player->SendPriorityMessageString("Out of ammo","MessageLine",2000,4); return false; } } //see if we are prospecting, if so, cancel prospect. m_Player->AbortProspecting(false, false); //interrupt any skills that need interrupting if(m_Player->m_CurrentSkill && m_Player->m_CurrentSkill->SkillInterruptable(&Ignored, &Ignored, &OnAction)) { if(OnAction) { m_Player->m_CurrentSkill->InterruptSkillOnAction(SHOOTING); } } /* Use the energy */ m_Player->RemoveEnergy(m_ItemInstance.EnergyUse); /*If autofire is on item, autofire */ if (m_AuxEquipItem->GetItemState() & ITEM_STATE_AUTO_FIRE_ENABLE) { AddItemStateFlag(ITEM_STATE_AUTO_FIRE); } /* Calculate ready time */ unsigned long myTime = GetNet7TickCount(); m_ReadyTime = myTime + unsigned long(m_ItemInstance.WeaponReload * 1000.0f); int AmmoShots = m_ItemBase->Fields(22)->iData; m_Target = m_Player->ShipIndex()->GetTargetGameID(); // Calculate delay long Delay = (int)(m_ItemInstance.WeaponReload/(float)AmmoShots/2.0f) * 1000; if (Delay > 400) { Delay = 400; } // Send out # of ammo used if (m_ItemBase->SubCategory() != 100) { if (AmmoShots > 1) { // Calculate delay ShootAmmo(Target->GameID()); // No delay for first one for(int x=1;xm_SectorMgr->AddTimedCall(m_Player, B_SHOOT_AMMO,(long)(Delay * (float) x), NULL, m_Slot, Target->GameID()); } } else { // No need for timer on single ammo ShootAmmo(Target->GameID()); } } else { // No need for timer on beams ShootAmmo(Target->GameID()); } return true; } else { //LogMessage("Weapon use failed\n"); return false; } } float Equipable::DamageMult(float Damage) { float CalcDamage = 0.0f; // Calculate Damage if (m_ItemBase->SubCategory() == 100) { CalcDamage = (float) (Damage * (1.0 + m_Player->m_Stats.GetStatType(STAT_BEAM_DAMAGE, STAT_BUFF_MULT))); CalcDamage += m_Player->m_Stats.GetStatType(STAT_BEAM_DAMAGE, STAT_BUFF_VALUE); } else if (m_ItemBase->SubCategory() == 101) { CalcDamage = (float) (Damage * (1.0 + m_Player->m_Stats.GetStatType(STAT_PROJECTILES_DAMAGE, STAT_BUFF_MULT))); CalcDamage += m_Player->m_Stats.GetStatType(STAT_PROJECTILES_DAMAGE, STAT_BUFF_VALUE); } else { CalcDamage = (float) (Damage * (1.0 + m_Player->m_Stats.GetStatType(STAT_MISSILE_DAMAGE, STAT_BUFF_MULT))); CalcDamage += m_Player->m_Stats.GetStatType(STAT_MISSILE_DAMAGE, STAT_BUFF_VALUE); } return CalcDamage; } void Equipable::ShootAmmo(int TargetID) { // Make sure all pointers are not null if (!m_ItemBase || !m_Player || !m_Player->m_ObjectMgr) { return; } Object * Target = m_Player->m_ObjectMgr->GetObjectFromID(TargetID); if (!Target) { return; } unsigned long myTime = GetNet7TickCount(); /* Damage delay is 0 for beams and 1sec/k for launchers */ u16 DamageDelay = 0; if (m_ItemBase->SubCategory() == 101) { DamageDelay = u16(Target->RangeFrom(m_Player->Position()) * 1.5f); } else if (m_ItemBase->SubCategory() == 102) { DamageDelay = u16(Target->RangeFrom(m_Player->Position()) * 2.0f); } /* Activate effect */ ObjectToObjectEffect OBTOBE; OBTOBE.Bitmask = 0x07; OBTOBE.GameID = m_Player->GameID(); OBTOBE.TargetID = m_Target; // Use ammo effect if it needs ammo if ((m_ItemBase->SubCategory() == 101 || m_ItemBase->SubCategory() == 102) && m_AmmoBase) { OBTOBE.EffectDescID = m_AmmoBase->UseEffect(); } else { OBTOBE.EffectDescID = m_ItemBase->UseEffect(); } char *message = m_Player->ShipIndex()->Inventory.MountBones.GetMountBoneName(m_Slot); OBTOBE.Message = message; OBTOBE.TimeStamp = myTime; OBTOBE.Duration = DamageDelay; OBTOBE.EffectID = myTime; m_Player->SendObjectToObjectEffectRL(&OBTOBE, true); bool critical = false; float Damage; if (m_ItemBase->SubCategory() == 100) { Damage = m_Player->CalcDamage(m_ItemInstance.WeaponDamage, m_ItemBase->SubCategory(), &critical); Target->DamageMOB(m_Player->GameID(), m_ItemInstance.WeaponDamageType, Damage, critical ? 3 : 0); } // Projectile or missile if (m_ItemBase->SubCategory() == 101 || m_ItemBase->SubCategory() == 102) { Damage = m_Player->CalcDamage(m_AmmoInstance.WeaponDamage, m_ItemBase->SubCategory(), &critical); m_Player->m_SectorMgr->AddTimedCall(0, B_MOB_DAMAGE, DamageDelay, Target, m_Player->GameID(), m_AmmoInstance.WeaponDamageType, critical ? 3 : 0, 0, 0, Damage); u32 StackCount = m_AuxAmmoItem->GetStackCount(); if (StackCount == 1) { if (!Reload()) { AddItemStateFlag(ITEM_STATE_NO_AMMO); AddItemStateFlag(ITEM_STATE_NO_TARGETING); AddItemStateFlag(ITEM_STATE_DISABLED); RemoveItemStateFlag(ITEM_STATE_AUTO_FIRE); m_AuxAmmoItem->Clear(); m_AmmoBase = 0; } } else { m_AuxAmmoItem->SetStackCount(StackCount - 1); } } } /* Checks to see if we are within range of the target */ bool Equipable::CheckRange(Object * Target) { float TargetSize; // Get target size if (Target->BaseAsset() != 0) { TargetSize = g_ServerMgr->m_CBassetList.GetRadius(Target->BaseAsset()) * Target->Scale(); } else { TargetSize = 0.0f; } return (Target->RangeFrom(m_Player->Position()) <= (m_Range + TargetSize)); } /* Checks to see if we have the correct orientation relative to the target */ bool Equipable::CheckOrientation(Object * Target) { if (m_ItemBase->SubCategory() == 100 || m_ItemBase->SubCategory() == 101) { return (fabsf(m_Player->GetAngleTo(Target->Position())) < (PI/4.5f)); } return true; } /* We are out of ammo, reload the launcher */ bool Equipable::Reload() { ItemBase * Ammo = 0; for(u32 i=0; iShipIndex()->Inventory.GetCargoSpace(); i++) { Ammo = g_ItemBaseMgr->GetItem(m_Player->ShipIndex()->Inventory.CargoInv.Item[i].GetItemTemplateID()); /* If this is ammo that matches the launcher */ if (Ammo && strstr(Ammo->Name(), m_ItemInstance.WeaponAmmo)) { m_AuxAmmoItem->SetData(m_Player->ShipIndex()->Inventory.CargoInv.Item[i].GetData()); m_Player->ShipIndex()->Inventory.CargoInv.Item[i].Empty(); return true; } } return false; } //This is for the various effects associated with the item void Equipable::AddEffects() { ItemBase * myItemBase = g_ItemBaseMgr->GetItem(m_AuxEquipItem->GetItemTemplateID()); if (myItemBase && myItemBase->EquipEffect() > 0) { // Add Equip Effect ObjectEffect Effect; Effect.Bitmask = 0x07; Effect.EffectDescID = myItemBase->EquipEffect(); Effect.GameID = m_Player->GameID(); Effect.Duration = 0; Effect.TimeStamp = GetNet7TickCount(); m_EEffectID = m_Player->m_Effects.AddEffect(&Effect); // ---- } /* if (m_ItemBase->m_EffectsEquip > 0) { // Display Equip Effect ObjectEffect OBTOBE; OBTOBE.Bitmask = 0x07; OBTOBE.GameID = m_Player->GameID(); OBTOBE.EffectDescID = m_ItemBase->m_EffectsEquip; OBTOBE.EffectID = GetNet7TickCount(); OBTOBE.TimeStamp = GetNet7TickCount(); OBTOBE.Duration = 1750; if (m_Player->ConnectionAvailable()) { m_Player->Connection()->SendObjectEffect(m_Player->GameID(), &OBTOBE, TRUE); // Sector Wide } } */ } void Equipable::RemoveEffects() { // Remove effect from player if (m_EEffectID > 0) { m_Player->m_Effects.RemoveEffect(m_EEffectID); m_EEffectID = 0; } } void Equipable::UpdateRange() { float Range_Bonus = 1; if (m_Type == EQUIP_WEAPON && m_ItemBase) { switch(m_ItemBase->SubCategory()) { case 100: // Beam Range_Bonus = 1.0f + m_Player->m_Stats.GetStatType(STAT_BEAM_RANGE, STAT_BUFF_MULT); m_Range = m_ItemInstance.WeaponRange * Range_Bonus + m_Player->m_Stats.GetStatType(STAT_BEAM_RANGE, STAT_BUFF_VALUE); break; case 101: // Projectile Range_Bonus = 1.0f + m_Player->m_Stats.GetStatType(STAT_PROJECTILES_RANGE, STAT_BUFF_MULT); m_Range = m_ItemInstance.WeaponRange * Range_Bonus + m_Player->m_Stats.GetStatType(STAT_PROJECTILES_RANGE, STAT_BUFF_VALUE); break; case 102: // Missiles Range_Bonus = 1.0f + m_Player->m_Stats.GetStatType(STAT_MISSILE_RANGE, STAT_BUFF_MULT); m_Range = m_AmmoInstance.WeaponRange * Range_Bonus + m_Player->m_Stats.GetStatType(STAT_MISSILE_RANGE, STAT_BUFF_VALUE); break; } if (Range_Bonus < 1.0f) { Range_Bonus = 1.0f; } //printf("WeaponRange: %f\n", m_Range); m_AuxEquipItem->SetTargetRange(m_Range); } } //This is for item stats that are part of the item void Equipable::SetStats(bool Remove) { int RemoveStat; if (Remove) RemoveStat = -1; else RemoveStat = 1; if (Remove) { for(int ID=0;IDm_Stats.DelStat(m_StatIDs[ID]); } m_MaxID = 0; /* If this is an empty item, disable the slot */ if (m_AuxEquipItem->GetItemTemplateID() < 0) { AddItemStateFlag(ITEM_STATE_DISABLED); } return; } switch(m_Type) { case EQUIP_WEAPON: /* We dont have ammot at this point anyways */ UpdateRange(); /* Removes all of the itemstate flags */ m_AuxEquipItem->SetItemState(m_AuxEquipItem->GetItemState() & 0xFFFFFF00); if (m_UsesAmmo && m_AuxAmmoItem->GetItemTemplateID() < 0) { AddItemStateFlag(ITEM_STATE_NO_AMMO); AddItemStateFlag(ITEM_STATE_NO_TARGETING); } break; case EQUIP_DEVICE: EquipDevice(!Remove); m_AuxEquipItem->SetTargetRange((float)m_ItemInstance.EffectRange); break; case EQUIP_SHIELD: { // Dont remove stats from the Stats YET if (!Remove) { // Set BaseValue Stats m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(STAT_BASE_VALUE, STAT_SHIELD_RECHARGE, m_ItemInstance.ShieldRecharge, "ITEM_VALUE"); m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(STAT_BASE_VALUE, STAT_SHIELD, m_ItemInstance.ShieldCap, "ITEM_VALUE"); // ---- float StartValue = m_Player->GetShield() / (m_Player->m_Stats.GetStat(STAT_SHIELD) / m_Player->ShipIndex()->GetMaxShield()); m_Player->ShipIndex()->SetMaxShield(m_Player->m_Stats.GetStat(STAT_SHIELD)); if (StartValue > 1.0f) { StartValue = 1.0f; } float ChargeRate = (m_Player->m_Stats.GetStat(STAT_SHIELD_RECHARGE) / m_Player->m_Stats.GetStat(STAT_SHIELD)) / 1000.0f; unsigned long EndTime = GetNet7TickCount() + unsigned long((1.0f - StartValue) / ChargeRate); m_Player->ShieldUpdate(EndTime, ChargeRate, StartValue); m_AuxEquipItem->SetTargetRange((float)m_ItemInstance.EffectRange); } } break; case EQUIP_REACTOR: { // Dont remove stats from the Stats YET if (!Remove) { // Set BaseValue Stats m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(STAT_BASE_VALUE, STAT_ENERGY, m_ItemInstance.ReactorCap, "ITEM_VALUE"); m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(STAT_BASE_VALUE, STAT_ENERGY_RECHARGE, m_ItemInstance.ReactorRecharge, "ITEM_VALUE"); // ---- float MaxEnergy = m_Player->m_Stats.GetStat(STAT_ENERGY); float RechargeEnergy = m_Player->m_Stats.GetStat(STAT_ENERGY_RECHARGE); float StartValue = m_Player->GetEnergy() / (MaxEnergy / m_Player->ShipIndex()->GetMaxEnergy()); m_Player->ShipIndex()->SetMaxEnergy(MaxEnergy); if (StartValue > 1.0f) { StartValue = 1.0f; } float ChargeRate = (RechargeEnergy / MaxEnergy) / 1000.0f; unsigned long EndTime = GetNet7TickCount() + unsigned long((1.0f - StartValue) / ChargeRate); m_Player->EnergyUpdate(EndTime, ChargeRate, StartValue); m_AuxEquipItem->SetTargetRange((float)m_ItemInstance.EffectRange); } } break; case EQUIP_ENGINE: float Speed = (float)m_ItemInstance.EngineSpeed; float Acceleration = 100.0f; // Dont remove stats from the Stats YET if (!Remove) { // Set BaseValue Stats m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(STAT_BASE_VALUE, STAT_IMPULSE, Speed, "ITEM_VALUE"); m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(STAT_BASE_VALUE, STAT_WARP, (float)m_ItemInstance.EngineWarpSpeed, "ITEM_VALUE"); m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(STAT_BASE_VALUE, STAT_SIGNATURE, (float)m_ItemInstance.EngineSignature, "ITEM_VALUE"); if (m_Player->m_Stats.GetStat(STAT_ACCELERATION) == 0) { m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(STAT_BASE_VALUE, STAT_ACCELERATION, Acceleration); } m_Player->ShipIndex()->CurrentStats.SetVisibility((s32)m_Player->m_Stats.GetStat(STAT_SIGNATURE)); m_Player->ShipIndex()->CurrentStats.SetSpeed((s32)m_Player->m_Stats.GetStat(STAT_IMPULSE)); m_Player->ShipIndex()->CurrentStats.SetWarpSpeed((s32)m_Player->m_Stats.GetStat(STAT_WARP)); /* Should create a method for this in player */ m_Player->ShipIndex()->SetMaxSpeed(m_Player->m_Stats.GetStat(STAT_IMPULSE)); m_Player->ShipIndex()->SetMinSpeed(-m_Player->m_Stats.GetStat(STAT_IMPULSE)/2); m_Player->ShipIndex()->SetAcceleration(m_Player->m_Stats.GetStat(STAT_ACCELERATION)); m_AuxEquipItem->SetTargetRange((float)m_ItemInstance.EffectRange); } break; } EquipEffects(RemoveStat); /* If this is an empty item, disable the slot */ if (m_AuxEquipItem->GetItemTemplateID() < 0) { AddItemStateFlag(ITEM_STATE_DISABLED); } } void Equipable::EquipEffects(int RemoveStat) { if (!m_ItemBase) return; ItemBaseData * ItemBaseD = m_ItemBase->Data(); if (!ItemBaseD) return; if (ItemBaseD->EquipableEffects.Count > 6) return; bool Remove = (RemoveStat == -1); if (Remove) return; for(int Effect=0;EffectEquipableEffects.Count;Effect++) { ItemEffect * EffectData = m_ItemBase->GetEquipEffect(Effect); // Load all varable data for(int StatID=0;StatID<3;StatID++) { char *StatName = EffectData->VarStats[StatID]; int VarType = EffectData->VarType[StatID]; float VarData = EffectData->DescVar[StatID]; // Add value if it is used if (VarType != 0) { if (VarType == 5) // See if we are using a duration in an eqipable! { // This should not happen! LogMessage("Warning! Buff: %s is Equipable using duration!", EffectData->BuffName); break; } else { // Convert to percentage if (VarType == STAT_BUFF_MULT || VarType == STAT_DEBUFF_MULT) { VarData = VarData / 100.0f; } m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(VarType, StatName, VarData, "ITEM_EFFECTS"); m_Player->m_Stats.UpdateAux(StatName); // Update Aux data } } } // Load all Constant Data for(int StatID=0;StatID<2;StatID++) { char *StatName = EffectData->ConstStats[StatID]; int VarType = EffectData->ConstType[StatID]; float VarData = EffectData->ConstValue[StatID]; // Add value if it is used if (VarType != 0) { if (VarType == 5) // See if we are using a duration in an eqipable! { // This should not happen! LogMessage("Warning! Buff: %s is Equipable using duration!", EffectData->BuffName); break; } // Convert to percentage if (VarType == STAT_BUFF_MULT || VarType == STAT_DEBUFF_MULT) { VarData = VarData / 100.0f; } m_StatIDs[m_MaxID++] = m_Player->m_Stats.SetStat(VarType, StatName, VarData, "ITEM_EFFECTS"); m_Player->m_Stats.UpdateAux(StatName); // Update Aux data } } } } void Equipable::RemoveTimeNode() { if (m_TimeNode.player_id) { m_Player->m_SectorMgr->RemoveTimedCall(&m_TimeNode, true); } } void Equipable::AddItemStateFlag(unsigned long State) { m_AuxEquipItem->SetItemState(m_AuxEquipItem->GetItemState() | State); } void Equipable::RemoveItemStateFlag(unsigned long State) { m_AuxEquipItem->SetItemState(m_AuxEquipItem->GetItemState() & ~State); } bool Equipable::ItemReady() { return (m_TimeNode.player_id <= 0); } float Equipable::GetQuality() { _Item *item = m_AuxEquipItem->GetItemData(); return item->Quality; } void Equipable::EquipDevice(bool equip) { if (m_AuxEquipItem->GetItemTemplateID() == 5081) //grail affinity device { m_Player->SetGrailAffinity(equip); return; } if (!m_ItemBase) return; float neg = equip ? 1.0f : -1.0f; float zero = equip ? 1.0f : 0.0f; char *description = m_ItemBase->Description(); //passive devices if (m_ItemBase->EquipableCount() > 0) { for (int i = 0; i < m_ItemBase->EquipableCount(); ++i) { ItemEffect *ie = m_ItemBase->GetEquipEffect(i); if (ie) { } } } //TODO: remove this mess and use the 'EquipableCount' vartype code above. //this is a nasty hack !! if (description) { //see if this is a sculptor type device if (CheckForItem(description, "increase to your Prospect skill")) { m_Player->ChangeProspectSkill(zero * (float)m_ItemBase->TechLevel() / 2.25f); if (strstr(description, "and scan range")) //scan range too? { m_Player->AddScanSkill(zero * m_ItemBase->TechLevel() * 2000.0f); } } //see if this is a harpy's type device if (CheckForItem(description, "increase to your tractor beam speed")) { m_Player->ChangeTractorBeamSpeed(zero * (float)m_ItemBase->TechLevel() * 0.18f ); } } } //remove this too, once the proper code is being used. bool Equipable::CheckForItem(char *description, char *search) { long skill_count = 0; bool no_duplicate = true; bool retval = false; if (strstr(description, search)) { retval = true; //check to see if there's already an item like this installed for(int i=0;i<6;i++) { _Item *item = m_Player->ShipIndex()->Inventory.EquipInv.EquipItem[9+i].GetItemData(); if (item->ItemTemplateID > 0) { ItemBase *itembase = g_ItemBaseMgr->GetItem(item->ItemTemplateID); //protect against equipping a null item, but how can that happen? if(itembase) { //what have we here? char *item_desc = itembase->Description(); if (strstr(item_desc, search)) { //already equipped this item, don't allow it to affect stats again skill_count++; if (skill_count > 1) { no_duplicate = false; break; } } } } } } return (retval && no_duplicate); }