From cc1d7d54c25c285f821c2737316ae029ee4efe96 Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 24 Jan 2015 22:00:06 -0500 Subject: [PATCH] Implemented mob equipment light sources --- changelog.txt | 8 ++++ common/eq_packet_structs.h | 2 +- common/item.cpp | 29 +++++++++++++++ common/item.h | 2 + zone/bot.cpp | 24 ++++++++++++ zone/bot.h | 1 + zone/client.cpp | 8 +++- zone/client.h | 1 + zone/client_packet.cpp | 13 ++++++- zone/command.cpp | 2 +- zone/corpse.cpp | 75 ++++++++++++++++++++++++++++---------- zone/corpse.h | 2 + zone/entity.cpp | 2 +- zone/loottables.cpp | 9 +++++ zone/merc.cpp | 30 +++++++++++++++ zone/merc.h | 1 + zone/mob.cpp | 42 ++++++++++++++++++++- zone/mob.h | 14 ++++++- zone/npc.cpp | 47 ++++++++++++++++++++++-- zone/npc.h | 1 + zone/pets.cpp | 4 ++ zone/zonedb.cpp | 2 +- zone/zonedump.h | 2 +- 23 files changed, 286 insertions(+), 35 deletions(-) diff --git a/changelog.txt b/changelog.txt index 438d026c9..a0676c143 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,13 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 01/24/2015 == +Uleat: Added equipment light source functionality to all mob derived classes (may still need tweaking...) +Notes: + - In addition to equipment light sources, innate npc type light sources have already been activated + - Valid light source values are 0 thru 15 (values are bitmask checked and limited to 0x0F) + - Not all classes handle equipment light sources the same way due to their equipment/loot list configurations + - Spell (casting?) light sources may be implemented at some point..more information is needed + == 01/22/2015 == Akkadius: Massive Log System overhaul, see: http://wiki.eqemulator.org/p?Logging_System_Overhaul&frm=Main diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 4ca5fa59c..a85e45be4 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -300,7 +300,7 @@ struct Spawn_Struct { /*0036*/ uint8 afk; // 0=no, 1=afk /*0238*/ uint32 guildID; // Current guild /*0242*/ char title[32]; // Title -/*0274*/ uint8 unknown0274; +/*0274*/ uint8 unknown0274; // non-zero prefixes name with '!' /*0275*/ uint8 set_to_0xFF[8]; // ***Placeholder (all ff) /*0283*/ uint8 helm; // Helm texture /*0284*/ uint32 race; // Spawn race diff --git a/common/item.cpp b/common/item.cpp index 3581c74f6..35de0e773 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -990,6 +990,35 @@ int Inventory::GetSlotByItemInst(ItemInst *inst) { return INVALID_INDEX; } +uint8 Inventory::FindHighestLightValue() +{ + uint8 light_value = NOT_USED; + + // NOTE: The client does not recognize augment light sources, applied or otherwise, and should not be parsed + for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { + if ((iter->first < EmuConstants::EQUIPMENT_BEGIN || iter->first > EmuConstants::EQUIPMENT_END) && iter->first != MainPowerSource) { continue; } + auto inst = iter->second; + if (inst == nullptr) { continue; } + auto item = inst->GetItem(); + if (item == nullptr) { continue; } + if (item->Light & 0xF0) { continue; } + if (item->Light > light_value) { light_value = item->Light; } + } + + for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { + if (iter->first < EmuConstants::GENERAL_BEGIN || iter->first > EmuConstants::GENERAL_END) { continue; } + auto inst = iter->second; + if (inst == nullptr) { continue; } + auto item = inst->GetItem(); + if (item == nullptr) { continue; } + if (item->ItemType != ItemTypeMisc && item->ItemType != ItemTypeLight) { continue; } + if (item->Light & 0xF0) { continue; } + if (item->Light > light_value) { light_value = item->Light; } + } + + return light_value; +} + void Inventory::dumpEntireInventory() { dumpWornItems(); diff --git a/common/item.h b/common/item.h index 975650e46..0868e8c5f 100644 --- a/common/item.h +++ b/common/item.h @@ -207,6 +207,8 @@ public: int GetSlotByItemInst(ItemInst *inst); + uint8 FindHighestLightValue(); + void dumpEntireInventory(); void dumpWornItems(); void dumpInventory(); diff --git a/zone/bot.cpp b/zone/bot.cpp index 49ec9574c..f7e6dbc32 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -96,6 +96,8 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm } strcpy(this->name, this->GetCleanName()); + + active_light = spell_light = equip_light = innate_light = NOT_USED; } // This constructor is used when the bot is loaded out of the database @@ -211,6 +213,8 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to if(cur_mana > max_mana) cur_mana = max_mana; cur_end = max_end; + + active_light = spell_light = equip_light = innate_light = NOT_USED; } Bot::~Bot() { @@ -380,6 +384,8 @@ NPCType Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::str BotNPCType.mana_regen = 1; BotNPCType.maxlevel = botLevel; + BotNPCType.light = NOT_USED; // due to the way that bots are coded..this is sent post-spawn + return BotNPCType; } @@ -4115,6 +4121,9 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { // Level the bot to the same level as the bot owner //this->SetLevel(botCharacterOwner->GetLevel()); + UpdateEquipLightValue(); + UpdateActiveLightValue(); + entity_list.AddBot(this, true, true); // Load pet @@ -4178,6 +4187,7 @@ void Bot::RemoveBotItemBySlot(uint32 slotID, std::string *errorMessage) { *errorMessage = std::string(results.ErrorMessage()); m_inv.DeleteItem(slotID); + UpdateEquipLightValue(); } // Retrieves all the inventory records from the database for this bot. @@ -4239,6 +4249,7 @@ void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { } + UpdateEquipLightValue(); } // Returns the inventory record for this bot from the database for the specified equipment slot. @@ -4364,6 +4375,9 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.size = 0; ns->spawn.NPC = 0; // 0=player,1=npc,2=pc corpse,3=npc corpse + UpdateActiveLightValue(); + ns->spawn.light = active_light; + ns->spawn.helm = helmtexture; //0xFF; ns->spawn.equip_chest2 = texture; //0xFF; @@ -5073,6 +5087,10 @@ void Bot::BotAddEquipItem(int slot, uint32 id) { equipment[slot] = id; // npc has more than just material slots. Valid material should mean valid inventory index SendWearChange(materialFromSlot); } + + UpdateEquipLightValue(); + if (UpdateActiveLightValue()) + SendAppearancePacket(AT_Light, GetActiveLightValue()); } } @@ -5087,6 +5105,10 @@ void Bot::BotRemoveEquipItem(int slot) { if(materialFromSlot == MaterialChest) SendWearChange(MaterialArms); } + + UpdateEquipLightValue(); + if (UpdateActiveLightValue()) + SendAppearancePacket(AT_Light, GetActiveLightValue()); } } @@ -8384,6 +8406,8 @@ void Bot::EquipBot(std::string* errorMessage) { return; } } + + UpdateEquipLightValue(); } //// This method is meant to be called by zone or client methods to clean up objects when a client camps, goes LD, zones out or something like that. diff --git a/zone/bot.h b/zone/bot.h index 30850a52c..7dd354358 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -332,6 +332,7 @@ public: void EquipBot(std::string* errorMessage); bool CheckLoreConflict(const Item_Struct* item); uint32 GetEquipmentColor(uint8 material_slot) const; + virtual void UpdateEquipLightValue() { equip_light = m_inv.FindHighestLightValue(); } // Static Class Methods static void SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* errorMessage); diff --git a/zone/client.cpp b/zone/client.cpp index d5fcf3b71..ecd777653 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -75,7 +75,7 @@ Client::Client(EQStreamInterface* ieqs) 0, // size 0.7, // runspeed glm::vec4(), - 0, // light + 0, // light - verified for client innate_light value 0xFF, // texture 0xFF, // helmtexture 0, // ac @@ -302,6 +302,9 @@ Client::Client(EQStreamInterface* ieqs) SavedRaidRestTimer = 0; interrogateinv_flag = false; + + active_light = innate_light; + spell_light = equip_light = NOT_USED; } Client::~Client() { @@ -1795,6 +1798,9 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.runspeed = (gmspeed == 0) ? runspeed : 3.125f; ns->spawn.showhelm = m_pp.showhelm ? 1 : 0; + UpdateEquipLightValue(); + UpdateActiveLightValue(); + ns->spawn.light = active_light; } bool Client::GMHideMe(Client* client) { diff --git a/zone/client.h b/zone/client.h index 45603d2c0..a80580af4 100644 --- a/zone/client.h +++ b/zone/client.h @@ -728,6 +728,7 @@ public: #endif uint32 GetEquipment(uint8 material_slot) const; // returns item id uint32 GetEquipmentColor(uint8 material_slot) const; + virtual void UpdateEquipLightValue() { equip_light = m_inv.FindHighestLightValue(); } inline bool AutoSplitEnabled() { return m_pp.autosplit != 0; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e9dfa1d3a..16417455d 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -731,12 +731,21 @@ void Client::CompleteConnect() entity_list.SendUntargetable(this); int x; - for (x = 0; x < 8; x++) + for (x = 0; x < 8; x++) { SendWearChange(x); + } + // added due to wear change above + UpdateActiveLightValue(); + SendAppearancePacket(AT_Light, GetActiveLightValue()); + Mob *pet = GetPet(); if (pet != nullptr) { - for (x = 0; x < 8; x++) + for (x = 0; x < 8; x++) { pet->SendWearChange(x); + } + // added due to wear change above + pet->UpdateActiveLightValue(); + pet->SendAppearancePacket(AT_Light, pet->GetActiveLightValue()); } entity_list.SendTraders(this); diff --git a/zone/command.cpp b/zone/command.cpp index b0a53f09d..dacbee399 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -4383,7 +4383,7 @@ void command_time(Client *c, const Seperator *sep) zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneMin() ); - c->Message(13, "It is now %s.", timeMessage); + c->Message(13, "It is now %s.", timeMessage); Log.Out(Logs::General, Logs::Zone_Server, "Current Time is: %s", timeMessage); } } diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 6b24a0222..57d4179ed 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -137,6 +137,10 @@ Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std: pc->IsRezzed(rezzed); pc->become_npc = false; + pc->spell_light = pc->innate_light = NOT_USED; + pc->UpdateEquipLightValue(); + //pc->UpdateActiveLightValue(); + safe_delete_array(pcs); return pc; @@ -146,7 +150,7 @@ Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NP // vesuvias - appearence fix : Mob("Unnamed_Corpse","",0,0,in_npc->GetGender(),in_npc->GetRace(),in_npc->GetClass(),BT_Humanoid,//bodytype added in_npc->GetDeity(),in_npc->GetLevel(),in_npc->GetNPCTypeID(),in_npc->GetSize(),0, - in_npc->GetPosition(), 0, in_npc->GetTexture(),in_npc->GetHelmTexture(), + in_npc->GetPosition(), in_npc->GetInnateLightValue(), in_npc->GetTexture(),in_npc->GetHelmTexture(), 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0xff,0,0,0,0,0,0,0,0,0), corpse_decay_timer(in_decaytime), @@ -197,6 +201,10 @@ Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NP allowed_looters[i] = 0; } this->rez_experience = 0; + + UpdateEquipLightValue(); + spell_light = NOT_USED; + UpdateActiveLightValue(); } Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( @@ -214,7 +222,7 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( client->GetSize(), // float in_size, 0, // float in_runspeed, client->GetPosition(), - 0, // uint8 in_light, + 0, // uint8 in_light, - verified for client innate_light value client->GetTexture(), // uint8 in_texture, client->GetHelmTexture(), // uint8 in_helmtexture, 0, // uint16 in_ac, @@ -403,6 +411,10 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( return; } //end "not leaving naked corpses" + UpdateEquipLightValue(); + spell_light = NOT_USED; + UpdateActiveLightValue(); + IsRezzed(false); Save(); } @@ -451,7 +463,7 @@ in_level, in_size, 0, position, -0, +0, // verified for client innate_light value in_texture, in_helmtexture, 0, @@ -522,6 +534,10 @@ in_helmtexture, allowed_looters[i] = 0; } SetPlayerKillItemID(0); + + UpdateEquipLightValue(); + spell_light = NOT_USED; + UpdateActiveLightValue(); } Corpse::~Corpse() { @@ -664,6 +680,8 @@ void Corpse::AddItem(uint32 itemnum, uint16 charges, int16 slot, uint32 aug1, ui item->aug_6=aug6; item->attuned=attuned; itemlist.push_back(item); + + UpdateEquipLightValue(); } ServerLootItem_Struct* Corpse::GetItem(uint16 lootslot, ServerLootItem_Struct** bag_item_data) { @@ -725,25 +743,25 @@ void Corpse::RemoveItem(uint16 lootslot) { } } -void Corpse::RemoveItem(ServerLootItem_Struct* item_data){ - uint8 material; - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* sitem = *cur; - if (sitem == item_data) { - is_corpse_changed = true; - itemlist.erase(cur); +void Corpse::RemoveItem(ServerLootItem_Struct* item_data) +{ + for (auto iter = itemlist.begin(); iter != itemlist.end(); ++iter) { + auto sitem = *iter; + if (sitem != item_data) { continue; } - material = Inventory::CalcMaterialFromSlot(sitem->equip_slot); - if(material != _MaterialInvalid) - SendWearChange(material); + is_corpse_changed = true; + itemlist.erase(iter); - safe_delete(sitem); + uint8 material = Inventory::CalcMaterialFromSlot(sitem->equip_slot); // autos to unsigned char + if (material != _MaterialInvalid) + SendWearChange(material); - return; - } + UpdateEquipLightValue(); + if (UpdateActiveLightValue()) + SendAppearancePacket(AT_Light, GetActiveLightValue()); + + safe_delete(sitem); + return; } } @@ -767,7 +785,7 @@ bool Corpse::IsEmpty() const { if (copper != 0 || silver != 0 || gold != 0 || platinum != 0) return false; - return(itemlist.size() == 0); + return itemlist.empty(); } bool Corpse::Process() { @@ -1267,6 +1285,9 @@ void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.NPC = 3; else ns->spawn.NPC = 2; + + UpdateActiveLightValue(); + ns->spawn.light = active_light; } void Corpse::QueryLoot(Client* to) { @@ -1413,6 +1434,20 @@ uint32 Corpse::GetEquipmentColor(uint8 material_slot) const { return 0; } +void Corpse::UpdateEquipLightValue() +{ + equip_light = NOT_USED; + + for (auto iter = itemlist.begin(); iter != itemlist.end(); ++iter) { + if (((*iter)->equip_slot < EmuConstants::EQUIPMENT_BEGIN || (*iter)->equip_slot > EmuConstants::GENERAL_END) && (*iter)->equip_slot != MainPowerSource) { continue; } + auto item = database.GetItem((*iter)->item_id); + if (item == nullptr) { continue; } + if (item->ItemType != ItemTypeMisc && item->ItemType != ItemTypeLight) { continue; } + if (item->Light & 0xF0) { continue; } + if (item->Light > equip_light) { equip_light = item->Light; } + } +} + void Corpse::AddLooter(Mob* who) { for (int i = 0; i < MAX_LOOTERS; i++) { if (allowed_looters[i] == 0) { diff --git a/zone/corpse.h b/zone/corpse.h index 7583ecfdb..8ef644241 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -125,6 +125,8 @@ class Corpse : public Mob { uint32 GetEquipmentColor(uint8 material_slot) const; inline int GetRezExp() { return rez_experience; } + virtual void UpdateEquipLightValue(); + protected: std::list MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot); diff --git a/zone/entity.cpp b/zone/entity.cpp index 6938f752a..b080cdf3c 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -621,7 +621,7 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue) } else { NewSpawn_Struct *ns = new NewSpawn_Struct; memset(ns, 0, sizeof(NewSpawn_Struct)); - npc->FillSpawnStruct(ns, 0); // Not working on player newspawns, so it's safe to use a ForWho of 0 + npc->FillSpawnStruct(ns, nullptr); // Not working on player newspawns, so it's safe to use a ForWho of 0 AddToSpawnQueue(npc->GetID(), &ns); safe_delete(ns); } diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 39caafcea..e004bbee5 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -174,6 +174,11 @@ void ZoneDatabase::AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* iteml } } } // We either ran out of items or reached our limit. + + npc->UpdateEquipLightValue(); + // no wearchange associated with this function..so, this should not be needed + //if (npc->UpdateActiveLightValue()) + // npc->SendAppearancePacket(AT_Light, npc->GetActiveLightValue()); } //if itemlist is null, just send wear changes @@ -359,6 +364,10 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge entity_list.QueueClients(this, outapp); safe_delete(outapp); } + + UpdateEquipLightValue(); + if (UpdateActiveLightValue()) + SendAppearancePacket(AT_Light, GetActiveLightValue()); } void NPC::AddItem(const Item_Struct* item, uint16 charges, bool equipitem) { diff --git a/zone/merc.cpp b/zone/merc.cpp index 995825687..5e16cd3d8 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -69,6 +69,8 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) size = d->size; CalcBonuses(); + // Class should use npc constructor to set light properties + SetHP(GetMaxHP()); SetMana(GetMaxMana()); SetEndurance(GetMaxEndurance()); @@ -1200,6 +1202,9 @@ void Merc::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.NPC = 1; // 0=player,1=npc,2=pc corpse,3=npc corpse ns->spawn.IsMercenary = 1; + UpdateActiveLightValue(); + ns->spawn.light = active_light; + /* // Wear Slots are not setup for Mercs yet unsigned int i; @@ -4899,10 +4904,35 @@ void Merc::UpdateMercAppearance() { this->SendWearChange(materialFromSlot); } } + + if (UpdateActiveLightValue()) + SendAppearancePacket(AT_Light, GetActiveLightValue()); +} + +void Merc::UpdateEquipLightValue() +{ + equip_light = NOT_USED; + + for (int index = MAIN_BEGIN; index < EmuConstants::EQUIPMENT_SIZE; ++index) { + if (equipment[index] == NOT_USED) { continue; } + auto item = database.GetItem(equipment[index]); + if (item == nullptr) { continue; } + if (item->Light & 0xF0) { continue; } + if (item->Light > equip_light) { equip_light = item->Light; } + } + + for (auto iter = itemlist.begin(); iter != itemlist.end(); ++iter) { + auto item = database.GetItem((*iter)->item_id); + if (item == nullptr) { continue; } + if (item->ItemType != ItemTypeMisc && item->ItemType != ItemTypeLight) { continue; } + if (item->Light & 0xF0) { continue; } + if (item->Light > equip_light) { equip_light = item->Light; } + } } void Merc::AddItem(uint8 slot, uint32 item_id) { equipment[slot] = item_id; + UpdateEquipLightValue(); } bool Merc::Spawn(Client *owner) { diff --git a/zone/merc.h b/zone/merc.h index 3d181a82e..210ef5518 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -140,6 +140,7 @@ public: void UpdateMercInfo(Client *c); void UpdateMercStats(Client *c); void UpdateMercAppearance(); + virtual void UpdateEquipLightValue(); void AddItem(uint8 slot, uint32 item_id); static const char *GetRandomName(); bool Spawn(Client *owner); diff --git a/zone/mob.cpp b/zone/mob.cpp index 750667bbc..c42f26fe4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -149,7 +149,9 @@ Mob::Mob(const char* in_name, if (runspeed < 0 || runspeed > 20) runspeed = 1.25f; - light = in_light; + active_light = innate_light = in_light; + spell_light = equip_light = NOT_USED; + texture = in_texture; helmtexture = in_helmtexture; haircolor = in_haircolor; @@ -898,7 +900,10 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.deity = deity; ns->spawn.animation = 0; ns->spawn.findable = findable?1:0; - ns->spawn.light = light; + + UpdateActiveLightValue(); + ns->spawn.light = active_light; + ns->spawn.showhelm = (helmtexture && helmtexture != 0xFF) ? 1 : 0; ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players @@ -2024,6 +2029,39 @@ void Mob::SetAppearance(EmuAppearance app, bool iIgnoreSelf) { } } +bool Mob::UpdateActiveLightValue() +{ + /* This is old information... + 0 - "None" + 1 - "Candle" + 2 - "Torch" + 3 - "Tiny Glowing Skull" + 4 - "Small Lantern" + 5 - "Stein of Moggok" + 6 - "Large Lantern" + 7 - "Flameless Lantern" + 8 - "Globe of Stars" + 9 - "Light Globe" + 10 - "Lightstone" + 11 - "Greater Lightstone" + 12 - "Fire Beatle Eye" + 13 - "Coldlight" + 14 - "Unknown" + 15 - "Unknown" + */ + + uint8 old_light = (active_light & 0x0F); + active_light = (innate_light & 0x0F); + + if (equip_light > active_light) { active_light = equip_light; } // limiter in property handler + if (spell_light > active_light) { active_light = spell_light; } // limiter in property handler + + if (active_light != old_light) + return true; + + return false; +} + void Mob::ChangeSize(float in_size = 0, bool bNoRestriction) { // Size Code if (!bNoRestriction) diff --git a/zone/mob.h b/zone/mob.h index 5d3783c3d..e2d25a952 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -651,6 +651,15 @@ public: bool IsDestructibleObject() { return destructibleobject; } void SetDestructibleObject(bool in) { destructibleobject = in; } + inline uint8 GetInnateLightValue() { return innate_light; } + inline uint8 GetEquipLightValue() { return equip_light; } + inline uint8 GetSpellLightValue() { return spell_light; } + virtual void UpdateEquipLightValue() { equip_light = NOT_USED; } + inline void SetSpellLightValue(uint8 light_value) { spell_light = (light_value & 0x0F); } + + inline uint8 GetActiveLightValue() { return active_light; } + bool UpdateActiveLightValue(); // returns true if change, false if no change + Mob* GetPet(); void SetPet(Mob* newpet); virtual Mob* GetOwner(); @@ -1058,7 +1067,10 @@ protected: glm::vec4 m_Delta; - uint8 light; + uint8 innate_light; // defined by db field `npc_types`.`light` - where appropriate + uint8 equip_light; // highest value of equipped/carried light-producing items + uint8 spell_light; // set value of any light-producing spell (can be modded to mimic equip_light behavior) + uint8 active_light; // highest value of all light sources float fixedZ; EmuAppearance _appearance; diff --git a/zone/npc.cpp b/zone/npc.cpp index 659e291a1..b9c465195 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -71,7 +71,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if d->size, d->runspeed, position, - d->light, + d->light, // innate_light d->texture, d->helmtexture, d->AC, @@ -352,6 +352,9 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if InitializeBuffSlots(); CalcBonuses(); raid_target = d->raid_target; + + active_light = d->light; + spell_light = equip_light = NOT_USED; } NPC::~NPC() @@ -436,13 +439,19 @@ void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot) { ServerLootItem_Struct* item = *cur; if (item->item_id == item_id && slot <= 0 && quantity <= 0) { itemlist.erase(cur); + UpdateEquipLightValue(); + if (UpdateActiveLightValue()) { SendAppearancePacket(AT_Light, GetActiveLightValue()); } return; } else if (item->item_id == item_id && item->equip_slot == slot && quantity >= 1) { - if (item->charges <= quantity) + if (item->charges <= quantity) { itemlist.erase(cur); - else + UpdateEquipLightValue(); + if (UpdateActiveLightValue()) { SendAppearancePacket(AT_Light, GetActiveLightValue()); } + } + else { item->charges -= quantity; + } return; } } @@ -474,6 +483,9 @@ void NPC::CheckMinMaxLevel(Mob *them) ++cur; } + UpdateEquipLightValue(); + if (UpdateActiveLightValue()) + SendAppearancePacket(AT_Light, GetActiveLightValue()); } void NPC::ClearItemList() { @@ -485,6 +497,10 @@ void NPC::ClearItemList() { safe_delete(item); } itemlist.clear(); + + UpdateEquipLightValue(); + if (UpdateActiveLightValue()) + SendAppearancePacket(AT_Light, GetActiveLightValue()); } void NPC::QueryLoot(Client* to) @@ -702,6 +718,27 @@ uint32 NPC::CountLoot() { return(itemlist.size()); } +void NPC::UpdateEquipLightValue() +{ + equip_light = NOT_USED; + + for (int index = MAIN_BEGIN; index < EmuConstants::EQUIPMENT_SIZE; ++index) { + if (equipment[index] == NOT_USED) { continue; } + auto item = database.GetItem(equipment[index]); + if (item == nullptr) { continue; } + if (item->Light & 0xF0) { continue; } + if (item->Light > equip_light) { equip_light = item->Light; } + } + + for (auto iter = itemlist.begin(); iter != itemlist.end(); ++iter) { + auto item = database.GetItem((*iter)->item_id); + if (item == nullptr) { continue; } + if (item->ItemType != ItemTypeMisc && item->ItemType != ItemTypeLight) { continue; } + if (item->Light & 0xF0) { continue; } + if (item->Light > equip_light) { equip_light = item->Light; } + } +} + void NPC::Depop(bool StartSpawnTimer) { uint16 emoteid = this->GetEmoteID(); if(emoteid != 0) @@ -906,7 +943,7 @@ NPC* NPC::SpawnNPC(const char* spawncommand, const glm::vec4& position, Client* npc_type->npc_id = 0; npc_type->loottable_id = 0; npc_type->texture = atoi(sep.arg[3]); - npc_type->light = 0; + npc_type->light = 0; // spawncommand needs update npc_type->runspeed = 1.25; npc_type->d_melee_texture1 = atoi(sep.arg[7]); npc_type->d_melee_texture2 = atoi(sep.arg[8]); @@ -1804,6 +1841,8 @@ void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) Mob::FillSpawnStruct(ns, ForWho); PetOnSpawn(ns); ns->spawn.is_npc = 1; + UpdateActiveLightValue(); + ns->spawn.light = GetActiveLightValue(); } void NPC::PetOnSpawn(NewSpawn_Struct* ns) diff --git a/zone/npc.h b/zone/npc.h index 0974a7645..e38aa85aa 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -187,6 +187,7 @@ public: void QueryLoot(Client* to); uint32 CountLoot(); inline uint32 GetLoottableID() const { return loottable_id; } + virtual void UpdateEquipLightValue(); inline uint32 GetCopper() const { return copper; } inline uint32 GetSilver() const { return silver; } diff --git a/zone/pets.cpp b/zone/pets.cpp index 980a5ce6f..117b466a7 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -417,6 +417,8 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, } } + npc->UpdateEquipLightValue(); + // finally, override size if one was provided if (in_size > 0.0f) npc->size = in_size; @@ -437,6 +439,8 @@ Pet::Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 po SetOwnerID(owner->GetID()); SetPetSpellID(spell_id); taunting = true; + + // Class should use npc constructor to set light properties } bool ZoneDatabase::GetPetEntry(const char *pet_type, PetRecord *into) { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 26b1e7843..d50a72473 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1920,7 +1920,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { tmpNPCType->no_target_hotkey = atoi(row[88]) == 1 ? true: false; tmpNPCType->raid_target = atoi(row[89]) == 0 ? false: true; tmpNPCType->attack_delay = atoi(row[90]); - tmpNPCType->light = atoi(row[91]); + tmpNPCType->light = (atoi(row[91]) & 0x0F); // If NPC with duplicate NPC id already in table, // free item we attempted to add. diff --git a/zone/zonedump.h b/zone/zonedump.h index 3dd6d4569..de10443cb 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -58,7 +58,7 @@ struct NPCType uint32 alt_currency_type; uint32 adventure_template; uint32 trap_template; - uint8 light; //not loaded from DB + uint8 light; uint32 AC; uint32 Mana; //not loaded from DB uint32 ATK; //not loaded from DB