diff --git a/changelog.txt b/changelog.txt index 1a0be1590..ca32db0d7 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,22 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 12/20/2014 == +Akkadius: Updated #cvs to display RoF2 Client Stream count + +== 12/19/2014 == +Trevius: (RoF2) Fixed Leadership AA Purchasing and Recipe Search by correcting opcodes. +Trevius: Fixed Armor Tinting (players and NPCs) that was broken during a previous update. +Trevius: (RoF2) Fixed Rest Timer, Show Helm Option, Auto-Consent Options, and identified Krono in the PP. +Trevius: Fixed Selling for Alternate Currency Merchants for RoF and RoF2. + +== 12/18/2014 == +Trevius: Finished lining up the RoF2 Player Profile Struct. Zone times are now normal, and everything from the PP is accurate in game now. +Trevius: Fixed zoning after death for RoF2. + +== 12/17/2014 == +demonstar55: Use vectors for route stuff, should be more CPU cache friendly so faster +Secrets: EQStream changes as recommended by a community member in private. + == 12/15/2014 == Trevius: (RoF+) Implemented the 6th Augment Slot for Items. Trevius: Player Corpses now saved attuned settings for Items. diff --git a/common/eq_dictionary.cpp b/common/eq_dictionary.cpp index 37414853e..46c8f4ff1 100644 --- a/common/eq_dictionary.cpp +++ b/common/eq_dictionary.cpp @@ -548,6 +548,7 @@ uint16 EQLimits::InventoryMapSize(int16 map, uint32 version) { }, { // local[MapBank] /*Unknown*/ NOT_USED, +/*62*/ NOT_USED, /*Titanium*/ Titanium::consts::MAP_BANK_SIZE, /*SoF*/ EmuConstants::MAP_BANK_SIZE, /*SoD*/ EmuConstants::MAP_BANK_SIZE, @@ -697,6 +698,7 @@ uint16 EQLimits::InventoryMapSize(int16 map, uint32 version) { }, { // local[MapCorpse] /*Unknown*/ NOT_USED, +/*62*/ NOT_USED, /*Titanium*/ Titanium::consts::MAP_CORPSE_SIZE, /*SoF*/ SoF::consts::MAP_CORPSE_SIZE, /*SoD*/ SoD::consts::MAP_CORPSE_SIZE, @@ -726,6 +728,7 @@ uint16 EQLimits::InventoryMapSize(int16 map, uint32 version) { }, { // local[MapInspect] /*Unknown*/ NOT_USED, +/*62*/ NOT_USED, /*Titanium*/ Titanium::consts::MAP_INSPECT_SIZE, /*SoF*/ SoF::consts::MAP_INSPECT_SIZE, /*SoD*/ SoD::consts::MAP_INSPECT_SIZE, @@ -1003,6 +1006,7 @@ uint64 EQLimits::CursorBitmask(uint32 version) { bool EQLimits::AllowsEmptyBagInBag(uint32 version) { static const bool local[_EmuClientCount] = { /*Unknown*/ false, +/*62*/ false, /*Titanium*/ Titanium::limits::ALLOWS_EMPTY_BAG_IN_BAG, /*SoF*/ SoF::limits::ALLOWS_EMPTY_BAG_IN_BAG, /*SoD*/ SoD::limits::ALLOWS_EMPTY_BAG_IN_BAG, @@ -1023,6 +1027,8 @@ bool EQLimits::AllowsEmptyBagInBag(uint32 version) { bool EQLimits::AllowsClickCastFromBag(uint32 version) { static const bool local[_EmuClientCount] = { /*Unknown*/ false, +/*62*/ false, +/*Titanium*/ Titanium::limits::ALLOWS_CLICK_CAST_FROM_BAG, /*SoF*/ SoF::limits::ALLOWS_CLICK_CAST_FROM_BAG, /*SoD*/ SoD::limits::ALLOWS_CLICK_CAST_FROM_BAG, /*Underfoot*/ Underfoot::limits::ALLOWS_CLICK_CAST_FROM_BAG, @@ -1082,6 +1088,7 @@ uint16 EQLimits::ItemContainerSize(uint32 version) { bool EQLimits::CoinHasWeight(uint32 version) { static const bool local[_EmuClientCount] = { /*Unknown*/ true, +/*62*/ true, /*Titanium*/ Titanium::limits::COIN_HAS_WEIGHT, /*SoF*/ SoF::limits::COIN_HAS_WEIGHT, /*SoD*/ SoD::limits::COIN_HAS_WEIGHT, diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index ed62bb183..cec29178d 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -1380,19 +1380,19 @@ struct PlayerPositionUpdateServer_Struct struct PlayerPositionUpdateClient_Struct { /*0000*/ uint16 spawn_id; -/*0022*/ uint16 sequence; //increments one each packet +/*0002*/ uint16 sequence; //increments one each packet /*0004*/ float y_pos; // y coord /*0008*/ float delta_z; // Change in z -/*0016*/ float delta_x; // Change in x -/*0012*/ float delta_y; // Change in y +/*0012*/ float delta_x; // Change in x +/*0016*/ float delta_y; // Change in y /*0020*/ int32 animation:10, // animation delta_heading:10, // change in heading padding0020:12; // ***Placeholder (mostly 1) /*0024*/ float x_pos; // x coord /*0028*/ float z_pos; // z coord -/*0034*/ uint16 heading:12, // Directional heading +/*0032*/ uint16 heading:12, // Directional heading padding0004:4; // ***Placeholder -/*0032*/ uint8 unknown0006[2]; // ***Placeholder +/*0034*/ uint8 unknown0006[2]; // ***Placeholder /*0036*/ }; diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index 879d57664..594f3b5bc 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -47,7 +47,13 @@ uint16 EQStream::MaxWindowSize=2048; -void EQStream::init() { +void EQStream::init(bool resetSession) { + // we only reset these statistics if it is a 'new' connection + if ( resetSession ) + { + streamactive = false; + sessionAttempts = 0; + } active_users = 0; Session=0; Key=0; @@ -313,18 +319,22 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) } #ifndef COLLECTOR if (GetState()==ESTABLISHED) { - _log(NET__ERROR, _L "Received OP_SessionRequest in ESTABLISHED state (%d)" __L, GetState()); + _log(NET__ERROR, _L "Received OP_SessionRequest in ESTABLISHED state (%d) streamactive (%i) attempt (%i)" __L, GetState(),streamactive,sessionAttempts); - /*RemoveData(); - init(); - State=UNESTABLISHED;*/ - _SendDisconnect(); - SetState(CLOSED); - break; + // client seems to try a max of 30 times (initial+3 retries) then gives up, giving it a few more attempts just in case + // streamactive means we identified the opcode for the stream, we cannot re-establish this connection + if ( streamactive || ( sessionAttempts > MAX_SESSION_RETRIES ) ) + { + _SendDisconnect(); + SetState(CLOSED); + break; + } } #endif //std::cout << "Got OP_SessionRequest" << std::endl; - init(); + sessionAttempts++; + // we set established below, so statistics will not be reset for session attempts/stream active. + init(GetState()!=ESTABLISHED); OutboundQueueClear(); SessionRequest *Request=(SessionRequest *)p->pBuffer; Session=ntohl(Request->Session); diff --git a/common/eq_stream.h b/common/eq_stream.h index ee797021f..bcbb548e9 100644 --- a/common/eq_stream.h +++ b/common/eq_stream.h @@ -49,6 +49,10 @@ class EQProtocolPacket; #define RETRANSMIT_ACKED_PACKETS true #endif +#ifndef MAX_SESSION_RETRIES +#define MAX_SESSION_RETRIES 30 +#endif + #pragma pack(1) struct SessionRequest { uint32 UnknownA; @@ -104,6 +108,9 @@ class EQStream : public EQStreamInterface { uint32 retransmittimer; uint32 retransmittimeout; + uint16 sessionAttempts; + bool streamactive; + //uint32 buffer_len; uint32 Session, Key; @@ -197,9 +204,9 @@ class EQStream : public EQStreamInterface { void _SendDisconnect(); - void init(); + void init(bool resetSession=true); public: - EQStream() { init(); remote_ip = 0; remote_port = 0; State=UNESTABLISHED; StreamType=UnknownStream; compressed=true; encoded=false; app_opcode_size=2; bytes_sent=0; bytes_recv=0; create_time=Timer::GetTimeSeconds(); } + EQStream() { init(); remote_ip = 0; remote_port = 0; State=UNESTABLISHED; StreamType=UnknownStream; compressed=true; encoded=false; app_opcode_size=2; bytes_sent=0; bytes_recv=0; create_time=Timer::GetTimeSeconds(); sessionAttempts = 0; streamactive=false; } EQStream(sockaddr_in addr) { init(); remote_ip=addr.sin_addr.s_addr; remote_port=addr.sin_port; State=UNESTABLISHED; StreamType=UnknownStream; compressed=true; encoded=false; app_opcode_size=2; bytes_sent=0; bytes_recv=0; create_time=Timer::GetTimeSeconds(); } virtual ~EQStream() { RemoveData(); SetState(CLOSED); } void SetMaxLen(uint32 length) { MaxLen=length; } @@ -224,6 +231,9 @@ class EQStream : public EQStreamInterface { void SetLastPacketTime(uint32 t) {LastPacket=t;} void Write(int eq_fd); + // whether or not the stream has been assigned (we passed our stream match) + void SetActive(bool val) { streamactive = val; } + // inline bool IsInUse() { bool flag; MInUse.lock(); flag=(active_users>0); MInUse.unlock(); return flag; } inline void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); } diff --git a/common/eq_stream_ident.cpp b/common/eq_stream_ident.cpp index b60ac28d0..97fdd2c48 100644 --- a/common/eq_stream_ident.cpp +++ b/common/eq_stream_ident.cpp @@ -110,6 +110,9 @@ void EQStreamIdentifier::Process() { _log(NET__IDENTIFY, "Identified stream %s:%d with signature %s", long2ip(r->stream->GetRemoteIP()).c_str(), ntohs(r->stream->GetRemotePort()), p->name.c_str()); + // before we assign the eqstream to an interface, let the stream recognize it is in use and the session should not be reset any further + r->stream->SetActive(true); + //might want to do something less-specific here... some day.. EQStreamInterface *s = new EQStreamProxy(r->stream, p->structs, p->opcodes); m_identified.push(s); diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 1659dbd1c..ad6fb95d6 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -205,7 +205,7 @@ namespace RoF SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); OUT(merchant_entity_id); - eq->slot_id = ServerToRoFSlot(emu->slot_id); + eq->slot_id = ServerToRoFMainInvSlot(emu->slot_id); OUT(charges); OUT(cost); @@ -2045,15 +2045,6 @@ namespace RoF outapp->WriteUInt32(emu->skills[r]); } - // deprecated - // Write zeroes for the rest of the skills - /* - for(uint32 r = 0; r < structs::MAX_PP_SKILL - MAX_PP_SKILL; r++) - { - outapp->WriteUInt32(emu->skills[r]); - } - */ - outapp->WriteUInt32(25); // Unknown count for (uint32 r = 0; r < 25; r++) @@ -2130,18 +2121,6 @@ namespace RoF outapp->WriteUInt32(structs::BUFF_COUNT); - //*000*/ uint8 slotid; // badly named... seems to be 2 for a real buff, 0 otherwise - //*001*/ float unknown004; // Seen 1 for no buff - //*005*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages - //*009*/ uint32 unknown016; - //*013*/ uint8 bard_modifier; - //*014*/ uint32 duration; - //*018*/ uint8 level; - //*019*/ uint32 spellid; - //*023*/ uint32 counters; - //*027*/ uint8 unknown0028[53]; - //*080*/ - for (uint32 r = 0; r < BUFF_COUNT; r++) { float instrument_mod = 0.0f; @@ -3902,7 +3881,7 @@ namespace RoF SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); IN(merchant_entity_id); - emu->slot_id = RoFToServerSlot(eq->slot_id); + emu->slot_id = RoFToServerMainInvSlot(eq->slot_id); IN(charges); IN(cost); @@ -3915,7 +3894,7 @@ namespace RoF SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); IN(merchant_entity_id); - emu->slot_id = RoFToServerSlot(eq->slot_id); + emu->slot_id = RoFToServerMainInvSlot(eq->slot_id); FINISH_DIRECT_DECODE(); } @@ -4878,7 +4857,8 @@ namespace RoF uint16 ornaIcon = 0; int32 heroModel = 0; /* - if (inst->GetOrnamentationAug(ornamentationAugtype)) { + if (inst->GetOrnamentationAug(ornamentationAugtype)) + { const Item_Struct *aug_weap = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); //Mainhand ss.write(aug_weap->IDFile, strlen(aug_weap->IDFile)); @@ -5081,11 +5061,6 @@ namespace RoF isbs.augslots[x].unknown = item->AugSlotUnk2[x]; } - // Increased to 6 max aug slots - //isbs.augslots[5].type = 0; - //isbs.augslots[5].visible = 1; - //isbs.augslots[5].unknown = 0; - isbs.ldonpoint_type = item->PointType; isbs.ldontheme = item->LDoNTheme; isbs.ldonprice = item->LDoNPrice; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index fbbceda2c..a750d2abb 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -205,7 +205,7 @@ namespace RoF2 SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); OUT(merchant_entity_id); - eq->slot_id = ServerToRoF2Slot(emu->slot_id); + eq->slot_id = ServerToRoF2MainInvSlot(emu->slot_id); OUT(charges); OUT(cost); @@ -1696,6 +1696,8 @@ namespace RoF2 eq->unknown932 = -1; // Set from PoK Example eq->unknown936 = -1; // Set from PoK Example eq->unknown944 = 1.0; // Set from PoK Example + eq->unknown948 = 0; // New on Live as of Dec 15 2014 + eq->unknown952 = 100; // New on Live as of Dec 15 2014 FINISH_ENCODE(); } @@ -2121,18 +2123,6 @@ namespace RoF2 outapp->WriteUInt32(structs::BUFF_COUNT); - //*000*/ uint8 slotid; // badly named... seems to be 2 for a real buff, 0 otherwise - //*001*/ float unknown004; // Seen 1 for no buff - //*005*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages - //*009*/ uint32 unknown016; - //*013*/ uint8 bard_modifier; - //*014*/ uint32 duration; - //*018*/ uint8 level; - //*019*/ uint32 spellid; - //*023*/ uint32 counters; - //*027*/ uint8 unknown0028[53]; - //*080*/ - for (uint32 r = 0; r < BUFF_COUNT; r++) { float instrument_mod = 0.0f; @@ -2172,7 +2162,6 @@ namespace RoF2 // 80 bytes of zeroes for (uint32 j = 0; j < 20; ++j) outapp->WriteUInt32(0); - } outapp->WriteUInt32(emu->platinum); @@ -2197,8 +2186,8 @@ namespace RoF2 outapp->WriteUInt32(emu->aapoints_spent); - outapp->WriteUInt32(5); // AA Points count ?? - outapp->WriteUInt32(1234); // AA Points assigned + outapp->WriteUInt32(5); // AA Window Tab Count + outapp->WriteUInt32(0); // AA Points assigned ? outapp->WriteUInt32(0); // AA Points in General ? outapp->WriteUInt32(0); // AA Points in Class ? outapp->WriteUInt32(0); // AA Points in Archetype ? @@ -2326,36 +2315,31 @@ namespace RoF2 outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(emu->gm); outapp->WriteUInt32(emu->guild_id); - outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. - outapp->WriteUInt32(0); // Unknown - observed 1 in a live packet. - outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt8(0); // Unknown outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt64(emu->exp); - outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt64(emu->exp); // int32 in client + + outapp->WriteUInt8(0); // Unknown - Seen 5 on Live outapp->WriteUInt32(emu->platinum_bank); outapp->WriteUInt32(emu->gold_bank); outapp->WriteUInt32(emu->silver_bank); outapp->WriteUInt32(emu->copper_bank); - // Commenting out for RoF Test outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(42); // The meaning of life ? - - for (uint32 r = 0; r < 42; r++) - { - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - } - - outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown + outapp->WriteSInt32(-1); // Unknown + outapp->WriteSInt32(-1); // Unknown + outapp->WriteUInt32(emu->career_tribute_points); outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(emu->tribute_points); @@ -2386,18 +2370,12 @@ namespace RoF2 outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown - - /* - - // Begin RoF2 Test - for (uint32 r = 0; r < 1000; r++) + + for (uint32 r = 0; r < 125; r++) + { outapp->WriteUInt8(0); // Unknown - // End RoF2 Test - - // Block of 121 unknown bytes - for (uint32 r = 0; r < 121; r++) - outapp->WriteUInt8(0); // Unknown - + } + outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(emu->currentRadCrystals); @@ -2411,7 +2389,9 @@ namespace RoF2 // Unknown String ? outapp->WriteUInt32(64); // Unknown for (uint32 r = 0; r < 64; r++) + { outapp->WriteUInt8(0); // Unknown + } outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown @@ -2439,22 +2419,30 @@ namespace RoF2 // Unknown String ? outapp->WriteUInt32(64); // Unknown for (uint32 r = 0; r < 64; r++) + { outapp->WriteUInt8(0); // Unknown + } // Unknown String ? outapp->WriteUInt32(64); // Unknown for (uint32 r = 0; r < 64; r++) + { outapp->WriteUInt8(0); // Unknown + } outapp->WriteUInt32(0); // Unknown // Block of 320 unknown bytes for (uint32 r = 0; r < 320; r++) + { outapp->WriteUInt8(0); // Unknown + } // Block of 343 unknown bytes for (uint32 r = 0; r < 343; r++) + { outapp->WriteUInt8(0); // Unknown + } outapp->WriteUInt32(0); // Unknown @@ -2479,10 +2467,14 @@ namespace RoF2 outapp->WriteUInt32(64); // Group of 64 int32s follow Group/Raid Leadership abilities ? for (uint32 r = 0; r < MAX_LEADERSHIP_AA_ARRAY; r++) + { outapp->WriteUInt32(emu->leader_abilities.ranks[r]); + } for (uint32 r = 0; r < 64 - MAX_LEADERSHIP_AA_ARRAY; r++) + { outapp->WriteUInt32(0); // Unused/unsupported Leadership abilities + } outapp->WriteUInt32(emu->air_remaining); // ? @@ -2535,33 +2527,31 @@ namespace RoF2 outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown - */ + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Krono - itemid 88888 Hard coded in client? outapp->WriteUInt8(emu->groupAutoconsent); outapp->WriteUInt8(emu->raidAutoconsent); outapp->WriteUInt8(emu->guildAutoconsent); - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(emu->level); // Level3 ? + outapp->WriteUInt32(emu->level); // Level3 ? outapp->WriteUInt8(emu->showhelm); outapp->WriteUInt32(emu->RestTimer); outapp->WriteUInt32(1024); // Unknown Count - // Block of 1024 unknown bytes - outapp->WriteUInt8(31); // Unknown - - for (uint32 r = 0; r < 1023; r++) + for (uint32 r = 0; r < 1024; r++) + { outapp->WriteUInt8(0); // Unknown + } outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown // Think we need 1 byte of padding at the end - outapp->WriteUInt8(0); // Unknown _log(NET__STRUCTS, "Player Profile Packet is %i bytes", outapp->GetWritePosition()); @@ -3887,6 +3877,7 @@ namespace RoF2 } // DECODE methods + DECODE(OP_AdventureMerchantSell) { DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); @@ -3906,7 +3897,7 @@ namespace RoF2 SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); IN(merchant_entity_id); - emu->slot_id = RoF2ToServerSlot(eq->slot_id); + emu->slot_id = RoF2ToServerMainInvSlot(eq->slot_id); IN(charges); IN(cost); @@ -3919,7 +3910,7 @@ namespace RoF2 SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); IN(merchant_entity_id); - emu->slot_id = RoF2ToServerSlot(eq->slot_id); + emu->slot_id = RoF2ToServerMainInvSlot(eq->slot_id); FINISH_DIRECT_DECODE(); } @@ -4860,8 +4851,8 @@ namespace RoF2 hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; - hdr.unknown048 = 7300 + Inventory::CalcMaterialFromSlot(slot_id_in); //0; - hdr.unknown052 = 7300 + Inventory::CalcMaterialFromSlot(slot_id_in); //0; + hdr.unknown048 = 0; + hdr.unknown052 = 0; hdr.isEvolving = item->EvolvingLevel > 0 ? 1 : 0; ss.write((const char*)&hdr, sizeof(RoF2::structs::ItemSerializationHeader)); @@ -5085,11 +5076,6 @@ namespace RoF2 isbs.augslots[x].unknown = item->AugSlotUnk2[x]; } - // Increased to 6 max aug slots - //isbs.augslots[5].type = 0; - //isbs.augslots[5].visible = 1; - //isbs.augslots[5].unknown = 0; - isbs.ldonpoint_type = item->PointType; isbs.ldontheme = item->LDoNTheme; isbs.ldonprice = item->LDoNPrice; @@ -5325,13 +5311,8 @@ namespace RoF2 iqbs.SpellDmg = item->SpellDmg; iqbs.clairvoyance = item->Clairvoyance; iqbs.unknown28 = 0; - - - // Begin RoF2 Test - iqbs.unknown_TEST1 = 0; - // End RoF2 Test - iqbs.unknown30 = 0; + iqbs.unknown37a = 0; iqbs.unknown39 = 1; iqbs.subitem_count = 0; diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index fbdd20575..19519212a 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -4,7 +4,6 @@ // incoming packets that require a DECODE translation: // Begin RoF2 Decodes - // End RoF2 Encodes/Decodes // These require Encodes/Decodes for RoF, so they do for RoF2 as well diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 90ea4f287..6776ff0e7 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -520,72 +520,73 @@ struct ServerZoneEntry_Struct //Adjusted from SEQ Everquest.h Struct //New Zone Struct - Size: 948 struct NewZone_Struct { -/*0000*/ char char_name[64]; // Character Name -/*0064*/ char zone_short_name[32]; // Zone Short Name -/*0096*/ char unknown0096[96]; -/*0192*/ char zone_long_name[278]; // Zone Long Name -/*0470*/ uint8 ztype; // Zone type (usually FF) -/*0471*/ uint8 fog_red[4]; // Zone fog (red) -/*0475*/ uint8 fog_green[4]; // Zone fog (green) -/*0479*/ uint8 fog_blue[4]; // Zone fog (blue) -/*0483*/ uint8 unknown323; -/*0484*/ float fog_minclip[4]; -/*0500*/ float fog_maxclip[4]; -/*0516*/ float gravity; -/*0520*/ uint8 time_type; -/*0521*/ uint8 rain_chance[4]; -/*0525*/ uint8 rain_duration[4]; -/*0529*/ uint8 snow_chance[4]; -/*0533*/ uint8 snow_duration[4]; -/*0537*/ uint8 unknown537[33]; -/*0570*/ uint8 sky; // Sky Type -/*0571*/ uint8 unknown571[13]; // ***Placeholder -/*0584*/ float zone_exp_multiplier; // Experience Multiplier -/*0588*/ float safe_y; // Zone Safe Y -/*0592*/ float safe_x; // Zone Safe X -/*0596*/ float safe_z; // Zone Safe Z -/*0600*/ float min_z; // Guessed - NEW - Seen 0 -/*0604*/ float max_z; // Guessed -/*0608*/ float underworld; // Underworld, min z (Not Sure?) -/*0612*/ float minclip; // Minimum View Distance -/*0616*/ float maxclip; // Maximum View DIstance -/*0620*/ uint8 unknown620[84]; // ***Placeholder -/*0704*/ char zone_short_name2[96]; //zone file name? excludes instance number which can be in previous version. -/*0800*/ int32 unknown800; //seen -1 -/*0804*/ char unknown804[40]; // -/*0844*/ int32 unknown844; //seen 600 -/*0848*/ int32 unknown848; -/*0852*/ uint16 zone_id; -/*0854*/ uint16 zone_instance; -/*0856*/ char unknown856[20]; -/*0876*/ uint32 SuspendBuffs; -/*0880*/ uint32 unknown880; // Seen 50 -/*0884*/ uint32 unknown884; // Seen 10 -/*0888*/ uint8 unknown888; // Seen 1 -/*0889*/ uint8 unknown889; // Seen 0 (POK) or 1 (rujj) -/*0890*/ uint8 unknown890; // Seen 1 -/*0891*/ uint8 unknown891; // Seen 0 -/*0892*/ uint8 unknown892; // Seen 0 -/*0893*/ uint8 unknown893; // Seen 0 - 00 -/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off -/*0895*/ uint8 unknown895; // Seen 0 - 00 -/*0896*/ uint32 unknown896; // Seen 180 -/*0900*/ uint32 unknown900; // Seen 180 -/*0904*/ uint32 unknown904; // Seen 180 -/*0908*/ uint32 unknown908; // Seen 2 -/*0912*/ uint32 unknown912; // Seen 2 -/*0916*/ float FogDensity; // Most zones have this set to 0.33 Blightfire had 0.16 -/*0920*/ uint32 unknown920; // Seen 0 -/*0924*/ uint32 unknown924; // Seen 0 -/*0928*/ uint32 unknown928; // Seen 0 -/*0932*/ int32 unknown932; // Seen -1 -/*0936*/ int32 unknown936; // Seen -1 -/*0940*/ uint32 unknown940; // Seen 0 -/*0944*/ float unknown944; // Seen 1.0 -/*0948*/ + /*0000*/ char char_name[64]; // Character Name + /*0064*/ char zone_short_name[32]; // Zone Short Name + /*0096*/ char unknown0096[96]; + /*0192*/ char zone_long_name[278]; // Zone Long Name + /*0470*/ uint8 ztype; // Zone type (usually FF) + /*0471*/ uint8 fog_red[4]; // Zone fog (red) + /*0475*/ uint8 fog_green[4]; // Zone fog (green) + /*0479*/ uint8 fog_blue[4]; // Zone fog (blue) + /*0483*/ uint8 unknown323; + /*0484*/ float fog_minclip[4]; + /*0500*/ float fog_maxclip[4]; + /*0516*/ float gravity; + /*0520*/ uint8 time_type; + /*0521*/ uint8 rain_chance[4]; + /*0525*/ uint8 rain_duration[4]; + /*0529*/ uint8 snow_chance[4]; + /*0533*/ uint8 snow_duration[4]; + /*0537*/ uint8 unknown537[33]; + /*0570*/ uint8 sky; // Sky Type + /*0571*/ uint8 unknown571[13]; // ***Placeholder + /*0584*/ float zone_exp_multiplier; // Experience Multiplier + /*0588*/ float safe_y; // Zone Safe Y + /*0592*/ float safe_x; // Zone Safe X + /*0596*/ float safe_z; // Zone Safe Z + /*0600*/ float min_z; // Guessed - NEW - Seen 0 + /*0604*/ float max_z; // Guessed + /*0608*/ float underworld; // Underworld, min z (Not Sure?) + /*0612*/ float minclip; // Minimum View Distance + /*0616*/ float maxclip; // Maximum View DIstance + /*0620*/ uint8 unknown620[84]; // ***Placeholder + /*0704*/ char zone_short_name2[96]; //zone file name? excludes instance number which can be in previous version. + /*0800*/ int32 unknown800; //seen -1 + /*0804*/ char unknown804[40]; // + /*0844*/ int32 unknown844; //seen 600 + /*0848*/ int32 unknown848; + /*0852*/ uint16 zone_id; + /*0854*/ uint16 zone_instance; + /*0856*/ char unknown856[20]; + /*0876*/ uint32 SuspendBuffs; + /*0880*/ uint32 unknown880; // Seen 50 + /*0884*/ uint32 unknown884; // Seen 10 + /*0888*/ uint8 unknown888; // Seen 1 + /*0889*/ uint8 unknown889; // Seen 0 (POK) or 1 (rujj) + /*0890*/ uint8 unknown890; // Seen 1 + /*0891*/ uint8 unknown891; // Seen 0 + /*0892*/ uint8 unknown892; // Seen 0 + /*0893*/ uint8 unknown893; // Seen 0 - 00 + /*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off + /*0895*/ uint8 unknown895; // Seen 0 - 00 + /*0896*/ uint32 unknown896; // Seen 180 + /*0900*/ uint32 unknown900; // Seen 180 + /*0904*/ uint32 unknown904; // Seen 180 + /*0908*/ uint32 unknown908; // Seen 2 + /*0912*/ uint32 unknown912; // Seen 2 + /*0916*/ float FogDensity; // Most zones have this set to 0.33 Blightfire had 0.16 + /*0920*/ uint32 unknown920; // Seen 0 + /*0924*/ uint32 unknown924; // Seen 0 + /*0928*/ uint32 unknown928; // Seen 0 + /*0932*/ int32 unknown932; // Seen -1 + /*0936*/ int32 unknown936; // Seen -1 + /*0940*/ uint32 unknown940; // Seen 0 + /*0944*/ float unknown944; // Seen 1.0 + /*0948*/ uint32 unknown948; // Seen 0 - New on Live as of Dec 15 2014 + /*0952*/ uint32 unknown952; // Seen 100 - New on Live as of Dec 15 2014 + /*0956*/ }; - /* ** Memorize Spell Struct ** Length: 16 Bytes @@ -1598,23 +1599,24 @@ struct RespawnWindow_Struct { */ struct PlayerPositionUpdateServer_Struct { - uint16 spawn_id; - uint16 spawnId2; - signed padding0004:12; - signed y_pos:19; // y coord - unsigned padding:1; - signed delta_z:13; // change in z - signed delta_x:13; // change in x - signed padding0008:6; - signed x_pos:19; // x coord - unsigned heading:12; // heading - signed padding0016:1; - signed delta_heading:10; // change in heading - signed z_pos:19; // z coord - signed padding0020:3; - signed animation:10; // animation - signed delta_y:13; // change in y - signed padding0024:9; + /*0000*/ uint16 spawn_id; + /*0002*/ uint16 spawnId2; + /*0004*/ signed padding0004 : 12; + signed y_pos : 19; // y coord + unsigned padding : 1; + /*0008*/ signed delta_z : 13; // change in z + signed delta_x : 13; // change in x + signed padding0008 : 6; + /*0012*/ signed x_pos : 19; // x coord + unsigned heading : 12; // heading + signed padding0016 : 1; + /*0016*/ signed delta_heading : 10; // change in heading + signed z_pos : 19; // z coord + signed padding0020 : 3; + /*0020*/ signed animation : 10; // animation + signed delta_y : 13; // change in y + signed padding0024 : 9; + /*0024*/ }; /* @@ -1625,21 +1627,22 @@ struct PlayerPositionUpdateServer_Struct */ struct PlayerPositionUpdateClient_Struct { - uint16 sequence; // increments one each packet - Verified - uint16 spawn_id; // Player's spawn id - uint8 unknown0004[6]; // ***Placeholder - float delta_x; // Change in x - unsigned heading:12; // Directional heading - unsigned padding0040:20; // ***Placeholder - float x_pos; // x coord (2nd loc value) - float delta_z; // Change in z - float z_pos; // z coord (3rd loc value) - float y_pos; // y coord (1st loc value) - unsigned animation:10; // ***Placeholder - unsigned padding0024:22; // animation - float delta_y; // Change in y - signed delta_heading:10; // change in heading - unsigned padding0041:22; // ***Placeholder + /*0000*/ uint16 sequence; // increments one each packet - Verified + /*0002*/ uint16 spawn_id; // Player's spawn id + /*0004*/ uint8 unknown0004[6]; // ***Placeholder + /*0010*/ float delta_x; // Change in x + /*0014*/ unsigned heading : 12; // Directional heading + unsigned padding0040 : 20; // ***Placeholder + /*0018*/ float x_pos; // x coord (2nd loc value) + /*0022*/ float delta_z; // Change in z + /*0026*/ float z_pos; // z coord (3rd loc value) + /*0030*/ float y_pos; // y coord (1st loc value) + /*0034*/ unsigned animation : 10; // ***Placeholder + unsigned padding0024 : 22; // animation + /*0038*/ float delta_y; // Change in y + /*0042*/ signed delta_heading : 10; // change in heading + unsigned padding0041 : 22; // ***Placeholder + /*0046*/ }; /* @@ -2177,8 +2180,8 @@ struct AltCurrencyUpdate_Struct { //When an item is selected while the alt currency merchant window is open struct AltCurrencySelectItem_Struct { /*000*/ uint32 merchant_entity_id; +/*004*/ MainInvItemSlotStruct slot_id; /*004*/ //uint32 slot_id; - ItemSlotStruct slot_id; /*008*/ uint32 unknown008; /*012*/ uint32 unknown012; /*016*/ uint32 unknown016; @@ -2235,10 +2238,10 @@ struct AltCurrencyReclaim_Struct { struct AltCurrencySellItem_Struct { /*000*/ uint32 merchant_entity_id; +/*004*/ MainInvItemSlotStruct slot_id; /*004*/ //uint32 slot_id; - ItemSlotStruct slot_id; -/*008*/ uint32 charges; -/*012*/ uint32 cost; +/*016*/ uint32 charges; +/*020*/ uint32 cost; }; struct Adventure_Purchase_Struct { @@ -2258,14 +2261,14 @@ struct Adventure_Sell_Struct { }; struct AdventurePoints_Update_Struct { -/*000*/ uint32 ldon_available_points; // Total available points -/*004*/ uint8 unkown_apu004[20]; -/*024*/ uint32 ldon_guk_points; // Earned Deepest Guk points -/*028*/ uint32 ldon_mirugal_points; // Earned Mirugal' Mebagerie points -/*032*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points -/*036*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points -/*040*/ uint32 ldon_takish_points; // Earned Takish points -/*044*/ uint8 unknown_apu042[216]; +/*000*/ uint32 ldon_available_points; // Total available points +/*004*/ uint8 unkown_apu004[20]; +/*024*/ uint32 ldon_guk_points; // Earned Deepest Guk points +/*028*/ uint32 ldon_mirugal_points; // Earned Mirugal' Mebagerie points +/*032*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points +/*036*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points +/*040*/ uint32 ldon_takish_points; // Earned Takish points +/*044*/ uint8 unknown_apu042[216]; }; @@ -4687,11 +4690,7 @@ struct ItemQuaternaryBodyStruct uint32 unknown37; uint32 unknown_RoF27; uint32 unknown_RoF28; - - // Begin RoF2 Test - uint8 unknown_TEST1; - // End RoF2 Test - + uint8 unknown37a; // (guessed position) New to RoF2 uint8 unknown38; // 0 uint8 unknown39; // 1 uint32 subitem_count; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index a7b494d0d..c84a670a9 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -2174,8 +2174,7 @@ struct AltCurrencyUpdate_Struct { //When an item is selected while the alt currency merchant window is open struct AltCurrencySelectItem_Struct { /*000*/ uint32 merchant_entity_id; -/*004*/ //uint32 slot_id; - ItemSlotStruct slot_id; +/*004*/ MainInvItemSlotStruct slot_id; /*008*/ uint32 unknown008; /*012*/ uint32 unknown012; /*016*/ uint32 unknown016; @@ -2232,8 +2231,7 @@ struct AltCurrencyReclaim_Struct { struct AltCurrencySellItem_Struct { /*000*/ uint32 merchant_entity_id; -/*004*/ //uint32 slot_id; - ItemSlotStruct slot_id; +/*004*/ MainInvItemSlotStruct slot_id; /*008*/ uint32 charges; /*012*/ uint32 cost; }; diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index 378665db1..3a63a8c0f 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -4365,8 +4365,8 @@ struct AltCurrencySelectItem_Struct { struct AltCurrencySellItem_Struct { /*000*/ uint32 merchant_entity_id; /*004*/ uint32 slot_id; -/*006*/ uint32 charges; -/*010*/ uint32 cost; +/*008*/ uint32 charges; +/*012*/ uint32 cost; }; struct AltCurrencyPopulateEntry_Struct diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 4065a3708..81dcb1b0a 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1234,6 +1234,9 @@ ItemInst* SharedDatabase::CreateBaseItem(const Item_Struct* item, int16 charges) // set it to 1 charge so that it is usable on creation if (charges == 0 && item->MaxCharges == -1) charges = 1; + // Stackable items need a minimum charge of 1 to remain moveable. + if(charges <= 0 && item->Stackable) + charges = 1; inst = new ItemInst(item, charges); diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index d2ed7f651..60b94e87d 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -113,7 +113,7 @@ OP_ClientUpdate=0x7dfc OP_ClientReady=0x345d OP_SetServerFilter=0x444d -# Guild Opcodes - Disabled until crashes are resolved in RoF +# Guild Opcodes OP_GetGuildMOTD=0x36e0 OP_GetGuildMOTDReply=0x4f1f OP_GuildMemberUpdate=0x69b9 @@ -121,7 +121,6 @@ OP_GuildInvite=0x7099 OP_GuildRemove=0x1444 OP_GuildPeace=0x67e3 OP_SetGuildMOTD=0x0b0b -OP_GuildList=0x6279 OP_GuildWar=0x1ffb OP_GuildLeader=0x7e09 OP_GuildDelete=0x3708 @@ -212,7 +211,7 @@ OP_ChannelMessage=0x2b2d OP_Assist=0x4478 OP_AssistGroup=0x27f8 OP_MoveCoin=0x0bcf -OP_ZonePlayerToBind=0x0ecb +OP_ZonePlayerToBind=0x08d8 OP_KeyRing=0x6857 OP_WhoAllRequest=0x674b OP_WhoAllResponse=0x578c @@ -257,9 +256,9 @@ OP_MoveDoor=0x08e8 OP_RemoveAllDoors=0x700c OP_EnvDamage=0x51fd OP_BoardBoat=0x4211 -OP_Forage=0x5306 OP_LeaveBoat=0x7617 OP_ControlBoat=0x0ae7 +OP_Forage=0x5306 OP_SafeFallSuccess=0x2219 OP_RezzComplete=0x760d OP_RezzRequest=0x3c21 @@ -287,16 +286,16 @@ OP_ReadBook=0x72df OP_Dye=0x23b9 OP_InterruptCast=0x048c OP_AAAction=0x424e -OP_LeadershipExpToggle=0x6c55 +OP_LeadershipExpToggle=0x74bd OP_LeadershipExpUpdate=0x2797 -OP_PurchaseLeadershipAA=0x0026 -OP_UpdateLeadershipAA=0x026 -OP_MarkNPC=0x5a58 -OP_MarkRaidNPC=0x74bd #unimplemented +OP_PurchaseLeadershipAA=0x6c55 +OP_UpdateLeadershipAA=0x0026 +OP_MarkNPC=0x1fb5 +OP_MarkRaidNPC=0x5a58 #unimplemented OP_ClearNPCMarks=0x2003 OP_ClearRaidNPCMarks=0x20d3 #unimplemented -OP_DelegateAbility=0x76b8 -OP_SetGroupTarget=0x2814 +OP_DelegateAbility=0x4c9d +OP_SetGroupTarget=0x026 OP_Charm=0x5d92 OP_Stun=0x36a4 OP_SendFindableNPCs=0x4613 @@ -318,7 +317,7 @@ OP_PVPLeaderBoardReply=0x071f OP_PVPLeaderBoardDetailsRequest=0x3707 OP_PVPLeaderBoardDetailsReply=0x25b7 OP_RestState=0x000f -OP_RespawnWindow=0x28bc +OP_RespawnWindow=0x0ecb OP_LDoNButton=0x5327 OP_SetStartCity=0x6326 OP_VoiceMacroIn=0x17fd @@ -367,7 +366,7 @@ OP_DzExpeditionInfo=0x4f7e OP_DzExpeditionList=0x9119 OP_DzMemberStatus=0xb2e3 OP_DzLeaderStatus=0x32f0 -OP_DzExpeditionEndsWarning=0x7e94 +OP_DzExpeditionEndsWarning=0x383c OP_DzMemberList=0x3de9 OP_DzCompass=0x3e0e OP_DzChooseZone=0x0b7d @@ -445,11 +444,11 @@ OP_ShopDelItem=0x724f OP_ClickObject=0x4aa1 OP_ClickObjectAction=0x0c1e OP_ClearObject=0x7a11 -OP_RecipeDetails=0x40d7 +OP_RecipeDetails=0x6e02 OP_RecipesFavorite=0x71b1 -OP_RecipesSearch=0x1db6 -OP_RecipeReply=0x6e02 -OP_RecipeAutoCombine=0x6261 +OP_RecipesSearch=0x6290 +OP_RecipeReply=0x1db6 +OP_RecipeAutoCombine=0x40d7 OP_TradeSkillCombine=0x579a # Tribute Packets: @@ -501,7 +500,7 @@ OP_GroupDisbandOther=0x74da OP_GroupLeaderChange=0x21b4 OP_GroupRoles=0x70e2 OP_GroupMakeLeader=0x4229 -OP_DoGroupLeadershipAbility=0x1fb5 +OP_DoGroupLeadershipAbility=0x6eae OP_GroupLeadershipAAUpdate=0x02cf OP_GroupMentor=0x3342 OP_InspectBuffs=0x486c diff --git a/world/clientlist.cpp b/world/clientlist.cpp index d0b90a266..6ef730990 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -1301,6 +1301,8 @@ void ClientList::SendClientVersionSummary(const char *Name) uint32 ClientSoDCount = 0; uint32 ClientUnderfootCount = 0; uint32 ClientRoFCount = 0; + uint32 ClientRoF2Count = 0; + LinkedListIterator Iterator(clientlist); @@ -1343,6 +1345,11 @@ void ClientList::SendClientVersionSummary(const char *Name) ++ClientRoFCount; break; } + case 7: + { + ++ClientRoF2Count; + break; + } default: break; } @@ -1352,7 +1359,7 @@ void ClientList::SendClientVersionSummary(const char *Name) } - zoneserver_list.SendEmoteMessage(Name, 0, 0, 13, "There are %i Titanium, %i SoF, %i SoD, %i UF, %i RoF clients currently connected.", - ClientTitaniumCount, ClientSoFCount, ClientSoDCount, ClientUnderfootCount, ClientRoFCount); + zoneserver_list.SendEmoteMessage(Name, 0, 0, 13, "There are %i Titanium, %i SoF, %i SoD, %i UF, %i RoF, %i RoF2 clients currently connected.", + ClientTitaniumCount, ClientSoFCount, ClientSoDCount, ClientUnderfootCount, ClientRoFCount, ClientRoF2Count); } diff --git a/world/worlddb.cpp b/world/worlddb.cpp index d9315e80e..e65a3a338 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -213,11 +213,21 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* } else { + uint32 color = 0; + if (pp.item_tint[matslot].rgb.use_tint) + { + color = pp.item_tint[matslot].color; + } + else + { + color = inst->GetColor(); + } + // Armor Materials/Models cs->equip[char_num][matslot].material = item->Material; cs->equip[char_num][matslot].elitematerial = item->EliteMaterial; cs->equip[char_num][matslot].heroforgemodel = inst->GetOrnamentHeroModel(matslot); - cs->equip[char_num][matslot].color.color = inst->GetColor(); + cs->equip[char_num][matslot].color.color = color; } } } diff --git a/zone/attack.cpp b/zone/attack.cpp index 5652b8bf8..37827ec63 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2107,6 +2107,18 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack #endif //BOTS } + if(give_exp && give_exp->IsTempPet() && give_exp->IsPetOwnerClient()) { + + if (give_exp->IsNPC() && give_exp->CastToNPC()->GetSwarmOwner()){ + Mob* temp_owner = nullptr; + temp_owner = entity_list.GetMobID(give_exp->CastToNPC()->GetSwarmOwner()); + + if (temp_owner) + give_exp = temp_owner; + } + } + + int PlayerCount = 0; // QueryServ Player Counting Client *give_exp_client = nullptr; @@ -3937,6 +3949,11 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { return; } + if (DivineAura()) { + mlog(COMBAT__PROCS, "Procs canceled, Divine Aura is in effect."); + return; + } + if(!weapon_g) { TrySpellProc(nullptr, (const Item_Struct*)nullptr, on); return; diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index aa89856a1..b9f35c9ff 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -538,9 +538,9 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu } void Client::CalcEdibleBonuses(StatBonuses* newbon) { -#if EQDEBUG >= 11 - std::cout<<"Client::CalcEdibleBonuses(StatBonuses* newbon)"<= 11 +// std::cout<<"Client::CalcEdibleBonuses(StatBonuses* newbon)"<GetTarget()->CastToBot()->SendWearChange(slotmaterial); } else { diff --git a/zone/client.cpp b/zone/client.cpp index d2349e02a..181589bf5 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2685,8 +2685,11 @@ void Client::SetMaterial(int16 in_slot, uint32 item_id) { const Item_Struct* item = database.GetItem(item_id); if (item && (item->ItemClass==ItemClassCommon)) { - uint32 matslot = Inventory::CalcMaterialFromSlot(in_slot); - m_pp.item_material[matslot] = GetEquipmentMaterial(matslot); + uint8 matslot = Inventory::CalcMaterialFromSlot(in_slot); + if (matslot != _MaterialInvalid) + { + m_pp.item_material[matslot] = GetEquipmentMaterial(matslot); + } } } @@ -3016,31 +3019,14 @@ void Client::SetTint(int16 in_slot, uint32 color) { // Still need to reconcile bracer01 versus bracer02 void Client::SetTint(int16 in_slot, Color_Struct& color) { - if (in_slot==MainHead) - m_pp.item_tint[MaterialHead].color=color.color; - else if (in_slot==MainArms) - m_pp.item_tint[MaterialArms].color=color.color; - else if (in_slot==MainWrist1) - m_pp.item_tint[MaterialWrist].color=color.color; - /* - // non-live behavior - else if (in_slot==SLOT_BRACER02) - m_pp.item_tint[MaterialWrist].color=color.color; - */ - else if (in_slot==MainHands) - m_pp.item_tint[MaterialHands].color=color.color; - else if (in_slot==MainPrimary) - m_pp.item_tint[MaterialPrimary].color=color.color; - else if (in_slot==MainSecondary) - m_pp.item_tint[MaterialSecondary].color=color.color; - else if (in_slot==MainChest) - m_pp.item_tint[MaterialChest].color=color.color; - else if (in_slot==MainLegs) - m_pp.item_tint[MaterialLegs].color=color.color; - else if (in_slot==MainFeet) - m_pp.item_tint[MaterialFeet].color=color.color; - database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.color); + uint8 matslot = Inventory::CalcMaterialFromSlot(in_slot); + if (matslot != _MaterialInvalid) + { + m_pp.item_tint[matslot].color = color.color; + database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.color); + } + } void Client::SetHideMe(bool flag) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0bf31a7e1..5c07a839c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1428,8 +1428,12 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Set item material tint */ for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) - if (m_pp.item_tint[i].rgb.use_tint == 1 || m_pp.item_tint[i].rgb.use_tint == 255) - m_pp.item_tint[i].rgb.use_tint = 0xFF; + { + if (m_pp.item_tint[i].rgb.use_tint == 1 || m_pp.item_tint[i].rgb.use_tint == 255) + { + m_pp.item_tint[i].rgb.use_tint = 0xFF; + } + } if (level){ level = m_pp.level; } @@ -5745,7 +5749,7 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) } else { - std::list pathlist = zone->pathing->FindRoute(Start, End); + std::deque pathlist = zone->pathing->FindRoute(Start, End); if (pathlist.size() == 0) { @@ -5784,7 +5788,7 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) p.z = GetZ(); points.push_back(p); - for (std::list::iterator Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) + for (auto Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) { if ((*Iterator) == -1) // Teleporter { diff --git a/zone/entity.cpp b/zone/entity.cpp index fb2eed2a3..085e9bdb5 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2664,7 +2664,7 @@ void EntityList::FindPathsToAllNPCs() while (it != npc_list.end()) { Map::Vertex Node0 = zone->pathing->GetPathNodeCoordinates(0, false); Map::Vertex Dest(it->second->GetX(), it->second->GetY(), it->second->GetZ()); - std::list Route = zone->pathing->FindRoute(Node0, Dest); + std::deque Route = zone->pathing->FindRoute(Node0, Dest); if (Route.size() == 0) printf("Unable to find a route to %s\n", it->second->GetName()); else diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 89b34c444..f4354e709 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -158,7 +158,7 @@ void Mob::CalculateNewFearpoint() Map::Vertex CurrentPosition(GetX(), GetY(), GetZ()); - std::list Route = zone->pathing->FindRoute(CurrentPosition, Loc); + std::deque Route = zone->pathing->FindRoute(CurrentPosition, Loc); if(Route.size() > 0) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 5175b16e8..270ca5dc6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -542,6 +542,8 @@ float Mob::_GetMovementSpeed(int mod) const // http://everquest.allakhazam.com/db/item.html?item=1721;page=1;howmany=50#m10822246245352 if (IsRooted()) return 0.0f; + else if (IsPseudoRooted()) + return 0.00001f; float speed_mod = runspeed; @@ -2795,10 +2797,17 @@ uint32 Mob::GetEquipmentColor(uint8 material_slot) const { const Item_Struct *item; - item = database.GetItem(GetEquipment(material_slot)); - if(item != 0) + if (armor_tint[material_slot]) { - return item->Color; + return armor_tint[material_slot]; + } + else + { + item = database.GetItem(GetEquipment(material_slot)); + if (item != 0) + { + return item->Color; + } } return 0; @@ -5323,98 +5332,98 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) } if (slot < 16){ - if (id == "classes") {stat = spells[spell_id].classes[slot]; } - else if (id == "dieties") {stat = spells[spell_id].deities[slot];} + if (id == "classes") {return spells[spell_id].classes[slot]; } + else if (id == "dieties") {return spells[spell_id].deities[slot];} } if (slot < 12){ - if (id == "base") {stat = spells[spell_id].base[slot];} - else if (id == "base2") {stat = spells[spell_id].base2[slot];} - else if (id == "max") {stat = spells[spell_id].max[slot];} - else if (id == "formula") {spells[spell_id].formula[slot];} - else if (id == "effectid") {spells[spell_id].effectid[slot];} + if (id == "base") {return spells[spell_id].base[slot];} + else if (id == "base2") {return spells[spell_id].base2[slot];} + else if (id == "max") {return spells[spell_id].max[slot];} + else if (id == "formula") {return spells[spell_id].formula[slot];} + else if (id == "effectid") {return spells[spell_id].effectid[slot];} } if (slot < 4){ - if (id == "components") { spells[spell_id].components[slot];} - else if (id == "component_counts") {spells[spell_id].component_counts[slot];} - else if (id == "NoexpendReagent") {spells[spell_id].NoexpendReagent[slot];} + if (id == "components") { return spells[spell_id].components[slot];} + else if (id == "component_counts") { return spells[spell_id].component_counts[slot];} + else if (id == "NoexpendReagent") {return spells[spell_id].NoexpendReagent[slot];} } - if (id == "range") {stat = static_cast(spells[spell_id].range); } - else if (id == "aoerange") {stat = static_cast(spells[spell_id].aoerange);} - else if (id == "pushback") {stat = static_cast(spells[spell_id].pushback);} - else if (id == "pushup") {stat = static_cast(spells[spell_id].pushup);} - else if (id == "cast_time") {stat = spells[spell_id].cast_time;} - else if (id == "recovery_time") {stat = spells[spell_id].recovery_time;} - else if (id == "recast_time") {stat = spells[spell_id].recast_time;} - else if (id == "buffdurationformula") {stat = spells[spell_id].buffdurationformula;} - else if (id == "buffduration") {stat = spells[spell_id].buffduration;} - else if (id == "AEDuration") {stat = spells[spell_id].AEDuration;} - else if (id == "mana") {stat = spells[spell_id].mana;} + if (id == "range") {return static_cast(spells[spell_id].range); } + else if (id == "aoerange") {return static_cast(spells[spell_id].aoerange);} + else if (id == "pushback") {return static_cast(spells[spell_id].pushback);} + else if (id == "pushup") {return static_cast(spells[spell_id].pushup);} + else if (id == "cast_time") {return spells[spell_id].cast_time;} + else if (id == "recovery_time") {return spells[spell_id].recovery_time;} + else if (id == "recast_time") {return spells[spell_id].recast_time;} + else if (id == "buffdurationformula") {return spells[spell_id].buffdurationformula;} + else if (id == "buffduration") {return spells[spell_id].buffduration;} + else if (id == "AEDuration") {return spells[spell_id].AEDuration;} + else if (id == "mana") {return spells[spell_id].mana;} //else if (id == "LightType") {stat = spells[spell_id].LightType;} - Not implemented - else if (id == "goodEffect") {stat = spells[spell_id].goodEffect;} - else if (id == "Activated") {stat = spells[spell_id].Activated;} - else if (id == "resisttype") {stat = spells[spell_id].resisttype;} - else if (id == "targettype") {stat = spells[spell_id].targettype;} - else if (id == "basedeiff") {stat = spells[spell_id].basediff;} - else if (id == "skill") {stat = spells[spell_id].skill;} - else if (id == "zonetype") {stat = spells[spell_id].zonetype;} - else if (id == "EnvironmentType") {stat = spells[spell_id].EnvironmentType;} - else if (id == "TimeOfDay") {stat = spells[spell_id].TimeOfDay;} - else if (id == "CastingAnim") {stat = spells[spell_id].CastingAnim;} - else if (id == "SpellAffectIndex") {stat = spells[spell_id].SpellAffectIndex; } - else if (id == "disallow_sit") {stat = spells[spell_id].disallow_sit; } + else if (id == "goodEffect") {return spells[spell_id].goodEffect;} + else if (id == "Activated") {return spells[spell_id].Activated;} + else if (id == "resisttype") {return spells[spell_id].resisttype;} + else if (id == "targettype") {return spells[spell_id].targettype;} + else if (id == "basedeiff") {return spells[spell_id].basediff;} + else if (id == "skill") {return spells[spell_id].skill;} + else if (id == "zonetype") {return spells[spell_id].zonetype;} + else if (id == "EnvironmentType") {return spells[spell_id].EnvironmentType;} + else if (id == "TimeOfDay") {return spells[spell_id].TimeOfDay;} + else if (id == "CastingAnim") {return spells[spell_id].CastingAnim;} + else if (id == "SpellAffectIndex") {return spells[spell_id].SpellAffectIndex; } + else if (id == "disallow_sit") {return spells[spell_id].disallow_sit; } //else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented - else if (id == "uninterruptable") {stat = spells[spell_id].uninterruptable; } - else if (id == "ResistDiff") {stat = spells[spell_id].ResistDiff; } - else if (id == "dot_stacking_exemp") {stat = spells[spell_id].dot_stacking_exempt; } - else if (id == "RecourseLink") {stat = spells[spell_id].RecourseLink; } - else if (id == "no_partial_resist") {stat = spells[spell_id].no_partial_resist; } - else if (id == "short_buff_box") {stat = spells[spell_id].short_buff_box; } - else if (id == "descnum") {stat = spells[spell_id].descnum; } - else if (id == "effectdescnum") {stat = spells[spell_id].effectdescnum; } - else if (id == "npc_no_los") {stat = spells[spell_id].npc_no_los; } - else if (id == "reflectable") {stat = spells[spell_id].reflectable; } - else if (id == "bonushate") {stat = spells[spell_id].bonushate; } - else if (id == "EndurCost") {stat = spells[spell_id].EndurCost; } - else if (id == "EndurTimerIndex") {stat = spells[spell_id].EndurTimerIndex; } - else if (id == "IsDisciplineBuf") {stat = spells[spell_id].IsDisciplineBuff; } - else if (id == "HateAdded") {stat = spells[spell_id].HateAdded; } - else if (id == "EndurUpkeep") {stat = spells[spell_id].EndurUpkeep; } - else if (id == "numhitstype") {stat = spells[spell_id].numhitstype; } - else if (id == "numhits") {stat = spells[spell_id].numhits; } - else if (id == "pvpresistbase") {stat = spells[spell_id].pvpresistbase; } - else if (id == "pvpresistcalc") {stat = spells[spell_id].pvpresistcalc; } - else if (id == "pvpresistcap") {stat = spells[spell_id].pvpresistcap; } - else if (id == "spell_category") {stat = spells[spell_id].spell_category; } - else if (id == "can_mgb") {stat = spells[spell_id].can_mgb; } - else if (id == "dispel_flag") {stat = spells[spell_id].dispel_flag; } - else if (id == "MinResist") {stat = spells[spell_id].MinResist; } - else if (id == "MaxResist") {stat = spells[spell_id].MaxResist; } - else if (id == "viral_targets") {stat = spells[spell_id].viral_targets; } - else if (id == "viral_timer") {stat = spells[spell_id].viral_timer; } - else if (id == "NimbusEffect") {stat = spells[spell_id].NimbusEffect; } - else if (id == "directional_start") {stat = static_cast(spells[spell_id].directional_start); } - else if (id == "directional_end") {stat = static_cast(spells[spell_id].directional_end); } - else if (id == "not_extendable") {stat = spells[spell_id].not_extendable; } - else if (id == "suspendable") {stat = spells[spell_id].suspendable; } - else if (id == "viral_range") {stat = spells[spell_id].viral_range; } - else if (id == "spellgroup") {stat = spells[spell_id].spellgroup; } - else if (id == "rank") {stat = spells[spell_id].rank; } - else if (id == "powerful_flag") {stat = spells[spell_id].powerful_flag; } - else if (id == "CastRestriction") {stat = spells[spell_id].CastRestriction; } - else if (id == "AllowRest") {stat = spells[spell_id].AllowRest; } - else if (id == "InCombat") {stat = spells[spell_id].InCombat; } - else if (id == "OutofCombat") {stat = spells[spell_id].OutofCombat; } - else if (id == "aemaxtargets") {stat = spells[spell_id].aemaxtargets; } - else if (id == "maxtargets") {stat = spells[spell_id].maxtargets; } - else if (id == "persistdeath") {stat = spells[spell_id].persistdeath; } - else if (id == "min_dist") {stat = static_cast(spells[spell_id].min_dist); } - else if (id == "min_dist_mod") {stat = static_cast(spells[spell_id].min_dist_mod); } - else if (id == "max_dist") {stat = static_cast(spells[spell_id].max_dist); } - else if (id == "min_range") {stat = static_cast(spells[spell_id].min_range); } - else if (id == "DamageShieldType") {stat = spells[spell_id].DamageShieldType; } + else if (id == "uninterruptable") {return spells[spell_id].uninterruptable; } + else if (id == "ResistDiff") {return spells[spell_id].ResistDiff; } + else if (id == "dot_stacking_exemp") {return spells[spell_id].dot_stacking_exempt; } + else if (id == "RecourseLink") {return spells[spell_id].RecourseLink; } + else if (id == "no_partial_resist") {return spells[spell_id].no_partial_resist; } + else if (id == "short_buff_box") {return spells[spell_id].short_buff_box; } + else if (id == "descnum") {return spells[spell_id].descnum; } + else if (id == "effectdescnum") {return spells[spell_id].effectdescnum; } + else if (id == "npc_no_los") {return spells[spell_id].npc_no_los; } + else if (id == "reflectable") {return spells[spell_id].reflectable; } + else if (id == "bonushate") {return spells[spell_id].bonushate; } + else if (id == "EndurCost") {return spells[spell_id].EndurCost; } + else if (id == "EndurTimerIndex") {return spells[spell_id].EndurTimerIndex; } + else if (id == "IsDisciplineBuf") {return spells[spell_id].IsDisciplineBuff; } + else if (id == "HateAdded") {return spells[spell_id].HateAdded; } + else if (id == "EndurUpkeep") {return spells[spell_id].EndurUpkeep; } + else if (id == "numhitstype") {return spells[spell_id].numhitstype; } + else if (id == "numhits") {return spells[spell_id].numhits; } + else if (id == "pvpresistbase") {return spells[spell_id].pvpresistbase; } + else if (id == "pvpresistcalc") {return spells[spell_id].pvpresistcalc; } + else if (id == "pvpresistcap") {return spells[spell_id].pvpresistcap; } + else if (id == "spell_category") {return spells[spell_id].spell_category; } + else if (id == "can_mgb") {return spells[spell_id].can_mgb; } + else if (id == "dispel_flag") {return spells[spell_id].dispel_flag; } + else if (id == "MinResist") {return spells[spell_id].MinResist; } + else if (id == "MaxResist") {return spells[spell_id].MaxResist; } + else if (id == "viral_targets") {return spells[spell_id].viral_targets; } + else if (id == "viral_timer") {return spells[spell_id].viral_timer; } + else if (id == "NimbusEffect") {return spells[spell_id].NimbusEffect; } + else if (id == "directional_start") {return static_cast(spells[spell_id].directional_start); } + else if (id == "directional_end") {return static_cast(spells[spell_id].directional_end); } + else if (id == "not_extendable") {return spells[spell_id].not_extendable; } + else if (id == "suspendable") {return spells[spell_id].suspendable; } + else if (id == "viral_range") {return spells[spell_id].viral_range; } + else if (id == "spellgroup") {return spells[spell_id].spellgroup; } + else if (id == "rank") {return spells[spell_id].rank; } + else if (id == "powerful_flag") {return spells[spell_id].powerful_flag; } + else if (id == "CastRestriction") {return spells[spell_id].CastRestriction; } + else if (id == "AllowRest") {return spells[spell_id].AllowRest; } + else if (id == "InCombat") {return spells[spell_id].InCombat; } + else if (id == "OutofCombat") {return spells[spell_id].OutofCombat; } + else if (id == "aemaxtargets") {return spells[spell_id].aemaxtargets; } + else if (id == "maxtargets") {return spells[spell_id].maxtargets; } + else if (id == "persistdeath") {return spells[spell_id].persistdeath; } + else if (id == "min_dist") {return static_cast(spells[spell_id].min_dist); } + else if (id == "min_dist_mod") {return static_cast(spells[spell_id].min_dist_mod); } + else if (id == "max_dist") {return static_cast(spells[spell_id].max_dist); } + else if (id == "min_range") {return static_cast(spells[spell_id].min_range); } + else if (id == "DamageShieldType") {return spells[spell_id].DamageShieldType; } return stat; } diff --git a/zone/mob.h b/zone/mob.h index e0a969fc4..431b3ac31 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1225,7 +1225,7 @@ protected: Map::Vertex PathingLastPosition; int PathingLoopCount; int PathingLastNodeVisited; - std::list Route; + std::deque Route; LOSType PathingLOSState; Timer *PathingLOSCheckTimer; Timer *PathingRouteUpdateTimerShort; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index b6c2fb111..253073d44 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -2751,7 +2751,7 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) { npc_spells_cache[iDBSpellsID]->attack_proc = tmpattack_proc; npc_spells_cache[iDBSpellsID]->proc_chance = tmpproc_chance; npc_spells_cache[iDBSpellsID]->range_proc = tmprange_proc; - npc_spells_cache[iDBSpellsID]->rproc_chance = tmpdproc_chance; + npc_spells_cache[iDBSpellsID]->rproc_chance = tmprproc_chance; npc_spells_cache[iDBSpellsID]->defensive_proc = tmpdefensive_proc; npc_spells_cache[iDBSpellsID]->dproc_chance = tmpdproc_chance; npc_spells_cache[iDBSpellsID]->fail_recast = tmppfail_recast; diff --git a/zone/npc.cpp b/zone/npc.cpp index 6fdab645c..ddfd1b243 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -476,7 +476,7 @@ void NPC::CheckMinMaxLevel(Mob *them) if(themlevel < (*cur)->min_level || themlevel > (*cur)->max_level) { material = Inventory::CalcMaterialFromSlot((*cur)->equip_slot); - if(material != 0xFF) + if (material != _MaterialInvalid) SendWearChange(material); cur = itemlist.erase(cur); @@ -1942,6 +1942,7 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue) else if(id == "special_attacks") { NPCSpecialAttacks(val.c_str(), 0, 1); return; } else if(id == "special_abilities") { ProcessSpecialAbilities(val.c_str()); return; } else if(id == "attack_speed") { attack_speed = (float)atof(val.c_str()); CalcBonuses(); return; } + else if(id == "attack_delay") { attack_delay = atoi(val.c_str()); CalcBonuses(); return; } else if(id == "atk") { ATK = atoi(val.c_str()); return; } else if(id == "accuracy") { accuracy_rating = atoi(val.c_str()); return; } else if(id == "avoidance") { avoidance_rating = atoi(val.c_str()); return; } diff --git a/zone/npc.h b/zone/npc.h index 081fca514..59fbcc3fd 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -155,6 +155,7 @@ public: virtual void RangedAttack(Mob* other); virtual void ThrowingAttack(Mob* other) { } int32 GetNumberOfAttacks() const { return attack_count; } + void DoRangedAttackDmg(Mob* other, bool Launch=true, int16 damage_mod=0, int16 chance_mod=0, SkillUseTypes skill=SkillArchery, float speed=4.0f, const char *IDFile = nullptr); bool DatabaseCastAccepted(int spell_id); bool IsFactionListAlly(uint32 other_faction); @@ -259,6 +260,7 @@ public: uint32 GetMinDMG() const {return min_dmg;} int16 GetSlowMitigation() const {return slow_mitigation;} float GetAttackSpeed() const {return attack_speed;} + uint8 GetAttackDelay() const {return attack_delay;} bool IsAnimal() const { return(bodytype == BT_Animal); } uint16 GetPetSpellID() const {return pet_spell_id;} void SetPetSpellID(uint16 amt) {pet_spell_id = amt;} diff --git a/zone/pathing.cpp b/zone/pathing.cpp index 5501ad2ca..2ddd7d94b 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -205,15 +205,15 @@ Map::Vertex PathManager::GetPathNodeCoordinates(int NodeNumber, bool BestZ) } -std::list PathManager::FindRoute(int startID, int endID) +std::deque PathManager::FindRoute(int startID, int endID) { _log(PATHING__DEBUG, "FindRoute from node %i to %i", startID, endID); memset(ClosedListFlag, 0, sizeof(int) * Head.PathNodeCount); - std::list OpenList, ClosedList; + std::deque OpenList, ClosedList; - std::listRoute; + std::dequeRoute; AStarNode AStarEntry, CurrentNode; @@ -251,7 +251,7 @@ std::list PathManager::FindRoute(int startID, int endID) Route.push_back(endID); - std::list::iterator RouteIterator; + std::deque::iterator RouteIterator; while(CurrentNode.PathNodeID != startID) { @@ -300,7 +300,7 @@ std::list PathManager::FindRoute(int startID, int endID) bool AlreadyInOpenList = false; - std::list::iterator OpenListIterator, InsertionPoint = OpenList.end(); + std::deque::iterator OpenListIterator, InsertionPoint = OpenList.end(); for(OpenListIterator = OpenList.begin(); OpenListIterator != OpenList.end(); ++OpenListIterator) { @@ -350,11 +350,11 @@ bool SortPathNodesByDistance(PathNodeSortStruct n1, PathNodeSortStruct n2) return n1.Distance < n2.Distance; } -std::list PathManager::FindRoute(Map::Vertex Start, Map::Vertex End) +std::deque PathManager::FindRoute(Map::Vertex Start, Map::Vertex End) { _log(PATHING__DEBUG, "FindRoute(%8.3f, %8.3f, %8.3f, %8.3f, %8.3f, %8.3f)", Start.x, Start.y, Start.z, End.x, End.y, End.z); - std::list noderoute; + std::deque noderoute; float CandidateNodeRangeXY = RuleR(Pathing, CandidateNodeRangeXY); @@ -365,7 +365,7 @@ std::list PathManager::FindRoute(Map::Vertex Start, Map::Vertex End) // int ClosestPathNodeToStart = -1; - std::list SortedByDistance; + std::deque SortedByDistance; PathNodeSortStruct TempNode; @@ -382,9 +382,9 @@ std::list PathManager::FindRoute(Map::Vertex Start, Map::Vertex End) } } - SortedByDistance.sort(SortPathNodesByDistance); + std::sort(SortedByDistance.begin(), SortedByDistance.end(), SortPathNodesByDistance); - for(std::list::iterator Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator) + for(auto Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator) { _log(PATHING__DEBUG, "Checking Reachability of Node %i from Start Position.", PathNodes[(*Iterator).id].id); @@ -420,9 +420,9 @@ std::list PathManager::FindRoute(Map::Vertex Start, Map::Vertex End) } } - SortedByDistance.sort(SortPathNodesByDistance); + std::sort(SortedByDistance.begin(), SortedByDistance.end(), SortPathNodesByDistance); - for(std::list::iterator Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator) + for(auto Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator) { _log(PATHING__DEBUG, "Checking Reachability of Node %i from End Position.", PathNodes[(*Iterator).id].id); _log(PATHING__DEBUG, " (%8.3f, %8.3f, %8.3f) to (%8.3f, %8.3f, %8.3f)", @@ -456,7 +456,7 @@ std::list PathManager::FindRoute(Map::Vertex Start, Map::Vertex End) { int CulledNodes = 0; - std::list::iterator First, Second; + std::deque::iterator First, Second; while((noderoute.size() >= 2) && (CulledNodes < NodesToAttemptToCull)) { @@ -487,7 +487,7 @@ std::list PathManager::FindRoute(Map::Vertex Start, Map::Vertex End) { int CulledNodes = 0; - std::list::iterator First, Second; + std::deque::iterator First, Second; while((noderoute.size() >= 2) && (CulledNodes < NodesToAttemptToCull)) { @@ -611,7 +611,7 @@ void PathManager::MeshTest() if(j == i) continue; - std::list Route = FindRoute(PathNodes[i].id, PathNodes[j].id); + std::deque Route = FindRoute(PathNodes[i].id, PathNodes[j].id); if(Route.size() == 0) { @@ -638,7 +638,7 @@ void PathManager::SimpleMeshTest() for(uint32 j = 1; j < Head.PathNodeCount; ++j) { - std::list Route = FindRoute(PathNodes[0].id, PathNodes[j].id); + std::deque Route = FindRoute(PathNodes[0].id, PathNodes[j].id); if(Route.size() == 0) { @@ -1103,7 +1103,7 @@ int PathManager::FindNearestPathNode(Map::Vertex Position) int ClosestPathNodeToStart = -1; - std::list SortedByDistance; + std::deque SortedByDistance; PathNodeSortStruct TempNode; @@ -1120,9 +1120,9 @@ int PathManager::FindNearestPathNode(Map::Vertex Position) } } - SortedByDistance.sort(SortPathNodesByDistance); + std::sort(SortedByDistance.begin(), SortedByDistance.end(), SortPathNodesByDistance); - for(std::list::iterator Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator) + for(auto Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator) { _log(PATHING__DEBUG, "Checking Reachability of Node %i from Start Position.", PathNodes[(*Iterator).id].id); @@ -1262,9 +1262,7 @@ void Mob::PrintRoute() printf("Route is : "); - std::list::iterator Iterator; - - for(Iterator = Route.begin(); Iterator !=Route.end(); ++Iterator) + for(auto Iterator = Route.begin(); Iterator !=Route.end(); ++Iterator) { printf("%i, ", (*Iterator)); } diff --git a/zone/pathing.h b/zone/pathing.h index c01cb6853..8848547b2 100644 --- a/zone/pathing.h +++ b/zone/pathing.h @@ -3,7 +3,7 @@ #include "map.h" -#include +#include class Client; class Mob; @@ -60,8 +60,8 @@ public: static PathManager *LoadPathFile(const char *ZoneName); bool loadPaths(FILE *fp); void PrintPathing(); - std::list FindRoute(Map::Vertex Start, Map::Vertex End); - std::list FindRoute(int startID, int endID); + std::deque FindRoute(Map::Vertex Start, Map::Vertex End); + std::deque FindRoute(int startID, int endID); Map::Vertex GetPathNodeCoordinates(int NodeNumber, bool BestZ = true); bool CheckLosFN(Map::Vertex a, Map::Vertex b); diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index c1f8d8828..279b64330 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -2119,6 +2119,32 @@ XS(XS_NPC_GetAttackSpeed) XSRETURN(1); } +XS(XS_NPC_GetAttackDelay); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetAttackDelay) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetAttackDelay(THIS)"); + { + NPC * THIS; + float RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->GetAttackDelay(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + XS(XS_NPC_GetAccuracyRating); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_GetAccuracyRating) { @@ -2145,6 +2171,32 @@ XS(XS_NPC_GetAccuracyRating) XSRETURN(1); } +XS(XS_NPC_GetAvoidanceRating); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetAvoidanceRating) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetAvoidanceyRating(THIS)"); + { + NPC * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->GetAvoidanceRating(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + XS(XS_NPC_GetSpawnKillCount); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_GetSpawnKillCount) { @@ -2245,6 +2297,81 @@ XS(XS_NPC_GetMerchantProbability) { XSRETURN(1); } +XS(XS_NPC_AddMeleeProc); +XS(XS_NPC_AddMeleeProc) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: NPC::AddMeleeProc(THIS,spellid,chance)"); + { + NPC * THIS; + int spell_id = (int)SvIV(ST(1)); + int chance = (int)SvIV(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->AddProcToWeapon(spell_id, true, chance); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_AddRangedProc); +XS(XS_NPC_AddRangedProc) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: NPC::AddRangedProc(THIS,spellid,chance)"); + { + NPC * THIS; + int spell_id = (int)SvIV(ST(1)); + int chance = (int)SvIV(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->AddDefensiveProc(spell_id,chance); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_AddDefensiveProc); +XS(XS_NPC_AddDefensiveProc) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: NPC::AddDefensiveProc(THIS,spellid,chance)"); + { + NPC * THIS; + int spell_id = (int)SvIV(ST(1)); + int chance = (int)SvIV(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->AddProcToWeapon(spell_id, true, chance); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -2342,11 +2469,16 @@ XS(boot_NPC) newXSproto(strcpy(buf, "GetSpellFocusHeal"), XS_NPC_GetSpellFocusHeal, file, "$"); newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetSlowMitigation, file, "$"); newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetAttackSpeed, file, "$"); + newXSproto(strcpy(buf, "GetAttackDelay"), XS_NPC_GetAttackDelay, file, "$"); newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$"); + newXSproto(strcpy(buf, "GetAvoidanceRating"), XS_NPC_GetAvoidanceRating, file, "$"); newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$"); newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$"); newXSproto(strcpy(buf, "SetMerchantProbability"), XS_NPC_SetMerchantProbability, file, "$$"); newXSproto(strcpy(buf, "GetMerchantProbability"), XS_NPC_GetMerchantProbability, file, "$"); + newXSproto(strcpy(buf, "AddMeleeProc"), XS_NPC_AddMeleeProc, file, "$$$"); + newXSproto(strcpy(buf, "AddRangedProc"), XS_NPC_AddRangedProc, file, "$$$"); + newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$"); XSRETURN_YES; } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 282eeb11e..f74949a0b 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -103,6 +103,9 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, if (!who) return; + if(who->GetInvul() || who->GetSpecialAbility(IMMUNE_MELEE) || who->GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE)) + return; //-5? + int32 hate = max_damage; if(hate_override > -1) hate = hate_override; @@ -1059,8 +1062,8 @@ bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes if(item) SendItemAnimation(other, item, skillInUse, speed); - else if (IsNPC()) - ProjectileAnimation(other, 0,false,speed,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse); + //else if (IsNPC()) + //ProjectileAnimation(other, 0,false,speed,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse); return true; } @@ -1097,12 +1100,19 @@ void Mob::ProjectileAttack() if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment){ if (target){ - if (ProjectileAtk[i].skill == SkillArchery) - DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id, nullptr, ProjectileAtk[i].ammo_slot); - else if (ProjectileAtk[i].skill == SkillThrowing) - DoThrowingAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0, ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_slot); - else if (ProjectileAtk[i].skill == SkillConjuration && IsValidSpell(ProjectileAtk[i].wpn_dmg)) - SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, spells[ProjectileAtk[i].wpn_dmg].ResistDiff, true); + + if (IsNPC()) + CastToNPC()->DoRangedAttackDmg(target, false, ProjectileAtk[i].wpn_dmg,0, static_cast(ProjectileAtk[i].skill)); + + else + { + if (ProjectileAtk[i].skill == SkillArchery) + DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id, nullptr, ProjectileAtk[i].ammo_slot); + else if (ProjectileAtk[i].skill == SkillThrowing) + DoThrowingAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0, ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_slot); + else if (ProjectileAtk[i].skill == SkillConjuration && IsValidSpell(ProjectileAtk[i].wpn_dmg)) + SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, spells[ProjectileAtk[i].wpn_dmg].ResistDiff, true); + } } ProjectileAtk[i].increment = 0; @@ -1187,14 +1197,8 @@ void NPC::RangedAttack(Mob* other) attacks = attacks > 0 ? attacks : 1; for(int i = 0; i < attacks; ++i) { - //if we have SPECATK_RANGED_ATK set then we range attack without weapon or ammo - const Item_Struct* weapon = nullptr; - const Item_Struct* ammo = nullptr; if(!GetSpecialAbility(SPECATK_RANGED_ATK)) - { - //find our bow and ammo return if we can't find them... return; - } int sa_min_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 4); //Min Range of NPC attack int sa_max_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 1); //Max Range of NPC attack @@ -1208,16 +1212,11 @@ void NPC::RangedAttack(Mob* other) if (sa_min_range) min_range = static_cast(sa_min_range); - mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", max_range); max_range *= max_range; - if(DistNoRootNoZ(*other) > max_range) { - mlog(COMBAT__RANGED, "Ranged attack out of range...%.2f vs %.2f", DistNoRootNoZ(*other), max_range); - //target is out of range, client does a message + if(DistNoRoot(*other) > max_range) return; - } - else if(DistNoRootNoZ(*other) < (min_range * min_range)) + else if(DistNoRoot(*other) < (min_range * min_range)) return; - if(!other || !IsAttackAllowed(other) || IsCasting() || @@ -1229,74 +1228,100 @@ void NPC::RangedAttack(Mob* other) return; } - SkillUseTypes skillinuse = SkillArchery; - skillinuse = static_cast(GetRangedSkill()); - - if(!ammo && !GetAmmoIDfile()) - ammo = database.GetItem(8005); - - if(ammo) - SendItemAnimation(other, ammo, SkillArchery); - else - ProjectileAnimation(other, 0,false,0,0,0,0,GetAmmoIDfile(),skillinuse); - FaceTarget(other); - if (!other->CheckHitChance(this, skillinuse, MainRange, GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2))) - { - mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); - other->Damage(this, 0, SPELL_UNKNOWN, skillinuse); - } - else - { - int16 WDmg = GetWeaponDamage(other, weapon); - int16 ADmg = GetWeaponDamage(other, ammo); - int32 TotalDmg = 0; - if(WDmg > 0 || ADmg > 0) - { - mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName()); - - int32 MaxDmg = max_dmg * RuleR(Combat, ArcheryNPCMultiplier); // should add a field to npc_types - int32 MinDmg = min_dmg * RuleR(Combat, ArcheryNPCMultiplier); - - if(RuleB(Combat, UseIntervalAC)) - TotalDmg = MaxDmg; - else - TotalDmg = zone->random.Int(MinDmg, MaxDmg); - - TotalDmg += TotalDmg * GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3) / 100; //Damage modifier - - other->AvoidDamage(this, TotalDmg, false); - other->MeleeMitigation(this, TotalDmg, MinDmg); - if (TotalDmg > 0) - CommonOutgoingHitSuccess(other, TotalDmg, skillinuse); - } - - else - TotalDmg = -5; - - if (TotalDmg > 0) - other->AddToHateList(this, TotalDmg, 0, false); - else - other->AddToHateList(this, 0, 0, false); - - other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillinuse); - - if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && !other->HasDied()) - TrySkillProc(other, skillinuse, 0, true, MainRange); - } - - //try proc on hits and misses - if(other && !other->HasDied()) - TrySpellProc(nullptr, (const Item_Struct*)nullptr, other, MainRange); - - if (HasSkillProcs() && other && !other->HasDied()) - TrySkillProc(other, skillinuse, 0, false, MainRange); + DoRangedAttackDmg(other); CommonBreakInvisible(); } } +void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 chance_mod, SkillUseTypes skill, float speed, const char *IDFile) { + + if ((other == nullptr || + (other->HasDied())) || + HasDied() || + (!IsAttackAllowed(other)) || + (other->GetInvul() || + other->GetSpecialAbility(IMMUNE_MELEE))) + { + return; + } + + SkillUseTypes skillInUse = static_cast(GetRangedSkill()); + + if (skill != skillInUse) + skillInUse = skill; + + if (Launch) + { + const char *ammo = "IT10"; + + if (IDFile != nullptr) + ammo = IDFile; + else if (GetAmmoIDfile()) + ammo = GetAmmoIDfile(); + + ProjectileAnimation(other, 0,false,speed,0,0,0,ammo,skillInUse); + + if (RuleB(Combat, ProjectileDmgOnImpact)) + { + TryProjectileAttack(other, nullptr, skillInUse, damage_mod, nullptr, nullptr, 0, speed); + return; + } + } + + if (!chance_mod) + chance_mod = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2); + + if (!other->CheckHitChance(this, skillInUse, MainRange, chance_mod)) + { + other->Damage(this, 0, SPELL_UNKNOWN, skillInUse); + } + else + { + int32 TotalDmg = 0; + int32 MaxDmg = max_dmg * RuleR(Combat, ArcheryNPCMultiplier); // should add a field to npc_types + int32 MinDmg = min_dmg * RuleR(Combat, ArcheryNPCMultiplier); + + if(RuleB(Combat, UseIntervalAC)) + TotalDmg = MaxDmg; + else + TotalDmg = zone->random.Int(MinDmg, MaxDmg); + + + if (!damage_mod) + damage_mod = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3);//Damage modifier + + TotalDmg += TotalDmg * damage_mod / 100; + + other->AvoidDamage(this, TotalDmg, false); + other->MeleeMitigation(this, TotalDmg, MinDmg); + + if (TotalDmg > 0) + CommonOutgoingHitSuccess(other, TotalDmg, skillInUse); + else + TotalDmg = -5; + + if (TotalDmg > 0) + other->AddToHateList(this, TotalDmg, 0, false); + else + other->AddToHateList(this, 0, 0, false); + + other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillInUse); + + if (TotalDmg > 0 && HasSkillProcSuccess() && !other->HasDied()) + TrySkillProc(other, skillInUse, 0, true, MainRange); + } + + //try proc on hits and misses + if(other && !other->HasDied()) + TrySpellProc(nullptr, (const Item_Struct*)nullptr, other, MainRange); + + if (HasSkillProcs() && other && !other->HasDied()) + TrySkillProc(other, skillInUse, 0, false, MainRange); +} + uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) { uint16 MaxDmg = (((2 * wDmg) * GetDamageTable(SkillThrowing)) / 100);