void CParty::ReloadParty() { auto info = GetPartyInfo(); //alliance if (this->m_PAlliance != nullptr) { for (auto&& party : m_PAlliance->partyList) { party->RefreshFlags(info); for (auto&& member : party->members) { CCharEntity* PChar = (CCharEntity*)member; PChar->ReloadPartyDec(); uint16 alliance = 0; PChar->pushPacket(new CPartyDefinePacket(party)); //auto effects = std::make_unique<CPartyEffectsPacket>(); uint8 j = 0; for (auto&& memberinfo : info) { if ((memberinfo.flags & (PARTY_SECOND | PARTY_THIRD)) != alliance) { alliance = memberinfo.flags & (PARTY_SECOND | PARTY_THIRD); j = 0; } auto PPartyMember = zoneutils::GetChar(memberinfo.id); if (PPartyMember) { PChar->pushPacket(new CPartyMemberUpdatePacket(PPartyMember, j, memberinfo.flags, PChar->getZone())); //if (memberinfo.partyid == party->GetPartyID() && PPartyMember != PChar) // effects->AddMemberEffects(PChar); } else { uint16 zoneid = memberinfo.zone == 0 ? memberinfo.prev_zone : memberinfo.zone; PChar->pushPacket(new CPartyMemberUpdatePacket( memberinfo.id, (const int8*)memberinfo.name.c_str(), memberinfo.flags, j, zoneid)); } j++; } //PChar->pushPacket(effects.release()); } } } else { RefreshFlags(info); //regular party for (uint8 i = 0; i < members.size(); ++i) { CCharEntity* PChar = (CCharEntity*)members.at(i); PChar->PLatentEffectContainer->CheckLatentsPartyJobs(); PChar->PLatentEffectContainer->CheckLatentsPartyMembers(members.size()); PChar->PLatentEffectContainer->CheckLatentsPartyAvatar(); PChar->ReloadPartyDec(); PChar->pushPacket(new CPartyDefinePacket(this)); //auto effects = std::make_unique<CPartyEffectsPacket>(); uint8 j = 0; for (auto&& memberinfo : info) { auto PPartyMember = zoneutils::GetChar(memberinfo.id); if (PPartyMember) { PChar->pushPacket(new CPartyMemberUpdatePacket(PPartyMember, j, memberinfo.flags, PChar->getZone())); //if (PPartyMember != PChar) // effects->AddMemberEffects(PChar); } else { uint16 zoneid = memberinfo.zone == 0 ? memberinfo.prev_zone : memberinfo.zone; PChar->pushPacket(new CPartyMemberUpdatePacket( memberinfo.id, (const int8*)memberinfo.name.c_str(), memberinfo.flags, j, zoneid)); //effects->AddMemberEffects(memberinfo.id); } j++; } //PChar->pushPacket(effects.release()); } } }
int32 map_cleanup(uint32 tick, CTaskMgr::CTask* PTask) { map_session_list_t::iterator it = map_session_list.begin(); while(it != map_session_list.end()) { map_session_data_t* map_session_data = it->second; CCharEntity* PChar = map_session_data->PChar; if ((time(NULL) - map_session_data->last_update) > 5) { if (PChar != NULL && !(PChar->nameflags.flags & FLAG_DC)) { PChar->nameflags.flags |= FLAG_DC; if (PChar->status == STATUS_NORMAL) { PChar->status = STATUS_UPDATE; PChar->loc.zone->SpawnPCs(PChar); } } if ((time(NULL) - map_session_data->last_update) > map_config.max_time_lastupdate) { if (PChar != NULL) { //[Alliance] fix to stop server crashing: //if a party within an alliance only has 1 char (that char will be party leader) //if char then disconnects we need to tell the server about the alliance change if(PChar->PParty != NULL && PChar->PParty->m_PAlliance != NULL && PChar->PParty->GetLeader() == PChar){ if(PChar->PParty->members.size() == 1){ if(PChar->PParty->m_PAlliance->partyList.size() == 2){ PChar->PParty->m_PAlliance->dissolveAlliance(); }else if(PChar->PParty->m_PAlliance->partyList.size() == 3){ PChar->PParty->m_PAlliance->removeParty(PChar->PParty); } } } // uncharm pet if player d/c if (PChar->PPet != NULL && PChar->PPet->objtype == TYPE_MOB) petutils::DespawnPet(PChar); ShowDebug(CL_CYAN"map_cleanup: %s timed out, closing session\n" CL_RESET, PChar->GetName()); PChar->status = STATUS_SHUTDOWN; PacketParser[0x00D](map_session_data, PChar, 0); } else if(!map_session_data->shuttingDown){ ShowWarning(CL_YELLOW"map_cleanup: WHITHOUT CHAR timed out, session closed\n" CL_RESET); const int8* Query = "DELETE FROM accounts_sessions WHERE client_addr = %u AND client_port = %u"; Sql_Query(SqlHandle, Query, map_session_data->client_addr, map_session_data->client_port); aFree(map_session_data->server_packet_data); map_session_list.erase(it++); delete map_session_data; continue; } } } else if (PChar != NULL && (PChar->nameflags.flags & FLAG_DC)) { PChar->nameflags.flags &= ~FLAG_DC; PChar->pushPacket(new CCharUpdatePacket(PChar)); if (PChar->status == STATUS_NORMAL) { PChar->status = STATUS_UPDATE; PChar->loc.zone->SpawnPCs(PChar); } charutils::SaveCharStats(PChar); } ++it; } return 0; }
void CParty::RemoveMember(CBattleEntity* PEntity) { DSP_DEBUG_BREAK_IF(PEntity == nullptr); DSP_DEBUG_BREAK_IF(PEntity->PParty != this); if (m_PLeader == PEntity) { RemovePartyLeader(PEntity); } else { for (uint32 i = 0; i < members.size(); ++i) { if (PEntity == members.at(i)) { members.erase(members.begin() + i); if (m_PartyType == PARTY_PCS) { CCharEntity* PChar = (CCharEntity*)PEntity; if (m_PQuaterMaster == PChar) { SetQuarterMaster(nullptr); } if (m_PSyncTarget == PChar) { SetSyncTarget(nullptr, 553); CStatusEffect* sync = PChar->StatusEffectContainer->GetStatusEffect(EFFECT_LEVEL_SYNC); if (sync && sync->GetDuration() == 0) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 30, 553)); sync->SetStartTime(server_clock::now()); sync->SetDuration(30000); } DisableSync(); } if (m_PSyncTarget != nullptr && m_PSyncTarget != PChar) { if (PChar->status != STATUS_DISAPPEAR) { CStatusEffect* sync = PChar->StatusEffectContainer->GetStatusEffect(EFFECT_LEVEL_SYNC); if (sync && sync->GetDuration() == 0) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 30, 553)); sync->SetStartTime(server_clock::now()); sync->SetDuration(30000); } } } PChar->PLatentEffectContainer->CheckLatentsPartyMembers(members.size()); PChar->pushPacket(new CPartyDefinePacket(nullptr)); PChar->pushPacket(new CPartyMemberUpdatePacket(PChar, 0, 0, PChar->getZone())); PChar->pushPacket(new CCharUpdatePacket(PChar)); PChar->PParty = nullptr; Sql_Query(SqlHandle, "DELETE FROM accounts_parties WHERE charid = %u;", PChar->id); uint8 data[4] {}; ref<uint32>(data, 0) = m_PartyID; message::send(MSG_PT_RELOAD, data, sizeof data, nullptr); if (PChar->PTreasurePool != nullptr && PChar->PTreasurePool->GetPoolType() != TREASUREPOOL_ZONE) { PChar->PTreasurePool->DelMember(PChar); PChar->PTreasurePool = new CTreasurePool(TREASUREPOOL_SOLO); PChar->PTreasurePool->AddMember(PChar); PChar->PTreasurePool->UpdatePool(PChar); } } break; } } } }
void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message_type, CBasicPacket* packet) { if (!packet) { return; } // Do not send packets that are updates of a hidden GM.. if (packet->id() == 0x00D && PEntity != nullptr && PEntity->objtype == TYPE_PC && ((CCharEntity*)PEntity)->m_isGMHidden) { // Ensure this packet is not despawning us.. if (packet->ref<uint8>(0x0A) != 0x20) { delete packet; return; } } if (!m_charList.empty()) { switch (message_type) { case CHAR_INRANGE_SELF: { if (PEntity->objtype == TYPE_PC) { ((CCharEntity*)PEntity)->pushPacket(new CBasicPacket(*packet)); } } case CHAR_INRANGE: { // todo: rewrite packet handlers and use enums instead of rawdog packet ids // 30 yalms if action packet, 50 otherwise const int checkDistanceSq = packet->id() == 0x0028 ? 900 : 2500; for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) { CCharEntity* PCurrentChar = (CCharEntity*)it->second; if (PEntity != PCurrentChar) { if (distanceSquared(PEntity->loc.p, PCurrentChar->loc.p) < checkDistanceSq && ((PEntity->objtype != TYPE_PC) || (((CCharEntity*)PEntity)->m_moghouseID == PCurrentChar->m_moghouseID))) { if (packet->id() == 0x00E && (packet->ref<uint8>(0x0A) != 0x20 || packet->ref<uint8>(0x0A) != 0x0F)) { uint32 id = packet->ref<uint32>(0x04); uint16 targid = packet->ref<uint16>(0x08); CBaseEntity* entity = GetEntity(targid); SpawnIDList_t spawnlist; if (entity) { if (entity->targid < 0x400) { if (entity->objtype == TYPE_MOB) { spawnlist = PCurrentChar->SpawnMOBList; } else if (entity->objtype == TYPE_NPC) { spawnlist = PCurrentChar->SpawnNPCList; } } else if (entity->targid >= 0x700) { spawnlist = PCurrentChar->SpawnPETList; } else { entity = nullptr; } } if (!entity) { // got a char or nothing as the target of this entity update (which really shouldn't happen ever) // so we're just going to skip this packet break; } SpawnIDList_t::iterator iter = spawnlist.lower_bound(id); if (!(iter == spawnlist.end() || spawnlist.key_comp()(id, iter->first))) { PCurrentChar->pushPacket(new CBasicPacket(*packet)); } } else { PCurrentChar->pushPacket(new CBasicPacket(*packet)); } } } } } break; case CHAR_INSHOUT: { for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) { CCharEntity* PCurrentChar = (CCharEntity*)it->second; if (PEntity != PCurrentChar) { if (distance(PEntity->loc.p, PCurrentChar->loc.p) < 180 && ((PEntity->objtype != TYPE_PC) || (((CCharEntity*)PEntity)->m_moghouseID == PCurrentChar->m_moghouseID))) { PCurrentChar->pushPacket(new CBasicPacket(*packet)); } } } } break; case CHAR_INZONE: { for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) { CCharEntity* PCurrentChar = (CCharEntity*)it->second; if (PCurrentChar->m_moghouseID == 0) { if (PEntity != PCurrentChar) { PCurrentChar->pushPacket(new CBasicPacket(*packet)); } } } } break; } } delete packet; }
int32 parse(int8* buff, size_t* buffsize, sockaddr_in* from, map_session_data_t* map_session_data) { // начало обработки входящего пакета int8* PacketData_Begin = &buff[FFXI_HEADER_SIZE]; int8* PacketData_End = &buff[*buffsize]; CCharEntity *PChar = map_session_data->PChar; uint16 SmallPD_Size = 0; uint16 SmallPD_Type = 0; uint16 SmallPD_Code = RBUFW(buff,0); for(int8* SmallPD_ptr = PacketData_Begin; SmallPD_ptr + (RBUFB(SmallPD_ptr,1) & 0xFE)*2 <= PacketData_End && (RBUFB(SmallPD_ptr,1) & 0xFE); SmallPD_ptr = SmallPD_ptr + SmallPD_Size*2) { SmallPD_Size = (RBUFB(SmallPD_ptr,1) & 0x0FE); SmallPD_Type = (RBUFW(SmallPD_ptr,0) & 0x1FF); if(PacketSize[SmallPD_Type] == SmallPD_Size || PacketSize[SmallPD_Type] == 0) // Tests incoming packets for the correct size prior to processing { // если код текущего пакета меньше либо равен последнему полученному // или больше глобального то игнорируем пакет if ((RBUFW(SmallPD_ptr,2) <= map_session_data->client_packet_id) || (RBUFW(SmallPD_ptr,2) > SmallPD_Code)) { continue; } if (SmallPD_Type != 0x15) { ShowInfo("parse: %03hX | %04hX %04hX %02hX from user: %s\n", SmallPD_Type, RBUFW(SmallPD_ptr,2), RBUFW(buff,2), SmallPD_Size, PChar->GetName()); } if (PChar->loc.zone == NULL && SmallPD_Type != 0x0A) { ShowWarning("This packet is unexpected from %s - Received %03hX earlier without matching 0x0A\n", PChar->GetName(), SmallPD_Type); } else { PacketParser[SmallPD_Type](map_session_data, PChar, SmallPD_ptr); } } else { ShowWarning("Bad packet size %03hX | %04hX %04hX %02hX from user: %s\n", SmallPD_Type, RBUFW(SmallPD_ptr,2), RBUFW(buff,2), SmallPD_Size, PChar->GetName()); } } map_session_data->client_packet_id = SmallPD_Code; // здесь мы проверяем, получил ли клиент предыдущий пакет // если не получил, то мы не создаем новый, а отправляем предыдущий if (RBUFW(buff,2) != map_session_data->server_packet_id) { WBUFW(map_session_data->server_packet_data,2) = SmallPD_Code; WBUFW(map_session_data->server_packet_data,8) = (uint32)time(NULL); g_PBuff = map_session_data->server_packet_data; *buffsize = map_session_data->server_packet_size; map_session_data->server_packet_data = buff; return -1; } // увеличиваем номер отправленного пакета только в случае отправки новых данных map_session_data->server_packet_id += 1; // собираем большой пакет, состоящий из нескольких маленьких CBasicPacket* PSmallPacket; *buffsize = FFXI_HEADER_SIZE; while(!PChar->isPacketListEmpty() && *buffsize + PChar->firstPacketSize()*2 < map_config.buffer_size ) { PSmallPacket = PChar->popPacket(); PSmallPacket->setCode(map_session_data->server_packet_id); memcpy(buff+*buffsize, PSmallPacket, PSmallPacket->getSize()*2); *buffsize += PSmallPacket->getSize()*2; delete PSmallPacket; } return 0; }
void CStatusEffectContainer::CheckRegen(uint32 tick) { DSP_DEBUG_BREAK_IF(m_POwner == nullptr); if (!m_POwner->isDead()) { if ((tick - m_RegenCheckTime) < 3000 ) { return; } CCharEntity* PChar = nullptr; if(m_POwner->objtype == TYPE_PC) { PChar = (CCharEntity*)m_POwner; } m_RegenCheckTime = tick; int16 regen = m_POwner->getMod(MOD_REGEN); int16 poison = m_POwner->getMod(MOD_REGEN_DOWN); int16 refresh = m_POwner->getMod(MOD_REFRESH) - m_POwner->getMod(MOD_REFRESH_DOWN); int16 regain = m_POwner->getMod(MOD_REGAIN) - m_POwner->getMod(MOD_REGAIN_DOWN); bool update = false; if (m_POwner->addHP(regen)) update = true; if(poison) { int16 damage = battleutils::HandleStoneskin(m_POwner, poison); if(damage > 0) { DelStatusEffectSilent(EFFECT_HEALING); m_POwner->addHP(-damage); WakeUp(); update = true; } } if (m_POwner->getMod(MOD_AVATAR_PERPETUATION) > 0 && (m_POwner->objtype == TYPE_PC)) { int16 perpetuation = m_POwner->getMod(MOD_AVATAR_PERPETUATION); if (m_POwner->StatusEffectContainer->HasStatusEffect(EFFECT_ASTRAL_FLOW)) perpetuation = 0; else { if (m_POwner->PPet != nullptr && PChar != nullptr) { if(m_POwner->PPet->objtype == TYPE_PET) { CPetEntity* PPet = (CPetEntity*)m_POwner->PPet; CItem* hands = PChar->getEquip(SLOT_HANDS); // carbuncle mitts only work on carbuncle if (hands && hands->getID() == 14062 && PPet->name == "Carbuncle"){ perpetuation /= 2; } } perpetuation -= charutils::AvatarPerpetuationReduction(PChar); if( perpetuation < 1 ) perpetuation = 1; } } if (m_POwner->addMP(refresh - perpetuation)) update = true; if( m_POwner->health.mp == 0 && m_POwner->PPet != nullptr && m_POwner->PPet->objtype == TYPE_PET) { CPetEntity* PPet = (CPetEntity*)m_POwner->PPet; if (PPet->getPetType() == PETTYPE_AVATAR) { petutils::DespawnPet(m_POwner); } } } else { if (m_POwner->addMP(refresh)) update = true; } if (m_POwner->addTP(regain)) update = true; if (m_POwner->PPet && ((CPetEntity*)(m_POwner->PPet))->getPetType() == PETTYPE_AUTOMATON) { ((CAutomatonEntity*)(m_POwner->PPet))->burdenTick(); } if( m_POwner->status != STATUS_DISAPPEAR && (m_POwner->objtype == TYPE_PC) && update) { charutils::UpdateHealth((CCharEntity*)m_POwner); } } }
void CZone::DecreaseZoneCounter(CCharEntity* PChar) { DSP_DEBUG_BREAK_IF(PChar == NULL); DSP_DEBUG_BREAK_IF(PChar->loc.zone != this); //remove pets if(PChar->PPet != NULL) { charutils::BuildingCharPetAbilityTable(PChar,(CPetEntity*)PChar->PPet,0);//blank the pet commands if(PChar->PPet->isCharmed) { petutils::DespawnPet(PChar); } else { PChar->PPet->status = STATUS_DISAPPEAR; if( ((CPetEntity*)(PChar->PPet))->getPetType() == PETTYPE_AVATAR ) PChar->setModifier(MOD_AVATAR_PERPETUATION, 0); } // It may have been nulled by DespawnPet if(PChar->PPet != NULL) { PChar->PPet->PBattleAI->SetCurrentAction(ACTION_NONE); DeletePET(PChar->PPet);//remove the TID for this pet for (EntityList_t::const_iterator it = m_charList.begin() ; it != m_charList.end() ; ++it) { //inform other players of the pets removal CCharEntity* PCurrentChar = (CCharEntity*)it->second; SpawnIDList_t::iterator PET = PCurrentChar->SpawnPETList.find(PChar->PPet->id); if( PET != PCurrentChar->SpawnPETList.end() ) { PCurrentChar->SpawnPETList.erase(PET); PCurrentChar->pushPacket(new CEntityUpdatePacket(PChar->PPet, ENTITY_DESPAWN)); } } PChar->PPet = NULL; } } //remove bcnm status if(m_InstanceHandler != NULL && PChar->StatusEffectContainer->HasStatusEffect(EFFECT_BATTLEFIELD)) { if(m_InstanceHandler->disconnectFromBcnm(PChar)){ ShowDebug("Removed %s from the BCNM they were in as they have left the zone.\n",PChar->GetName()); } if(PChar->loc.destination==0){ //this player is disconnecting/logged out, so move them to the entrance //move depending on zone int pos[4] = {0,0,0,0}; instanceutils::getStartPosition(m_zoneID,pos); if(pos!=NULL){ PChar->loc.p.x = pos[0]; PChar->loc.p.y = pos[1]; PChar->loc.p.z = pos[2]; PChar->loc.p.rotation = pos[3]; charutils::SaveCharPosition(PChar); } else{ ShowWarning("%s has disconnected from the BCNM but cannot move them to the lobby as the lobby position is unknown!\n",PChar->GetName()); } } } else if(m_InstanceHandler != NULL && PChar->StatusEffectContainer->HasStatusEffect(EFFECT_DYNAMIS, 0)) { if(m_InstanceHandler->disconnectFromDynamis(PChar)){ ShowDebug("Removed %s from the BCNM they were in as they have left the zone.\n",PChar->GetName()); } if(PChar->loc.destination==0){ //this player is disconnecting/logged out, so move them to the entrance //move depending on zone int pos[4] = {0,0,0,0}; instanceutils::getStartPosition(m_zoneID,pos); if(pos!=NULL){ PChar->loc.p.x = pos[0]; PChar->loc.p.y = pos[1]; PChar->loc.p.z = pos[2]; PChar->loc.p.rotation = pos[3]; charutils::SaveCharPosition(PChar); } else{ ShowWarning("%s has disconnected from the BCNM but cannot move them to the lobby as the lobby position is unknown!\n",PChar->GetName()); } } } for (EntityList_t::const_iterator it = m_mobList.begin() ; it != m_mobList.end() ; ++it) { CMobEntity* PCurrentMob = (CMobEntity*)it->second; PCurrentMob->PEnmityContainer->Clear(PChar->id); if(PCurrentMob->m_OwnerID.id == PChar->id){ PCurrentMob->m_OwnerID.clean(); } } // TODO: могут возникать проблемы с переходом между одной и той же зоной (zone == prevzone) m_charList.erase(PChar->targid); ShowDebug(CL_CYAN"CZone:: %s DecreaseZoneCounter <%u> %s\n" CL_RESET, GetName(), m_charList.size(),PChar->GetName()); if (ZoneTimer && m_charList.empty()) { ZoneTimer->m_type = CTaskMgr::TASK_REMOVE; ZoneTimer = NULL; HealAllMobs(); } else { for (EntityList_t::const_iterator it = m_charList.begin() ; it != m_charList.end() ; ++it) { CCharEntity* PCurrentChar = (CCharEntity*)it->second; SpawnIDList_t::iterator PC = PCurrentChar->SpawnPCList.find(PChar->id); if( PC != PCurrentChar->SpawnPCList.end() ) { PCurrentChar->SpawnPCList.erase(PC); PCurrentChar->pushPacket(new CCharPacket(PChar,ENTITY_DESPAWN)); } } } if (PChar->m_LevelRestriction != 0) { PChar->StatusEffectContainer->DelStatusEffectSilent(EFFECT_LEVEL_SYNC); PChar->StatusEffectContainer->DelStatusEffectSilent(EFFECT_LEVEL_RESTRICTION); } if (PChar->PTreasurePool != NULL) // TODO: условие для устранения проблем с MobHouse, надо блин решить ее раз и навсегда { PChar->PTreasurePool->DelMember(PChar); } for (regionList_t::const_iterator region = m_regionList.begin(); region != m_regionList.end(); ++region) { if ((*region)->GetRegionID() == PChar->m_InsideRegionID) { luautils::OnRegionLeave(PChar, *region); break; } } PChar->loc.zone = NULL; PChar->loc.prevzone = m_zoneID; PChar->SpawnPCList.clear(); PChar->SpawnNPCList.clear(); PChar->SpawnMOBList.clear(); PChar->SpawnPETList.clear(); }
void CParty::SetSyncTarget(CBattleEntity* PEntity, uint16 message) { if (map_config.level_sync_enable) { if (PEntity && PEntity->objtype == TYPE_PC) { CCharEntity* PChar = (CCharEntity*)PEntity; //enable level sync if (PChar->GetMLevel() < 10 ) { ((CCharEntity*)GetLeader())->pushPacket(new CMessageBasicPacket((CCharEntity*)GetLeader(), (CCharEntity*)GetLeader(), 0, 10, 541)); return; } else if (PChar->getZone() != GetLeader()->getZone()) { ((CCharEntity*)GetLeader())->pushPacket(new CMessageBasicPacket((CCharEntity*)GetLeader(), (CCharEntity*)GetLeader(), 0, 0, 542)); return; } else { for (uint32 i = 0; i < members.size(); ++i) { if(members.at(i)->StatusEffectContainer->HasStatusEffect(EFFECT_LEVEL_RESTRICTION)) { ((CCharEntity*)GetLeader())->pushPacket(new CMessageBasicPacket((CCharEntity*)GetLeader(), (CCharEntity*)GetLeader(), 0, 0, 543)); return; } } m_PSyncTarget = PChar; for (uint32 i = 0; i < members.size(); ++i) { if(members.at(i)->objtype != TYPE_PC) continue; CCharEntity* member = (CCharEntity*)members.at(i); if (member->status != STATUS_DISAPPEAR && member->getZone() == PChar->getZone() ) { member->pushPacket(new CMessageStandardPacket(PChar->GetMLevel(), 0, 0, 0, message)); member->StatusEffectContainer->AddStatusEffect(new CStatusEffect( EFFECT_LEVEL_SYNC, EFFECT_LEVEL_SYNC, PChar->GetMLevel(), 0, 0), true); member->StatusEffectContainer->DelStatusEffectsByFlag(EFFECTFLAG_DISPELABLE); member->loc.zone->PushPacket(member, CHAR_INRANGE, new CCharSyncPacket(member)); } } } } else { if (m_PSyncTarget != NULL) { //disable level sync for (uint32 i = 0; i < members.size(); ++i) { if(members.at(i)->objtype != TYPE_PC) continue; CCharEntity* member = (CCharEntity*)members.at(i); if (member->status != STATUS_DISAPPEAR && member->getZone() == m_PSyncTarget->getZone() ) { CStatusEffect* sync = member->StatusEffectContainer->GetStatusEffect(EFFECT_LEVEL_SYNC); if (sync && sync->GetDuration() == 0) { member->pushPacket(new CMessageBasicPacket(member, member, 10, 30, message)); sync->SetStartTime(gettick()); sync->SetDuration(30000); } } } } } } }
/************************************************************************ * * * Tick regen/refresh/regain effects * * * ************************************************************************/ void CStatusEffectContainer::CheckRegen(uint32 tick) { DSP_DEBUG_BREAK_IF(m_POwner == NULL); if (!m_POwner->isDead()) { if ((tick - m_RegenCheckTime) < 3000 ) { return; } CCharEntity* PChar = NULL; if(m_POwner->objtype == TYPE_PC) { PChar = (CCharEntity*)m_POwner; } m_RegenCheckTime = tick; int8 regen = m_POwner->getMod(MOD_REGEN); int8 poison = m_POwner->getMod(MOD_REGEN_DOWN); int8 refresh = m_POwner->getMod(MOD_REFRESH) - m_POwner->getMod(MOD_REFRESH_DOWN); int16 regain = m_POwner->getMod(MOD_REGAIN) - m_POwner->getMod(MOD_REGAIN_DOWN); m_POwner->addHP(regen); if(poison) { int16 damage = battleutils::HandleStoneskin(m_POwner, poison); if(damage > 0) { DelStatusEffectSilent(EFFECT_HEALING); m_POwner->addHP(-damage); WakeUp(); } } if (m_POwner->getMod(MOD_AVATAR_PERPETUATION) > 0 && (m_POwner->objtype == TYPE_PC)) { int8 perpetuation = m_POwner->getMod(MOD_AVATAR_PERPETUATION); if (m_POwner->StatusEffectContainer->HasStatusEffect(EFFECT_ASTRAL_FLOW)) perpetuation = 0; else { if (m_POwner->PPet != NULL && PChar != NULL) { if(m_POwner->PPet->objtype == TYPE_PET) { CPetEntity* PPet = (CPetEntity*)m_POwner->PPet; CItem* hands = PChar->getEquip(SLOT_HANDS); // carbuncle mitts only work on carbuncle if (hands && hands->getID() == 14062 && PPet->name == "Carbuncle"){ perpetuation /= 2; } } perpetuation -= charutils::AvatarPerpetuationReduction(PChar); if( perpetuation < 1 ) perpetuation = 1; } } m_POwner->addMP(refresh - perpetuation); if( m_POwner->health.mp == 0 && m_POwner->PPet != NULL && m_POwner->PPet->objtype == TYPE_PET) { CPetEntity* PPet = (CPetEntity*)m_POwner->PPet; if (PPet->getPetType() == PETTYPE_AVATAR) { petutils::DespawnPet(m_POwner); } } } else { m_POwner->addMP(refresh); } if(PChar != NULL && IsAsleep()) { CItem* neck = PChar->getEquip(SLOT_NECK); // opo-opo necklace if(neck != NULL && neck->getID() == 13143) { // add tp regain += 2.5f; } } m_POwner->addTP(regain); if( m_POwner->status != STATUS_DISAPPEAR && (m_POwner->objtype == TYPE_PC)) { charutils::UpdateHealth((CCharEntity*)m_POwner); } } }
void CLinkshell::RemoveMemberByName(int8* MemberName) { for (uint32 i = 0; i < members.size(); ++i) { if (strcmp(MemberName, members.at(i)->GetName()) == 0) { CCharEntity* PMember = (CCharEntity*)members.at(i); CItemLinkshell* PItemLinkshell = (CItemLinkshell*)PMember->getEquip(SLOT_LINK1); SLOTTYPE slot = SLOT_LINK1; int lsNum = 1; if (PItemLinkshell->GetLSID() != this->getID()) { PItemLinkshell = (CItemLinkshell*)PMember->getEquip(SLOT_LINK2); slot = SLOT_LINK2; lsNum = 2; } if (PItemLinkshell != nullptr && PItemLinkshell->isType(ITEM_LINKSHELL)) { linkshell::DelOnlineMember(PMember, PItemLinkshell); PItemLinkshell->setSubType(ITEM_UNLOCKED); PMember->equip[slot] = 0; if (slot == SLOT_LINK1) { PMember->nameflags.flags &= ~FLAG_LINKSHELL; PMember->updatemask |= UPDATE_HP; } PMember->pushPacket(new CInventoryAssignPacket(PItemLinkshell, INV_NORMAL)); PMember->pushPacket(new CLinkshellEquipPacket(PMember,lsNum)); } CItemContainer* Inventory = PMember->getStorage(LOC_INVENTORY); for (uint8 SlotID = 0; SlotID < Inventory->GetSize(); ++SlotID) { CItemLinkshell* PItemLinkshell = (CItemLinkshell*)Inventory->GetItem(SlotID); if (PItemLinkshell != nullptr && PItemLinkshell->isType(ITEM_LINKSHELL) && PItemLinkshell->GetLSID() == m_id) { const int8* Query = "UPDATE char_inventory SET itemid = (itemid+2) WHERE charid = %u AND location = %u AND slot = %u LIMIT 1"; Sql_Query(SqlHandle, Query, PMember->id, LOC_INVENTORY, SlotID); PItemLinkshell->SetLSID(0); PItemLinkshell->setID(PItemLinkshell->getID() + 2); PMember->pushPacket(new CInventoryItemPacket(PItemLinkshell, LOC_INVENTORY, SlotID)); } } charutils::SaveCharStats(PMember); charutils::SaveCharEquip(PMember); PMember->pushPacket(new CInventoryFinishPacket()); PMember->pushPacket(new CCharUpdatePacket(PMember)); PMember->pushPacket(new CMessageSystemPacket(0,0,109)); return; } } }
void CParty::ReloadParty() { //check if partyflags have changed int ret = Sql_Query(SqlHandle, "SELECT charid, partyflag FROM accounts_parties WHERE partyid = %d;", m_PartyID); if (ret != SQL_ERROR && Sql_NumRows(SqlHandle) > 0) { uint8 j = 0; while (Sql_NextRow(SqlHandle) == SQL_SUCCESS) { uint32 charid = Sql_GetUIntData(SqlHandle, 0); uint16 memberflags = Sql_GetUIntData(SqlHandle, 1); if (memberflags & PARTY_LEADER) { bool found = false; for (auto member : members) { if (member->id == charid) { m_PLeader = member; found = true; } } if (!found) { m_PLeader = nullptr; } } if (memberflags & PARTY_QM) { bool found = false; for (auto member : members) { if (member->id == charid) { m_PQuaterMaster = member; found = true; } } if (!found) { m_PQuaterMaster = nullptr; } } if (memberflags & ALLIANCE_LEADER && m_PAlliance) { bool found = false; for (auto member : members) { if (member->id == charid) { m_PAlliance->setMainParty(this); found = true; } } if (!found) { m_PAlliance->setMainParty(nullptr); } } } } //alliance if (this->m_PAlliance != nullptr) { for (uint8 a = 0; a < m_PAlliance->partyList.size(); ++a) { for (uint8 i = 0; i < m_PAlliance->partyList.at(a)->members.size(); ++i) { CCharEntity* PChar = (CCharEntity*)m_PAlliance->partyList.at(a)->members.at(i); PChar->ReloadPartyDec(); uint16 alliance = 0; PChar->pushPacket(new CPartyDefinePacket(m_PAlliance->partyList.at(a))); int ret = Sql_Query(SqlHandle, "SELECT chars.charid, chars.charname, partyflag, pos_zone, pos_prevzone FROM accounts_parties \ LEFT JOIN chars ON accounts_parties.charid = chars.charid WHERE \ allianceid = %d ORDER BY partyflag & %u, timestamp;", m_PAlliance->m_AllianceID, PARTY_SECOND | PARTY_THIRD); if (ret != SQL_ERROR && Sql_NumRows(SqlHandle) > 0) { uint8 j = 0; while (Sql_NextRow(SqlHandle) == SQL_SUCCESS) { if ((Sql_GetUIntData(SqlHandle, 2) & (PARTY_SECOND | PARTY_THIRD)) != alliance) { alliance = Sql_GetUIntData(SqlHandle, 2) & (PARTY_SECOND | PARTY_THIRD); j = 0; } CCharEntity* PPartyMember = zoneutils::GetChar(Sql_GetUIntData(SqlHandle, 0)); if (PPartyMember) { PChar->pushPacket(new CPartyMemberUpdatePacket(PPartyMember, j, PChar->getZone())); } else { uint16 zoneid = Sql_GetUIntData(SqlHandle, 3) == 0 ? Sql_GetUIntData(SqlHandle, 4) : Sql_GetUIntData(SqlHandle, 3); PChar->pushPacket(new CPartyMemberUpdatePacket( Sql_GetUIntData(SqlHandle, 0), Sql_GetData(SqlHandle, 1), Sql_GetUIntData(SqlHandle, 2), zoneid)); } j++; } } } } } else //regular party for (uint8 i = 0; i < members.size(); ++i)
void CLinkshell::ChangeMemberRank(int8* MemberName, uint8 toSack) { //topearl = 3 //tosack = 2 int newId = 512 + toSack; if (newId == 514 || newId == 515) { for (uint32 i = 0; i < members.size(); ++i) { if (strcmp(MemberName, members.at(i)->GetName()) == 0) { CCharEntity* PMember = (CCharEntity*)members.at(i); SLOTTYPE slot = SLOT_LINK1; int lsID = 1; if (PMember->PLinkshell2 == this) { lsID = 2; slot = SLOT_LINK2; } CItemLinkshell* PItemLinkshell = (CItemLinkshell*)PMember->getEquip(slot); if (PItemLinkshell != nullptr && PItemLinkshell->isType(ITEM_LINKSHELL)) { PItemLinkshell->setID(newId); PMember->pushPacket(new CInventoryAssignPacket(PItemLinkshell, INV_NORMAL)); PMember->pushPacket(new CLinkshellEquipPacket(PMember, lsID)); } CItemContainer* Inventory = PMember->getStorage(LOC_INVENTORY); for (uint8 SlotID = 0; SlotID < Inventory->GetSize(); ++SlotID) { CItemLinkshell* PItemLinkshell = (CItemLinkshell*)Inventory->GetItem(SlotID); if (PItemLinkshell != nullptr && PItemLinkshell->isType(ITEM_LINKSHELL) && PItemLinkshell->GetLSID() == m_id) { const int8* Query = "UPDATE char_inventory SET itemid = %u WHERE charid = %u AND location = %u AND slot = %u LIMIT 1"; Sql_Query(SqlHandle, Query, PItemLinkshell->getID(),PMember->id, LOC_INVENTORY, SlotID); if (lsID == 1) { Sql_Query(SqlHandle, "UPDATE accounts_sessions SET linkshellid1 = %u , linkshellrank1 = %u WHERE charid = %u", m_id, PItemLinkshell->GetLSType(), PMember->id); } else if (lsID == 2) { Sql_Query(SqlHandle, "UPDATE accounts_sessions SET linkshellid2 = %u , linkshellrank2 = %u WHERE charid = %u", m_id, PItemLinkshell->GetLSType(), PMember->id); } PMember->pushPacket(new CInventoryItemPacket(PItemLinkshell, LOC_INVENTORY, SlotID)); } } charutils::SaveCharStats(PMember); charutils::SaveCharEquip(PMember); PMember->pushPacket(new CInventoryFinishPacket()); PMember->pushPacket(new CCharUpdatePacket(PMember)); return; } } } }
bool CStatusEffectContainer::AddStatusEffect(CStatusEffect* PStatusEffect, bool silent) { if(PStatusEffect == NULL){ ShowWarning("status_effect_container::AddStatusEffect Status effect given was NULL!\n"); return false; } uint16 statusId = PStatusEffect->GetStatusID(); if(statusId >= MAX_EFFECTID){ ShowWarning("status_effect_container::AddStatusEffect statusId given is OVER limit %d\n", statusId); return false; } if(CanGainStatusEffect((EFFECT)statusId, PStatusEffect->GetPower())) { // remove clean up other effects OverwriteStatusEffect(PStatusEffect); PStatusEffect->SetOwner(m_POwner); SetEffectParams(PStatusEffect); // remove effects with same type DelStatusEffectsByType(PStatusEffect->GetType()); PStatusEffect->SetStartTime(gettick()); m_StatusEffectList.push_back(PStatusEffect); luautils::OnEffectGain(m_POwner, PStatusEffect); m_POwner->addModifiers(&PStatusEffect->modList); if( m_POwner->health.maxhp != 0) //make sure we're not in the middle of logging in { m_POwner->UpdateHealth(); } if (m_POwner->objtype == TYPE_PC) { CCharEntity* PChar = (CCharEntity*)m_POwner; if (PStatusEffect->GetIcon() != 0) { UpdateStatusIcons(); } if (PChar->status == STATUS_NORMAL) PChar->status = STATUS_UPDATE; if( m_POwner->health.maxhp != 0) //make sure we're not in the middle of logging in { //check for latents CLatentEffectContainer* PLatentEffectContainer; PChar->PLatentEffectContainer->CheckLatentsFoodEffect(); PChar->PLatentEffectContainer->CheckLatentsStatusEffect(); PChar->UpdateHealth(); PChar->pushPacket(new CCharHealthPacket(PChar)); } PChar->pushPacket(new CCharSyncPacket(PChar)); } return true; } return false; }
/************************************************************************ * * * Creates up to many attacks for a particular hand. * * * ************************************************************************/ void CAttackRound::CreateAttacks(CItemWeapon* PWeapon, PHYSICAL_ATTACK_DIRECTION direction) { uint8 num = 1; bool isPC = m_attacker->objtype == TYPE_PC; // Checking the players weapon hit count if (PWeapon->getReqLvl() <= m_attacker->GetMLevel()) { num = PWeapon->getHitCount(); } // If the attacker is a mobentity or derived from mobentity, check to see if it has any special mutli-hit capabilties if (dynamic_cast<CMobEntity*>(m_attacker)) { auto multiHitMax = (uint8)static_cast<CMobEntity*>(m_attacker)->getMobMod(MOBMOD_MULTI_HIT); if (multiHitMax > 0) num = 1 + battleutils::getHitCount(multiHitMax); } AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, num); // Checking the players triple, double and quadruple attack int16 tripleAttack = m_attacker->getMod(Mod::TRIPLE_ATTACK); int16 doubleAttack = m_attacker->getMod(Mod::DOUBLE_ATTACK); int16 quadAttack = m_attacker->getMod(Mod::QUAD_ATTACK); //check for merit upgrades if (isPC) { CCharEntity* PChar = (CCharEntity*)m_attacker; //merit chance only applies if player has the job trait if (charutils::hasTrait(PChar, TRAIT_TRIPLE_ATTACK)) tripleAttack += PChar->PMeritPoints->GetMeritValue(MERIT_TRIPLE_ATTACK_RATE, PChar); // Ambush Augment adds +1% Triple Attack per merit (need to satisfy conditions for Ambush) if (charutils::hasTrait(PChar, TRAIT_AMBUSH) && PChar->getMod(Mod::AUGMENTS_AMBUSH) > 0 && abs(m_defender->loc.p.rotation - m_attacker->loc.p.rotation) < 23) { tripleAttack += PChar->PMeritPoints->GetMerit(MERIT_AMBUSH)->count; } if (charutils::hasTrait(PChar, TRAIT_DOUBLE_ATTACK)) doubleAttack += PChar->PMeritPoints->GetMeritValue(MERIT_DOUBLE_ATTACK_RATE, PChar); // TODO: Quadruple attack merits when SE release them. } quadAttack = std::clamp<int16>(quadAttack, 0, 100); doubleAttack = std::clamp<int16>(doubleAttack, 0, 100); tripleAttack = std::clamp<int16>(tripleAttack, 0, 100); // Checking Mikage Effect - Hits Vary With Num of Utsusemi Shadows for Main Weapon if (m_attacker->StatusEffectContainer->HasStatusEffect(EFFECT_MIKAGE) && m_attacker->m_Weapons[SLOT_MAIN]->getID() == PWeapon->getID()) { auto shadows = (uint8)m_attacker->getMod(Mod::UTSUSEMI); //ShowDebug(CL_CYAN"Create Attacks: Mikage Active, Rolling Attack Chance for %d Shadowss...\n" CL_RESET, shadows); AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, shadows); } else if (num == 1 && dsprand::GetRandomNumber(100) < quadAttack) AddAttackSwing(PHYSICAL_ATTACK_TYPE::QUAD, direction, 3); else if (num == 1 && dsprand::GetRandomNumber(100) < tripleAttack) AddAttackSwing(PHYSICAL_ATTACK_TYPE::TRIPLE, direction, 2); else if (num == 1 && dsprand::GetRandomNumber(100) < doubleAttack) AddAttackSwing(PHYSICAL_ATTACK_TYPE::DOUBLE, direction, 1); // Apply Mythic OAT mods (mainhand only) if (direction == PHYSICAL_ATTACK_DIRECTION::RIGHTATTACK) { int16 occAttThriceRate = std::clamp<int16>(m_attacker->getMod(Mod::MYTHIC_OCC_ATT_THRICE), 0, 100); int16 occAttTwiceRate = std::clamp<int16>(m_attacker->getMod(Mod::MYTHIC_OCC_ATT_TWICE), 0, 100); if (num == 1 && dsprand::GetRandomNumber(100) < occAttThriceRate) { AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, 2); } else if (num == 1 && dsprand::GetRandomNumber(100) < occAttTwiceRate) { AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, 1); } } // Ammo extra swing - players only if (isPC && m_attacker->getMod(Mod::AMMO_SWING) > 0) { // Check for ammo CCharEntity* PChar = (CCharEntity*)m_attacker; CItemArmor* PAmmo = PChar->getEquip(SLOT_AMMO); CItemArmor* PMain = PChar->getEquip(SLOT_MAIN); CItemArmor* PSub = PChar->getEquip(SLOT_SUB); uint8 slot = PChar->equip[SLOT_AMMO]; uint8 loc = PChar->equipLoc[SLOT_AMMO]; uint8 ammoCount = 0; // Handedness check, checking mod of the weapon for the purposes of level scaling if (battleutils::GetScaledItemModifier(PChar, PMain, Mod::AMMO_SWING_TYPE) == 2 && dsprand::GetRandomNumber(100) < m_attacker->getMod(Mod::AMMO_SWING) && PAmmo != nullptr && ammoCount < PAmmo->getQuantity()) { AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, direction, 1); ammoCount += 1; } else { if (direction == RIGHTATTACK && battleutils::GetScaledItemModifier(PChar, PMain, Mod::AMMO_SWING_TYPE) == 1 && dsprand::GetRandomNumber(100) < m_attacker->getMod(Mod::AMMO_SWING) && PAmmo != nullptr && ammoCount < PAmmo->getQuantity()) { AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, RIGHTATTACK, 1); ammoCount += 1; } if (direction == LEFTATTACK && PSub != nullptr && battleutils::GetScaledItemModifier(PChar, PSub, Mod::AMMO_SWING_TYPE) == 1 && dsprand::GetRandomNumber(100) < m_attacker->getMod(Mod::AMMO_SWING) && PAmmo != nullptr && ammoCount < PAmmo->getQuantity()) { AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, LEFTATTACK, 1); ammoCount += 1; } } if (PAmmo != nullptr) { if (PAmmo->getQuantity() == ammoCount) { charutils::UnequipItem(PChar, SLOT_AMMO); charutils::SaveCharEquip(PChar); } charutils::UpdateItem(PChar, loc, slot, -ammoCount); PChar->pushPacket(new CInventoryFinishPacket()); } } // TODO: Possible Lua function for the nitty gritty stuff below. // Iga mod: Extra attack chance whilst dual wield is on. if (direction == LEFTATTACK && dsprand::GetRandomNumber(100) < m_attacker->getMod(Mod::EXTRA_DUAL_WIELD_ATTACK)) AddAttackSwing(PHYSICAL_ATTACK_TYPE::NORMAL, RIGHTATTACK, 1); }
void CParty::SetSyncTarget(int8* MemberName, uint16 message) { CBattleEntity* PEntity = nullptr; if (MemberName) { PEntity = GetMemberByName(MemberName); } if (map_config.level_sync_enable) { if (PEntity && PEntity->objtype == TYPE_PC) { CCharEntity* PChar = (CCharEntity*)PEntity; //enable level sync if (PChar->GetMLevel() < 10) { ((CCharEntity*)GetLeader())->pushPacket(new CMessageBasicPacket((CCharEntity*)GetLeader(), (CCharEntity*)GetLeader(), 0, 10, 541)); return; } else if (PChar->getZone() != GetLeader()->getZone()) { ((CCharEntity*)GetLeader())->pushPacket(new CMessageBasicPacket((CCharEntity*)GetLeader(), (CCharEntity*)GetLeader(), 0, 0, 542)); return; } else { for (uint8 i = 0; i < members.size(); ++i) { if (members.at(i)->StatusEffectContainer->HasStatusEffect({EFFECT_LEVEL_RESTRICTION, EFFECT_LEVEL_SYNC})) { ((CCharEntity*)GetLeader())->pushPacket(new CMessageBasicPacket((CCharEntity*)GetLeader(), (CCharEntity*)GetLeader(), 0, 0, 543)); return; } } m_PSyncTarget = PChar; for (uint8 i = 0; i < members.size(); ++i) { if (members.at(i)->objtype != TYPE_PC) continue; CCharEntity* member = (CCharEntity*)members.at(i); if (member->status != STATUS_DISAPPEAR && member->getZone() == PChar->getZone()) { member->pushPacket(new CMessageStandardPacket(PChar->GetMLevel(), 0, 0, 0, message)); member->StatusEffectContainer->AddStatusEffect(new CStatusEffect( EFFECT_LEVEL_SYNC, EFFECT_LEVEL_SYNC, PChar->GetMLevel(), 0, 0), true); member->StatusEffectContainer->DelStatusEffectsByFlag(EFFECTFLAG_DISPELABLE | EFFECTFLAG_ON_ZONE); member->loc.zone->PushPacket(member, CHAR_INRANGE, new CCharSyncPacket(member)); } } Sql_Query(SqlHandle, "UPDATE accounts_parties SET partyflag = partyflag & ~%d WHERE partyid = %u AND partyflag & %d", PARTY_SYNC, m_PartyID, PARTY_SYNC); Sql_Query(SqlHandle, "UPDATE accounts_parties SET partyflag = partyflag | %d WHERE partyid = %u AND charid = '%u';", PARTY_SYNC, m_PartyID, PChar->id); } } else { if (m_PSyncTarget != nullptr) { //disable level sync for (uint8 i = 0; i < members.size(); ++i) { if (members.at(i)->objtype != TYPE_PC) continue; CCharEntity* member = (CCharEntity*)members.at(i); if (member->status != STATUS_DISAPPEAR) { CStatusEffect* sync = member->StatusEffectContainer->GetStatusEffect(EFFECT_LEVEL_SYNC); if (sync && sync->GetDuration() == 0) { member->pushPacket(new CMessageBasicPacket(member, member, 10, 30, message)); sync->SetStartTime(server_clock::now()); sync->SetDuration(30000); } } } } m_PSyncTarget = nullptr; Sql_Query(SqlHandle, "UPDATE accounts_parties SET partyflag = partyflag & ~%d WHERE partyid = %u AND partyflag & %d", PARTY_SYNC, m_PartyID, PARTY_SYNC); } } }
/************************************************************************ * * * Creates up to many attacks for a particular hand. * * * ************************************************************************/ void CAttackRound::CreateAttacks(CItemWeapon* PWeapon, PHYSICAL_ATTACK_DIRECTION direction) { uint8 num = 1; // Checking the players weapon hit count if (PWeapon->getReqLvl() <= m_attacker->GetMLevel()) { num = PWeapon->getHitCount(); } AddAttackSwing(ATTACK_NORMAL, direction, num); // Checking the players triple, double and quadruple attack int16 tripleAttack = m_attacker->getMod(MOD_TRIPLE_ATTACK); int16 doubleAttack = m_attacker->getMod(MOD_DOUBLE_ATTACK); int16 quadAttack = m_attacker->getMod(MOD_QUAD_ATTACK); //check for merit upgrades if (m_attacker->objtype == TYPE_PC) { CCharEntity* PChar = (CCharEntity*)m_attacker; //merit chance only applies if player has the job trait if (charutils::hasTrait(PChar, TRAIT_TRIPLE_ATTACK)) tripleAttack += PChar->PMeritPoints->GetMeritValue(MERIT_TRIPLE_ATTACK_RATE, PChar); if (charutils::hasTrait(PChar, TRAIT_DOUBLE_ATTACK)) doubleAttack += PChar->PMeritPoints->GetMeritValue(MERIT_DOUBLE_ATTACK_RATE, PChar); // TODO: Quadruple attack merits when SE release them. } quadAttack = dsp_cap(quadAttack,0,100); doubleAttack = dsp_cap(doubleAttack,0,100); tripleAttack = dsp_cap(tripleAttack,0,100); // Checking Mikage Effect - Hits Vary With Num of Utsusemi Shadows for Main Weapon if (m_attacker->StatusEffectContainer->HasStatusEffect(EFFECT_MIKAGE) && m_attacker->m_Weapons[SLOT_MAIN]->getID() == PWeapon->getID()) { int16 shadows = m_attacker->getMod(MOD_UTSUSEMI); //ShowDebug(CL_CYAN"Create Attacks: Mikage Active, Rolling Attack Chance for %d Shadowss...\n" CL_RESET, shadows); AddAttackSwing(ATTACK_NORMAL, direction, shadows); } else if (num == 1 && dsprand::GetRandomNumber(100) < quadAttack) AddAttackSwing(QUAD_ATTACK, direction, 3); else if (num == 1 && dsprand::GetRandomNumber(100) < tripleAttack) AddAttackSwing(TRIPLE_ATTACK, direction, 2); else if (num == 1 && dsprand::GetRandomNumber(100) < doubleAttack) AddAttackSwing(DOUBLE_ATTACK, direction, 1); // Ammo extra swing - players only if (m_attacker->objtype == TYPE_PC && m_attacker->getMod(MOD_AMMO_SWING) > 0) { // Check for ammo CCharEntity* PChar = (CCharEntity*)m_attacker; CItemArmor* PAmmo = PChar->getEquip(SLOT_AMMO); uint8 slot = PChar->equip[SLOT_AMMO]; uint8 loc = PChar->equipLoc[SLOT_AMMO]; if (dsprand::GetRandomNumber(100) < m_attacker->getMod(MOD_AMMO_SWING)) { // Add swing, then subtract an ammo item, unequip if there's one left. AddAttackSwing(ATTACK_NORMAL, direction, 1); if (PAmmo->getQuantity() == 1) { charutils::UnequipItem(PChar, SLOT_AMMO); charutils::SaveCharEquip(PChar); } charutils::UpdateItem(PChar, loc, slot, -1); PChar->pushPacket(new CInventoryFinishPacket()); } } // TODO: Possible Lua function for the nitty gritty stuff below. // Iga mod: Extra attack chance whilst dual wield is on. if (direction == LEFTATTACK && dsprand::GetRandomNumber(100) < m_attacker->getMod(MOD_EXTRA_DUAL_WIELD_ATTACK)) AddAttackSwing(ATTACK_NORMAL, RIGHTATTACK, 1); }
void DespawnPet(CBattleEntity* PMaster) { DSP_DEBUG_BREAK_IF(PMaster->PPet == NULL); CBattleEntity* PPet = PMaster->PPet; // mob was not reset properly on death/uncharm // reset manually if (PPet->isCharmed && PMaster->objtype == TYPE_MOB) { PPet->isCharmed = false; PMaster->charmTime = 0; delete PPet->PBattleAI; PPet->PBattleAI = new CAIMobDummy((CMobEntity*)PMaster); PPet->PBattleAI->SetLastActionTime(gettick()); PPet->PBattleAI->SetCurrentAction(ACTION_FALL); ShowDebug("An ex charmed mob was not reset properly, Manually resetting it.\n"); return; } switch (PPet->objtype) { case TYPE_PET: { PPet->PBattleAI->SetCurrentAction(ACTION_FALL); if( ((CPetEntity*)PPet)->getPetType() == PETTYPE_AVATAR ) PMaster->setModifier(MOD_AVATAR_PERPETUATION, 0); if (PMaster->PParty != NULL) { for (uint8 i = 0; i < PMaster->PParty->members.size(); ++i) { CCharEntity* PMember = (CCharEntity*)PMaster->PParty->members.at(i); PMember->PLatentEffectContainer->CheckLatentsPartyAvatar(); } } CCharEntity* PChar = (CCharEntity*)PMaster; PChar->PLatentEffectContainer->CheckLatentsPartyAvatar(); } break; case TYPE_MOB: { if(PMaster->objtype == TYPE_PC) { CMobEntity* PMob = (CMobEntity*)PMaster->PPet; CCharEntity* PChar = (CCharEntity*)PMaster; // mobs charm wears off whist fighting another mob. Both mobs now attack player since mobs are no longer enemies if(PMob->PBattleAI != NULL && PMob->PBattleAI->GetBattleTarget() != NULL && PMob->PBattleAI->GetBattleTarget()->objtype == TYPE_MOB){ ((CMobEntity*)PMob->PBattleAI->GetBattleTarget())->PEnmityContainer->Clear(); ((CMobEntity*)PMob->PBattleAI->GetBattleTarget())->PEnmityContainer->UpdateEnmity(PChar, 0, 0); } //clear the ex-charmed mobs enmity PMob->PEnmityContainer->Clear(); // charm time is up, mob attacks player now if (PMob->GetHPP() != 0 && PMob->PMaster->GetHPP() != 0 && distance(PMob->loc.p, PMob->PMaster->loc.p) < 30) { PMob->PEnmityContainer->UpdateEnmity(PChar, 0, 0); } else { PMob->m_OwnerID.clean(); } // dirty exp if not full PMob->m_giveExp = PMob->GetHPP() == 100; //master using leave command if (PMaster->PBattleAI->GetCurrentAction() == ACTION_JOBABILITY_FINISH && PMaster->PBattleAI->GetCurrentJobAbility()->getID() == 55 || PChar->loc.zoning) { PMob->PEnmityContainer->Clear(); PMob->m_OwnerID.clean(); } PMob->isCharmed = false; PMob->charmTime = 0; PMob->PMaster = NULL; delete PMob->PBattleAI; PMob->PBattleAI = new CAIMobDummy(PMob); PMob->PBattleAI->SetLastActionTime(gettick()); if (PMob->GetHPP() == 0) PMob->PBattleAI->SetCurrentAction(ACTION_FALL); else PMob->PBattleAI->SetCurrentAction(ACTION_DISENGAGE); PChar->PPet = NULL; PChar->pushPacket(new CCharUpdatePacket(PChar)); PMob->loc.zone->PushPacket(PMob, CHAR_INRANGE, new CEntityUpdatePacket(PMob, ENTITY_UPDATE)); } } break; case TYPE_PC: { // освобождаем персонажа из под контроля } break; } }
void CZoneEntities::DecreaseZoneCounter(CCharEntity* PChar) { DSP_DEBUG_BREAK_IF(PChar == NULL); DSP_DEBUG_BREAK_IF(PChar->loc.zone != m_zone); //remove pets if (PChar->PPet != NULL) { charutils::BuildingCharPetAbilityTable(PChar, (CPetEntity*)PChar->PPet, 0);//blank the pet commands if (PChar->PPet->isCharmed) { petutils::DespawnPet(PChar); } else { PChar->PPet->status = STATUS_DISAPPEAR; if (((CPetEntity*)(PChar->PPet))->getPetType() == PETTYPE_AVATAR) PChar->setModifier(MOD_AVATAR_PERPETUATION, 0); } // It may have been nulled by DespawnPet if (PChar->PPet != NULL) { PChar->PPet->PBattleAI->SetCurrentAction(ACTION_NONE); DeletePET(PChar->PPet);//remove the TID for this pet for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) { //inform other players of the pets removal CCharEntity* PCurrentChar = (CCharEntity*)it->second; SpawnIDList_t::iterator PET = PCurrentChar->SpawnPETList.find(PChar->PPet->id); if (PET != PCurrentChar->SpawnPETList.end()) { PCurrentChar->SpawnPETList.erase(PET); PCurrentChar->pushPacket(new CEntityUpdatePacket(PChar->PPet, ENTITY_DESPAWN, UPDATE_NONE)); } } PChar->PPet = NULL; } } //remove bcnm status if (m_zone->m_BattlefieldHandler != NULL && PChar->StatusEffectContainer->HasStatusEffect(EFFECT_BATTLEFIELD)) { if (m_zone->m_BattlefieldHandler->disconnectFromBcnm(PChar)){ ShowDebug("Removed %s from the BCNM they were in as they have left the zone.\n", PChar->GetName()); } if (PChar->loc.destination == 0){ //this player is disconnecting/logged out, so move them to the entrance //move depending on zone int pos[4] = { 0, 0, 0, 0 }; battlefieldutils::getStartPosition(m_zone->GetID(), pos); if (pos != NULL){ PChar->loc.p.x = pos[0]; PChar->loc.p.y = pos[1]; PChar->loc.p.z = pos[2]; PChar->loc.p.rotation = pos[3]; charutils::SaveCharPosition(PChar); } else{ ShowWarning("%s has disconnected from the BCNM but cannot move them to the lobby as the lobby position is unknown!\n", PChar->GetName()); } } } else if (m_zone->m_BattlefieldHandler != NULL && PChar->StatusEffectContainer->HasStatusEffect(EFFECT_DYNAMIS, 0)) { if (m_zone->m_BattlefieldHandler->disconnectFromDynamis(PChar)){ ShowDebug("Removed %s from the BCNM they were in as they have left the zone.\n", PChar->GetName()); } if (PChar->loc.destination == 0){ //this player is disconnecting/logged out, so move them to the entrance //move depending on zone int pos[4] = { 0, 0, 0, 0 }; battlefieldutils::getStartPosition(m_zone->GetID(), pos); if (!(pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0)){ PChar->loc.p.x = pos[0]; PChar->loc.p.y = pos[1]; PChar->loc.p.z = pos[2]; PChar->loc.p.rotation = pos[3]; charutils::SaveCharPosition(PChar); } else{ ShowWarning("%s has disconnected from the BCNM but cannot move them to the lobby as the lobby position is unknown!\n", PChar->GetName()); } } } for (EntityList_t::const_iterator it = m_mobList.begin(); it != m_mobList.end(); ++it) { CMobEntity* PCurrentMob = (CMobEntity*)it->second; PCurrentMob->PEnmityContainer->Clear(PChar->id); if (PCurrentMob->m_OwnerID.id == PChar->id){ PCurrentMob->m_OwnerID.clean(); } } // TODO: могут возникать проблемы с переходом между одной и той же зоной (zone == prevzone) m_charList.erase(PChar->targid); ShowDebug(CL_CYAN"CZone:: %s DecreaseZoneCounter <%u> %s\n" CL_RESET, m_zone->GetName(), m_charList.size(), PChar->GetName()); }
bool CStatusEffectContainer::AddStatusEffect(CStatusEffect* PStatusEffect, bool silent) { if(PStatusEffect == nullptr){ ShowWarning("status_effect_container::AddStatusEffect Status effect given was nullptr!\n"); return false; } uint16 statusId = PStatusEffect->GetStatusID(); if(statusId >= MAX_EFFECTID){ ShowWarning("status_effect_container::AddStatusEffect statusId given is OVER limit %d\n", statusId); return false; } if(CanGainStatusEffect((EFFECT)statusId, PStatusEffect->GetPower())) { // check for minimum duration if(PStatusEffect->GetDuration() < effects::EffectsParams[statusId].MinDuration){ PStatusEffect->SetDuration(effects::EffectsParams[statusId].MinDuration); } // remove clean up other effects OverwriteStatusEffect(PStatusEffect); PStatusEffect->SetOwner(m_POwner); SetEffectParams(PStatusEffect); // remove effects with same type DelStatusEffectsByType(PStatusEffect->GetType()); PStatusEffect->SetStartTime(gettick()); m_StatusEffectList.push_back(PStatusEffect); luautils::OnEffectGain(m_POwner, PStatusEffect); m_POwner->addModifiers(&PStatusEffect->modList); if (PStatusEffect->GetStatusID() >= EFFECT_FIRE_MANEUVER && PStatusEffect->GetStatusID() <= EFFECT_DARK_MANEUVER && m_POwner->objtype == TYPE_PC) { puppetutils::CheckAttachmentsForManeuver((CCharEntity*)m_POwner, PStatusEffect->GetStatusID(), true); } if( m_POwner->health.maxhp != 0) //make sure we're not in the middle of logging in { m_POwner->UpdateHealth(); } if (m_POwner->objtype == TYPE_PC) { CCharEntity* PChar = (CCharEntity*)m_POwner; if (PStatusEffect->GetIcon() != 0) { UpdateStatusIcons(); } if( m_POwner->health.maxhp != 0) //make sure we're not in the middle of logging in { //check for latents PChar->PLatentEffectContainer->CheckLatentsFoodEffect(); PChar->PLatentEffectContainer->CheckLatentsStatusEffect(); PChar->PLatentEffectContainer->CheckLatentsRollSong(PStatusEffect->GetFlag() & (EFFECTFLAG_SONG | EFFECTFLAG_ROLL)); PChar->UpdateHealth(); PChar->pushPacket(new CCharHealthPacket(PChar)); } PChar->pushPacket(new CCharSyncPacket(PChar)); } m_POwner->updatemask |= UPDATE_HP; return true; } return false; }
void CZoneEntities::PushPacket(CBaseEntity* PEntity, GLOBAL_MESSAGE_TYPE message_type, CBasicPacket* packet) { // Do not send packets that are updates of a hidden GM.. if (packet != NULL && packet->getType() == 0x0D && PEntity != NULL && PEntity->objtype == TYPE_PC && ((CCharEntity*)PEntity)->m_isGMHidden) { // Ensure this packet is not despawning us.. if (packet->getData()[0x06] != 0x20) { delete packet; return; } } if (!m_charList.empty()) { switch (message_type) { case CHAR_INRANGE_SELF: { if (PEntity->objtype == TYPE_PC) { ((CCharEntity*)PEntity)->pushPacket(new CBasicPacket(*packet)); } } case CHAR_INRANGE: { for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) { CCharEntity* PCurrentChar = (CCharEntity*)it->second; if (PEntity != PCurrentChar) { if (distance(PEntity->loc.p, PCurrentChar->loc.p) < 50) { if (packet != NULL && packet->getType() == 0x0E && (RBUFB(packet->getData(), (0x0A) - 4) != 0x20 || RBUFB(packet->getData(), (0x0A) - 4) != 0x0F)) { uint32 id = RBUFL(packet->getData(), (0x04) - 4); uint16 targid = RBUFW(packet->getData(), (0x08) - 4); CBaseEntity* entity = GetEntity(targid); SpawnIDList_t spawnlist; if (entity) { if (entity->targid < 0x400) { if (entity->objtype == TYPE_MOB) { spawnlist = PCurrentChar->SpawnMOBList; } else if (entity->objtype == TYPE_NPC) { spawnlist = PCurrentChar->SpawnNPCList; } } else if (entity->targid >= 0x700) { spawnlist = PCurrentChar->SpawnPETList; } else { entity = NULL; } } if (!entity) { // got a char or nothing as the target of this entity update (which really shouldn't happen ever) // so we're just going to skip this packet break; } SpawnIDList_t::iterator iter = spawnlist.lower_bound(id); if (!(iter == spawnlist.end() || spawnlist.key_comp()(id, iter->first))) { PCurrentChar->pushPacket(new CBasicPacket(*packet)); } } else { PCurrentChar->pushPacket(new CBasicPacket(*packet)); } } } } } break; case CHAR_INSHOUT: { for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) { CCharEntity* PCurrentChar = (CCharEntity*)it->second; if (PEntity != PCurrentChar) { if (distance(PEntity->loc.p, PCurrentChar->loc.p) < 180) { PCurrentChar->pushPacket(new CBasicPacket(*packet)); } } } } break; case CHAR_INZONE: { for (EntityList_t::const_iterator it = m_charList.begin(); it != m_charList.end(); ++it) { CCharEntity* PCurrentChar = (CCharEntity*)it->second; if (PEntity != PCurrentChar) { PCurrentChar->pushPacket(new CBasicPacket(*packet)); } } } break; } } delete packet; }
void CParty::DelMember(CBattleEntity* PEntity) { DSP_DEBUG_BREAK_IF(PEntity == NULL); DSP_DEBUG_BREAK_IF(PEntity->PParty != this); if (m_PLeader == PEntity) { RemovePartyLeader(PEntity); } else { for (uint32 i = 0; i < members.size(); ++i) { if (PEntity == members.at(i)) { members.erase(members.begin() + i); if (m_PartyType == PARTY_PCS) { CCharEntity* PChar = (CCharEntity*)PEntity; if (m_PQuaterMaster == PChar) { SetQuaterMaster(NULL); } if (m_PSyncTarget == PChar) { SetSyncTarget(NULL, 553); CStatusEffect* sync = PChar->StatusEffectContainer->GetStatusEffect(EFFECT_LEVEL_SYNC); if (sync && sync->GetDuration() == 0) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 30, 553)); sync->SetStartTime(gettick()); sync->SetDuration(30000); } DisableSync(); } if (m_PSyncTarget != NULL && m_PSyncTarget != PChar) { if (PChar->status != STATUS_DISAPPEAR && PChar->getZone() == m_PSyncTarget->getZone()) { CStatusEffect* sync = PChar->StatusEffectContainer->GetStatusEffect(EFFECT_LEVEL_SYNC); if (sync && sync->GetDuration() == 0) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 30, 553)); sync->SetStartTime(gettick()); sync->SetDuration(30000); } } } PChar->PLatentEffectContainer->CheckLatentsPartyMembers(members.size()); PChar->pushPacket(new CPartyDefinePacket(NULL)); PChar->pushPacket(new CPartyMemberUpdatePacket(PChar, 0, PChar->getZone())); PChar->pushPacket(new CCharUpdatePacket(PChar)); PChar->PParty = NULL; if (PChar->PTreasurePool != NULL && PChar->PTreasurePool->GetPoolType() != TREASUREPOOL_ZONE) { PChar->PTreasurePool->DelMember(PChar); PChar->PTreasurePool = new CTreasurePool(TREASUREPOOL_SOLO); PChar->PTreasurePool->AddMember(PChar); PChar->PTreasurePool->UpdatePool(PChar); } } break; } } } }
void CMobEntity::DropItems() { CCharEntity* PChar = (CCharEntity*)GetEntity(m_OwnerID.targid, TYPE_PC); if (PChar != nullptr && PChar->id == m_OwnerID.id) { loc.zone->PushPacket(this, CHAR_INRANGE, new CMessageBasicPacket(PChar, this, 0, 0, MSGBASIC_DEFEATS_TARG)); if (!CalledForHelp()) { blueutils::TryLearningSpells(PChar, this); m_UsedSkillIds.clear(); if (m_giveExp) { charutils::DistributeExperiencePoints(PChar, this); } DropList_t* DropList = itemutils::GetDropList(m_DropID); //ShowDebug(CL_CYAN"DropID: %u dropping with TH Level: %u\n" CL_RESET, PMob->m_DropID, PMob->m_THLvl); if (DropList != nullptr && !getMobMod(MOBMOD_NO_DROPS) && DropList->size()) { for (uint8 i = 0; i < DropList->size(); ++i) { //THLvl is the number of 'extra chances' at an item. If the item is obtained, then break out. uint8 tries = 0; uint8 maxTries = 1 + (m_THLvl > 2 ? 2 : m_THLvl); uint8 bonus = (m_THLvl > 2 ? (m_THLvl - 2) * 10 : 0); while (tries < maxTries) { if (dsprand::GetRandomNumber(1000) < DropList->at(i).DropRate * map_config.drop_rate_multiplier + bonus) { PChar->PTreasurePool->AddItem(DropList->at(i).ItemID, this); break; } tries++; } } } // check for gil (beastmen drop gil, some NMs drop gil) if (CanDropGil() || (map_config.all_mobs_gil_bonus > 0 && getMobMod(MOBMOD_GIL_MAX) >= 0)) // Negative value of MOBMOD_GIL_MAX is used to prevent gil drops in Dynamis/Limbus. { charutils::DistributeGil(PChar, this); // TODO: REALISATION MUST BE IN TREASUREPOOL } //check for seal drops /* MobLvl >= 1 = Beastmen Seals ID=1126 >= 50 = Kindred Seals ID=1127 >= 75 = Kindred Crests ID=2955 >= 90 = High Kindred Crests ID=2956 */ uint16 Pzone = PChar->getZone(); bool validZone = ((Pzone > 0 && Pzone < 39) || (Pzone > 42 && Pzone < 134) || (Pzone > 135 && Pzone < 185) || (Pzone > 188 && Pzone < 255)); if (validZone && charutils::GetRealExp(PChar->GetMLevel(), GetMLevel()) > 0) { if (((PChar->StatusEffectContainer->HasStatusEffect(EFFECT_SIGNET) && conquest::GetInfluenceGraphics(PChar->loc.zone->GetRegionID()) < 64) || (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_SANCTION) && PChar->loc.zone->GetRegionID() >= 28 && PChar->loc.zone->GetRegionID() <= 32) || (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_SIGIL) && PChar->loc.zone->GetRegionID() >= 33 && PChar->loc.zone->GetRegionID() <= 40)) && m_Element > 0 && dsprand::GetRandomNumber(100) < 20) // Need to move to CRYSTAL_CHANCE constant { PChar->PTreasurePool->AddItem(4095 + m_Element, this); } // Todo: Avatarite and Geode drops during day/weather. Much higher chance during weather than day. // Item element matches day/weather element, not mob crystal. Lv80+ xp mobs can drop Avatarite. // Wiki's have conflicting info on mob lv required for Geodes. One says 50 the other 75. I think 50 is correct. if (dsprand::GetRandomNumber(100) < 20 && PChar->PTreasurePool->CanAddSeal() && !getMobMod(MOBMOD_NO_DROPS)) { //RULES: Only 1 kind may drop per mob if (GetMLevel() >= 75 && luautils::IsExpansionEnabled("ABYSSEA")) //all 4 types { switch (dsprand::GetRandomNumber(4)) { case 0: PChar->PTreasurePool->AddItem(1126, this); break; case 1: PChar->PTreasurePool->AddItem(1127, this); break; case 2: //PChar->PTreasurePool->AddItem(2955, this); break; case 3: //PChar->PTreasurePool->AddItem(2956, this); break; } } else if (GetMLevel() >= 70 && luautils::IsExpansionEnabled("ABYSSEA")) //b.seal & k.seal & k.crest { switch (dsprand::GetRandomNumber(3)) { case 0: PChar->PTreasurePool->AddItem(1126, this); break; case 1: PChar->PTreasurePool->AddItem(1127, this); break; case 2: //PChar->PTreasurePool->AddItem(2955, this); break; } } else if (GetMLevel() >= 50) //b.seal & k.seal only { if (dsprand::GetRandomNumber(2) == 0) { PChar->PTreasurePool->AddItem(1126, this); } else { PChar->PTreasurePool->AddItem(1127, this); } } else { //b.seal only PChar->PTreasurePool->AddItem(1126, this); } } } } PChar->setWeaponSkillKill(false); StatusEffectContainer->KillAllStatusEffect(); // NOTE: this is called for all alliance / party members! luautils::OnMobDeath(this, PChar); } else { luautils::OnMobDeath(this, nullptr); } }