/* EQEMu: Everquest Server Emulator Copyright (C) 2001-2004 EQEMu Development Team (http://eqemu.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY except by those people which sell it, which are required to give you total support for your newly bought product; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" #include using namespace std; #include #include #include #include #include #ifdef _CRTDBG_MAP_ALLOC #undef new #endif #include #ifdef _CRTDBG_MAP_ALLOC #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) #endif using namespace std; #ifdef WIN32 #include #define snprintf _snprintf #define vsnprintf _vsnprintf #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif bool spells_loaded = false; volatile bool RunLoops = true; extern volatile bool ZoneLoaded; #ifdef SHAREMEM #include "../common/EMuShareMem.h" extern LoadEMuShareMemDLL EMuShareMemDLL; #ifndef WIN32 #include #include #include #include #ifndef FREEBSD union semun { int val; struct semid_ds *buf; ushort *array; struct seminfo *__buf; void *__pad; }; #endif #endif #endif #include "../common/queue.h" #include "../common/timer.h" #include "../common/EQStream.h" #include "../common/eq_packet_structs.h" #include "../common/Mutex.h" #include "../common/version.h" #include "../common/files.h" #include "../common/EQEMuError.h" #include "../common/packet_dump_file.h" #include "masterentity.h" #include "worldserver.h" #include "net.h" #include "spdat.h" #include "zone.h" #include "command.h" #include "parser.h" #include "embparser.h" #ifndef NEW_LoadSPDat void LoadSPDat(SPDat_Spell_Struct** SpellsPointer) { //FILE *fp; //cout << "Beginning Spells memset." << endl; int u; //for (u = 0; u < SPDAT_RECORDS; u++) //{ //cout << u << ' '; memset((char*) &spells,0,sizeof(SPDat_Spell_Struct)*SPDAT_RECORDS); //} //cout << "Memset finished\n"; char temp=' '; int tempid=0; char token[64]=""; int a = 0; char sep='^'; LogFile->write(EQEMuLog::Normal, "If this is the last message you see, you forgot to move spells_en.txt from your EQ dir to this dir."); #ifdef FREEBSD #error ifstreams seem to break BSD... #endif ifstream in;in.open(SPELLS_FILE); if(!in.is_open()){ LogFile->write(EQEMuLog::Error, "File '%s' not found in same directory as zone.exe, spell loading FAILED!", SPELLS_FILE); return; } //while(!in.eof()) //{in >> temp;} //for(int x =0; x< spellsen_size; x++) // memset((char*) &spells[x],0,sizeof(SPDat_Spell_Struct)); while(tempid <= SPDAT_RECORDS-1) { //if(tempid>3490) //{ // cout << "BLEH"; // getch(); //} //in.getline(&temp, 624); in.get(temp); while(chrcmpI(&temp, &sep)) { strncat(token,&temp,1); a++;//cout << temp<< ' '; in.get(temp); } tempid=atoi(token); if(tempid>=SPDAT_RECORDS) break; //cout << "TempID: " << tempid << endl; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } strncpy(spells[tempid].name,token,a); a=0; for(u=0;u<64;u++) token[u]=(char)0; //cout << spells[tempid].name << '^'; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } strncpy(spells[tempid].player_1,token,a); //cout << spells[tempid].player_1 << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } strncpy(spells[tempid].teleport_zone,token,a); //cout << spells[tempid].teleport_zone << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } strncpy(spells[tempid].you_cast,token,a); //cout << spells[tempid].you_cast << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } strncpy(spells[tempid].other_casts,token,a); //cout << spells[tempid].other_casts << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } strncpy(spells[tempid].cast_on_you,token,a); //cout << spells[tempid].cast_on_you << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } strncpy(spells[tempid].cast_on_other,token,a); //cout << spells[tempid].cast_on_other << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } strncpy(spells[tempid].spell_fades,token,a); //cout << spells[tempid].spell_fades << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); in.get(temp); } spells[tempid].range=atof(token); //cout << spells[tempid].range << '^'; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].aoerange=atof(token); //cout << spells[tempid].aoerange << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].pushback=atof(token); //cout << spells[tempid].pushback << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].pushup=atof(token); //cout << spells[tempid].pushup << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].cast_time=atoi(token); //cout << spells[tempid].cast_time << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].recovery_time=atoi(token); //cout << spells[tempid].recovery_time << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].recast_time=atoi(token); //cout << spells[tempid].recast_time << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].buffdurationformula=atoi(token); //cout << spells[tempid].buffdurationformula << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].buffduration=atoi(token); //cout << spells[tempid].buffduration << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].ImpactDuration=atoi(token); //cout << spells[tempid].ImpactDuration<< '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].mana=atoi(token); //cout << spells[tempid].mana << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; int y; for(y=0; y< 12;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].base[y]=atoi(token); //cout << spells[tempid].base[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } for(y=0; y< 12;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].max[y]=atoi(token); //cout << spells[tempid].max[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].icon=atoi(token); //cout << spells[tempid].icon << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].memicon=atoi(token); //cout << spells[tempid].memicon << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; for(y=0; y< 4;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].components[y]=atoi(token); //cout << spells[tempid].components[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } for(y=0; y< 4;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].component_counts[y]=atoi(token);//atoi(token); //cout << spells[tempid].component_counts[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } for(y=0; y< 4;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].NoexpendReagent[y]=atoi(token); //NoExpend Reagent //cout << spells[tempid].NoexpendReagent[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } for(y=0; y< 12;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].formula[y]=atoi(token); //cout << spells[tempid].formula[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].LightType=atoi(token); //cout << spells[tempid].LightType << '^'; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].goodEffect=atoi(token); //cout << spells[tempid].goodEffect << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].Activated=atoi(token); //cout << spells[tempid].Activated << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].resisttype=atoi(token); //cout << spells[tempid].resisttype << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; for(y=0; y< 12;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].effectid[y]=atoi(token); //cout << spells[tempid].effectid[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].targettype=atoi(token); //cout << spells[tempid].targettype << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].basediff=atoi(token); //cout << spells[tempid].basediff<< '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].skill=atoi(token); //cout << spells[tempid].skill << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].zonetype=atoi(token); //cout << spells[tempid].zonetype << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].EnvironmentType=atoi(token); //cout << spells[tempid].EnvironmentType << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].TimeOfDay=atoi(token); //cout << spells[tempid].TimeOfDay << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; for(y=0; y< 15;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].classes[y]= atoi(token); //cout << spells[tempid].classes[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } //cout << "end class"; /*for(y=0; y< 3;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].unknown1[y]=atoi(token); cout << spells[tempid].unknown1[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].unknown2=atoi(token); cout << spells[tempid].unknown2 << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; */ in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].CastingAnim=atoi(token); //cout << spells[tempid].CastingAnim << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].TargetAnim=atoi(token); //cout << spells[tempid].TargetAnim << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].TravelType=atoi(token); //cout << spells[tempid].TravelType << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].SpellAffectIndex=atoi(token); //cout << spells[tempid].SpellAffectIndex << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; for(y=0; y< 23;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].Spacing2[y]=atoi(token); //cout << spells[tempid].base[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].ResistDiff=atoi(token); //cout << spells[tempid].ResistDiff << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; in.get(temp); for(y=0; y< 2;y++) { in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].Spacing3[y]=atoi(token); //cout << spells[tempid].base[y] << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; } in.get(temp); while(chrcmpI(&temp,&sep)) { strncat(token,&temp,1); a++; in.get(temp); } spells[tempid].RecourseLink = atoi(token); //cout << spells[tempid].RecourseLink << '^'; a=0; for(u=0;u<64;u++) token[u]=(char)0; while(temp!='\n') in.get(temp); //cout << endl; if(tempid==SPDAT_RECORDS-1) break; } //for(u=0;u< SPDAT_RECORDS;u++) // cout << u << ' ' << spells[u].name << '^'; spells_loaded = true; cout << "Spells loaded.\n"; in.close(); } #endif /*void EntityList::SendAATimer(uint32 charid,UseAA_Struct* uaa){ Client* client2=this->GetClientByCharID(charid); if(!client2){ LogFile->write(EQEMuLog::Error, "Error in SendAATimer: Couldnt find character!"); return; } client2->SendAATimer(uaa); } void ZoneDatabase::UpdateAndDeleteAATimers(uint32 charid){ char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; char *query2 = 0; if (!RunQuery(query, MakeAnyLenString(&query, "delete from aa_timers where charid=%i and UNIX_TIMESTAMP(now())>=end",charid), errbuf)) { LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query, errbuf); } if (!RunQuery(query2, MakeAnyLenString(&query2, "update aa_timers set end=end-(UNIX_TIMESTAMP(now())-begin),begin=UNIX_TIMESTAMP(now()) where charid=%i",charid), errbuf)) { LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query2, errbuf); } safe_delete_array(query); safe_delete_array(query2); } void ZoneDatabase::UpdateTimersClientConnected(uint32 charid){ char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; if (!RunQuery(query, MakeAnyLenString(&query, "update aa_timers set end=(UNIX_TIMESTAMP(now())+(end-begin)),begin=UNIX_TIMESTAMP(now()) where charid=%i",charid), errbuf)) { LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query, errbuf); } safe_delete_array(query); } void ZoneDatabase::GetAATimers(uint32 charid){ char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; if (RunQuery(query, MakeAnyLenString(&query, "SELECT ability,begin,end from aa_timers WHERE charid=%i", charid), errbuf, &result)) { while( ( row = mysql_fetch_row(result) ) ){ UseAA_Struct* uaa=new UseAA_Struct(); uaa->ability=atoi(row[0]); uaa->begin=atoi(row[1]); uaa->end=atoi(row[2]); entity_list.SendAATimer(charid,uaa); safe_delete(uaa); } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Database::GetAATimers query '%s' %s", query, errbuf); } safe_delete_array(query); } uint32 ZoneDatabase::GetTimerRemaining(uint32 charid,uint32 ability){ char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; uint32 remain=0; if (RunQuery(query, MakeAnyLenString(&query, "SELECT end-begin from aa_timers WHERE charid=%i and ability=%i", charid,ability), errbuf, &result)) { if((row=mysql_fetch_row(result))){ remain=atoi(row[0]); } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Database::GetTimerRemaining query '%s' %s", query, errbuf); } safe_delete_array(query); return remain; } void ZoneDatabase::UpdateAATimers(uint32 charid,uint32 endtime,uint32 begintime,uint32 ability){ char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; if(begintime==0){ if (!RunQuery(query, MakeAnyLenString(&query, "replace into aa_timers (charid,end,begin,ability) values(%i,UNIX_TIMESTAMP(now())+%i,UNIX_TIMESTAMP(now()),%i)",charid,endtime,ability), errbuf)) { LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query, errbuf); } } else{ if (!RunQuery(query, MakeAnyLenString(&query, "replace into aa_timers (charid,end,begin,ability) values(%i,%i,%i,%i)",charid,endtime,begintime,ability), errbuf)) { LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query, errbuf); } } safe_delete_array(query); }*/ /* uint16 Client::GetCombinedAC_TEST() { int ac1; ac1 = GetRawItemAC(); if (m_pp.class_ != WIZARD && m_pp.class_ != MAGICIAN && m_pp.class_ != NECROMANCER && m_pp.class_ != ENCHANTER) { ac1 = ac1*4/3; } ac1 += GetSkill(DEFENSE)/3; if (GetAGI() > 70) { ac1 += GetAGI()/20; } int ac2; ac2 = GetRawItemAC(); if (m_pp.class_ != WIZARD && m_pp.class_ != MAGICIAN && m_pp.class_ != NECROMANCER && m_pp.class_ != ENCHANTER) { ac2 = ac2*4/3; } ac2 += GetSkill(DEFENSE)*400/255; int combined_ac = (ac1+ac2)*1000/847; return combined_ac; float combined_ac = ((float)ac1+(float)ac2)*1000.0f/847.0f; return (uint16) combined_ac;//*10.0f)-10; } */ /*bool Client::GetIncreaseSpellDurationItem(uint16 &spell_id, char *itemname) { for (int i=0; i<22; i++) { const ItemInst* inst = m_inv[i]; if (!inst || !inst->IsType(ItemTypeCommon)) continue; const Item_Struct* item = inst->GetItem(); if (item->FocusId && (item->FocusId != 0xFFFF)) { if (IsIncreaseDurationSpell(item->FocusId)) { spell_id = item->FocusId; if (itemname) strcpy(itemname, item->Name); return true; } } } return false; } bool Client::GetReduceManaCostItem(uint16 &spell_id, char *itemname) { for (int i=0; i<22; i++) { const ItemInst* inst = m_inv[i]; if (!inst || !inst->IsType(ItemTypeCommon)) continue; const Item_Struct* item = inst->GetItem(); if (item->FocusId && (item->FocusId != 0xFFFF)) { if (IsReduceManaSpell(item->FocusId)) { spell_id = item->FocusId; if (itemname) strcpy(itemname, item->Name); return true; } } } return false; } bool Client::GetReduceCastTimeItem(uint16 &spell_id, char *itemname) { for (int i=0; i<22; i++) { const ItemInst* inst = m_inv[i]; if (!inst || !inst->IsType(ItemTypeCommon)) continue; const Item_Struct* item = inst->GetItem(); if (item->FocusId && (item->FocusId != 0xFFFF)) { if (IsReduceCastTimeSpell(item->FocusId)) { spell_id = item->FocusId; if (itemname) strcpy(itemname, item->Name); return true; } } } return false; } bool Client::GetExtendedRangeItem(uint16 &spell_id, char *itemname) { for (int i=0; i<22; i++) { const ItemInst* inst = m_inv[i]; if (!inst || !inst->IsType(ItemTypeCommon)) continue; const Item_Struct* item = inst->GetItem(); if (item->FocusId && (item->FocusId != 0xFFFF)) { if (IsExtRangeSpell(item->FocusId)) { spell_id = item->FocusId; if (itemname) strcpy(itemname, item->Name); return true; } } } return false; } bool Client::GetImprovedHealingItem(uint16 &spell_id, char *itemname) { for (int i=0; i<22; i++) { const ItemInst* inst = m_inv[i]; if (!inst || !inst->IsType(ItemTypeCommon)) continue; const Item_Struct* item = inst->GetItem(); if (item->FocusId && (item->FocusId != 0xFFFF)) { if (IsImprovedHealingSpell(item->FocusId)) { spell_id = item->FocusId; if (itemname) strcpy(itemname, item->Name); return true; } } } return false; } bool Client::GetImprovedDamageItem(uint16 &spell_id, char *itemname) { for (int i=0; i<22; i++) { const ItemInst* inst = m_inv[i]; if (!inst || !inst->IsType(ItemTypeCommon)) continue; const Item_Struct* item = inst->GetItem(); if (item->FocusId && (item->FocusId != 0xFFFF)) { if (IsImprovedDamageSpell(item->FocusId)) { spell_id = item->FocusId; if (itemname) strcpy(itemname, item->Name); return true; } } } return false; } int32 Client::GenericFocus(uint16 spell_id, uint16 modspellid) { int modifier = 100, i; const SPDat_Spell_Struct &spell = spells[spell_id]; const SPDat_Spell_Struct &modspell = spells[modspellid]; for (i = 0; i < EFFECT_COUNT; i++) { if(IsBlankSpellEffect(modspellid, i)) continue; switch( spells[modspellid].effectid[i] ) { case SE_LimitMaxLevel: if (spell.classes[(GetClass()%16) - 1] > modspell.base[i]) return 100; break; case SE_LimitMinLevel: if (spell.classes[(GetClass()%16) - 1] < modspell.base[i]) return 100; break; case SE_IncreaseRange: modifier += modspell.base[i]; break; case SE_IncreaseSpellHaste: modifier -= modspell.base[i]; break; case SE_IncreaseSpellDuration: modifier += modspell.base[i]; break; case SE_LimitSpell: // negative sign means exclude // positive sign means include if (modspell.base[i] < 0) { if (modspell.base[i] * (-1) == spell_id) return 100; } else { if (spells[modspellid].base[i] != spell_id) return 100; } break; case SE_LimitEffect: switch( spells[modspellid].base[i] ) { case -147: if (IsPercentalHealSpell(spell_id)) return 100; break; case -101: if (IsCHDurationSpell(spell_id)) return 100; break; case -40: if (IsInvulnerabilitySpell(spell_id)) return 100; break; case -32: if (IsSummonItemSpell(spell_id)) return 100; break; case 0: if (!IsEffectHitpointsSpell(spell_id)) return 100; break; case 33: if (!IsSummonPetSpell(spell_id)) return 100; break; case 36: if (!IsPoisonCounterSpell(spell_id)) return 100; break; case 71: if (!IsSummonSkeletonSpell(spell_id)) return 100; break; default: LogFile->write(EQEMuLog::Normal, "GenericFocus: unknown limit effect %d", spells[modspellid].base[i]); } break; case SE_LimitCastTime: if (modspell.base[i] > (int16)spell.cast_time) return 100; break; case SE_LimitSpellType: switch( spells[modspellid].base[i] ) { case 0: if (!IsDetrimentalSpell(spell_id)) return 100; break; case 1: if (!IsBeneficialSpell(spell_id)) return 100; break; default: LogFile->write(EQEMuLog::Normal, "GenericFocus: unknown limit spelltype %d", spells[modspellid].base[i]); } break; case SE_LimitMinDur: if (modspell.base[i] > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) return 100; break; case SE_ImprovedDamage: case SE_ImprovedHeal: modifier += modspell.base[i]; break; case SE_ReduceManaCost: modifier -= modspell.base[i]; break; default: LogFile->write(EQEMuLog::Normal, "GenericFocus: unknown effectid %d", modspell.effectid[i]); } } return modifier; } */ /*void Client::Discipline(ClientDiscipline_Struct* disc_in, Mob* tar) { Message(0, "Disc packet id=%d, %x,%x,%x", disc_in->disc_id, disc_in->unknown3[0], disc_in->unknown3[1], disc_in->unknown3[2]); if (!p_timers.Expired(&database, pTimerDisciplineReuse)) { char val1[20]={0}; char val2[20]={0}; uint32 remain = p_timers.GetRemainingTime(pTimerDisciplineReuse); Message_StringID(0,DISCIPLINE_CANUSEIN,ConvertArray((remain)/60,val1),ConvertArray(remain%60,val2)); //Message(0,"You can use a new discipline in %i minutes %i seconds.", (disc_timer.GetRemainingTime()/1000)/60, disc_timer.GetRemainingTime()/1000%60); return; } //reuse times are a little messes up, they should scale down somehow //as you gain in levels, but im not sure how, so its just a lvl 60 bonus right now //should change this to check classes better. //both in seconds, converted at the end. uint32 duration = 0; uint32 reuse = 0; switch(disc_in->disc_id){ // Shared? case discResistant: { // Resistant // 1 minute duration // 1 hour reuse // +3 to +10 to resists if (GetLevel()<=29) return; duration = 60; reuse = 60*60; entity_list.MessageClose(this, false, 100, 0, "%s has become more resistant!", GetName()); break; } case discFearless: { // Fearless // 11 second duration // 1 hour reuse // 100% fear immunity if (GetLevel()<=39) return; duration = 11; reuse = 60*60; entity_list.MessageClose_StringID(this, false, 100, 0, DISCIPLINE_FEARLESS, GetName()); //entity_list.MessageClose(this, false, 100, 0, "%s becomes fearless!", GetName()); break; } case discWhirlwind: { // Counterattack/Whirlwind/Furious // warrior level 56 // rogue/monk level 53 // 9 second duration // 1 hour reuse if ( (GetClass() == WARRIOR && GetLevel() <= 56) ||(GetLevel() <= 53) ) return; duration = 9; reuse = 60*60; entity_list.MessageClose(this, false, 100, 0, "%s\'s face becomes twisted with fury!", GetName()); break; } case discFellstrike: { // Duelist/Innerflame/Fellstrike // monk level 56 // rogue level 59 // warrior level 58 // 12 second duration // 30 minute reuse // min 4*base hand/weapon damage if ( (GetClass() == MONK && GetLevel() <= 55) ||(GetClass() == WARRIOR && GetLevel() <= 58) ||(GetClass() == ROGUE && GetLevel() <= 59) ) return; duration = 12; reuse = 60*30; entity_list.MessageClose(this, false, 100, 0, "%s\'s muscles bulge with force of will!", GetName()); break; } case discBlindingSpeed: { // Blindingspeed/Hundredfist // rogue level 58 // monk level 57 // 15 second duration // 30 minute reuse if ( (GetClass() == MONK && GetLevel() <= 58) ||(GetClass() == ROGUE && GetLevel() <= 57) ) return; //disc_timer.Start(1000*60*30); //disc_elapse.Start(1000*15); duration = 15; reuse = 60*30; Message(0, "This discipline not implemented.."); break; } case discDeadeye: { // Deadeye/Charge // warrior level 53 // rogue level 54 // 14 second duration // 30 minute reuse if ( (GetClass() == WARRIOR && GetLevel() <= 53) ||(GetClass() == ROGUE && GetLevel() <= 54) ) return; duration = 14; reuse = 60*30; entity_list.MessageClose(this, false, 100, 0, "%s feels unstopable!", GetName()); break; } // Warrior case discEvasive: { // Evasive // level 52 // 3 minute duration // 15 minute reuse // +35% avoidance // -15% out duration = 3*60; reuse = 15*60; break; } case discMightystrike: { // Mightystrike // level 54 // 10 second duration // 1 hour reuse // Auto crit duration = 10; reuse = 60*60; break; } case discDefensive: { // Defensive // level 55 // 3 minute duration // 15 minute reuse, 10 after 60 // +35% mitigation // -15% out duration = 3*60; if(level > 59) reuse = 10*60; else reuse = 15*60; break; } case discPrecise: { // Precise // level 57 // 3 minute duration // 30 minute reuse // -15% avoidance // +35% out duration = 3*60; reuse = 30*60; break; } case discAggressive: { // Aggressive // level 60 // 3 minute duration // 27 minute reuse // -15% mitigation // +35% out duration = 3*60; reuse = 27*60; break; } // Monk case discStonestance: { // Stonestance // level 51 duration = 12; if(level > 59) reuse = 3*60; else reuse = 12*60; break; } case discThunderkick: { // Thunderkick // level 52 if(level > 59) reuse = 1*60; else reuse = 7*60; duration = 5*60; //hack for now, checked in combat and expired once used. break; } case discVoidance: { // Voidance // level 54 if(level > 59) reuse = 54*60; else reuse = 60*60; duration = 8; break; } case discSilentfist: { // Silentfist // level 59 // 6-7 minute reuse // Dragon punch damage bonus // Chance to stun if(level > 59) reuse = 6*60; else reuse = 7*60; duration = 5*60; //hack for now, checked in combat and expired once used. break; } case discAshenhand: { // Ashenhand // level 60 // 72 minute reuse // Eagle Strike damage bonus // Chance to slay reuse = 72*60; duration = 5*60; //hack for now, checked in combat and expired once used. break; } // Rogue case discNimble: { // Nimble // level 55 // 12 second duration // 30 minute reuse // Auto dodge if(level > 59) reuse = 25*60; else reuse = 30*60; duration = 12; break; } case discKinesthetics: { // Kinesthetics // level 57 // 18 second duration // 30,27 minute reuse // Auto dualwield // Auto double attack if(level > 59) reuse = 27*60; else reuse = 30*60; duration = 18; break; } // Paladin case discHolyforge: { // Holyforge // level 55 // 2 minute duration // 72 minute reuse // Crit/Crip undead // +15% to crit chance if(level > 59) reuse = 65*60; else reuse = 72*60; duration = 5*60; break; } case discSanctification: { // Sanctification // level 60 // 10 second duration // 72 minute reuse // Spell immunity reuse = 72*60; duration = 15; break; } // Ranger case discTrueshot: { // Trueshot // level 55 // 2 minute duration // 72 minute reuse // Max to two times max bow damage // +15% to hit if(level > 59) reuse = 67*60; else reuse = 72*60; duration = 2*60; break; } case discWeaponshield: { // Weaponshield // level 60 // 15 second duration // 72 minute reuse // auto parry reuse = 72*60; duration = 20; break; } // Bard case discDeftdance: { // Deftdance // level 55 // 10 second duration // 72 minute reuse // auto dodge // auto dualwield if(level > 59) reuse = 67*60; else reuse = 72*60; duration = 10; break; } case discPuretone: { // Puretone // level 60 // 2 minute duration // 72 minute reuse // Auto instrument reuse = 72*60; duration = 4*60; break; } // Shadow knight case discUnholyAura: { // Unholy // level 55 // 72 minute reuse // +25% to harmtouch // -300 to resist if(level > 59) reuse = 67*60; else reuse = 72*60; duration = 5*60; //hack for now, checked in combat and expired once used. break; } case discLeechCurse: { // Leech curse // level 60 // 15 second duration // 72 minute reuse // Heal self for each point of melee damage done reuse = 72*60; duration = 15; break; } // Default case 0:{ // Timer request break; } default: LogFile->write(EQEMuLog::Error, "Unknown Discipline requested by client: %s class: %i Disciline:%i", GetName(), class_,disc_in->disc_id); return; } if(reuse != 0) { p_timers.Start(pTimerDisciplineReuse, reuse); //nonpersistent timer for the 'discipline ready' message disc_timer.Start(1000*reuse); } if(duration != 0) disc_elapse.Start(1000*duration); disc_inuse = disc_in->disc_id; }*/ #if 0 // solar: this is old code /*void EntityList::AESpell(Mob* caster, Mob* center, float dist, uint16 spell_id, bool group) { LinkedListIterator iterator(mob_list); iterator.Reset(); while(iterator.MoreElements()) { Mob* mob = iterator.GetData(); if (group){ // Client casting group spell with out target group buffs enabled // Skip non group members if ( caster->IsClient() && !caster->CastToClient()->TGB() && GetGroupByMob(mob) != 0 && !GetGroupByMob(mob)->IsGroupMember(caster) ) { LogFile->write(EQEMuLog::Debug, "Group spell skipping %s", mob->GetName()); iterator.Advance(); continue; } // Client casting group spell with target group buffs enabled else if ( caster->IsClient() && caster->CastToClient()->TGB() && GetGroupByMob(mob) != 0 && GetGroupByMob(mob)->IsGroupMember(caster) ){ LogFile->write(EQEMuLog::Debug, "Group spell TGB on %s's Group", mob->GetName()); GetGroupByMob(mob)->CastGroupSpell(caster, spell_id); iterator.Advance(); continue; } else if ( caster->IsClient() && caster->CastToClient()->TGB() && GetGroupByMob(mob) == 0 && mob == center ){ LogFile->write(EQEMuLog::Debug, "Group spell TGB on %s", mob->GetName()); caster->SpellOnTarget(spell_id, mob); return; } } if ( mob->DistNoZ(*center) <= dist && !(mob->IsClient() && mob->CastToClient()->GMHideMe()) && !mob->IsCorpse() ) { //cout << "AE Spell Hit: t=" << iterator.GetData()->GetName() << ", d=" << iterator.GetData()->CastToMob()->DistNoRoot(center) << ", x=" << iterator.GetData()->CastToMob()->GetX() << ", y=" << iterator.GetData()->CastToMob()->GetY() << endl; if (caster == mob) { // Caster gets the first hit, already handled in spells.cpp } #ifdef IPC else if(caster->IsNPC() && !caster->CastToNPC()->IsInteractive()) { #else else if(caster->IsNPC()) { #endif // Npc if (caster->IsAttackAllowed(mob) && spells[spell_id].targettype != ST_AEBard) { // printf("NPC Spell casted on %s\n", mob->GetName()); caster->SpellOnTarget(spell_id, mob); } else if (mob->IsAIControlled() && spells[spell_id].targettype == ST_AEBard) { // printf("NPC mgb/aebard spell casted on %s\n", mob->GetName()); caster->SpellOnTarget(spell_id, mob); } else { // printf("NPC AE, fall thru. spell_id:%i, Target type:%x\n", spell_id, spells[spell_id].targettype); } } #ifdef IPC else if(caster->IsNPC() && caster->CastToNPC()->IsInteractive()) { // Interactive npc if (caster->IsAttackAllowed(mob) && spells[spell_id].targettype != ST_AEBard && spells[spell_id].targettype != ST_GroupTeleport) { // printf("IPC Spell casted on %s\n", mob->GetName()); caster->SpellOnTarget(spell_id, mob); } else if (!mob->IsAIControlled() && (spells[spell_id].targettype == ST_AEBard||group) && mob->CastToClient()->GetPVP() == caster->CastToClient()->GetPVP()) { if (group && GetGroupByMob(mob) != GetGroupByMob(caster)) { iterator.Advance(); continue; } // printf("IPC mgb/aebard spell casted on %s\n", mob->GetName()); caster->SpellOnTarget(spell_id, mob); } else { // printf("NPC AE, fall thru. spell_id:%i, Target type:%x\n", spell_id, spells[spell_id].targettype); } } #endif else if (caster->IsClient() && !(caster->CastToClient()->IsBecomeNPC())) { // Client if (caster->IsAttackAllowed(mob) && spells[spell_id].targettype != ST_AEBard){ // printf("Client Spell casted on %s\n", mob->GetName()); caster->SpellOnTarget(spell_id, mob); } else if(spells[spell_id].targettype == ST_GroupTeleport && mob->IsClient() && mob->isgrouped && caster->isgrouped && entity_list.GetGroupByMob(caster)) { Group* caster_group = entity_list.GetGroupByMob(caster); if(caster_group != 0 && caster_group->IsGroupMember(mob)) caster->SpellOnTarget(spell_id,mob); } else if (mob->IsClient() && (spells[spell_id].targettype == ST_AEBard||group) && mob->CastToClient()->GetPVP() == caster->CastToClient()->GetPVP()) { if (group && GetGroupByMob(mob) != GetGroupByMob(caster)) { iterator.Advance(); continue; } else if (mob->IsClient() && spells[spell_id].targettype == ST_AEBard && mob->CastToClient()->GetPVP() == caster->CastToClient()->GetPVP()) caster->SpellOnTarget(spell_id, mob); #ifdef IPC else if (mob->IsNPC() && mob->CastToNPC()->IsInteractive()) { if (group && GetGroupByMob(mob) != GetGroupByMob(caster)) continue; caster->SpellOnTarget(spell_id, mob); } #endif } } else if (caster->IsClient()) { // Client BecomeNPC caster->SpellOnTarget(spell_id, mob); } } iterator.Advance(); } }*/ #endif // solar: old code /*#if 0 // Queries the loottable: adds item & coin to the npc void ZoneDatabase::AddLootTableToNPC(uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; *copper = 0; *silver = 0; *gold = 0; *plat = 0; if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, mincash, maxcash, avgcoin FROM loottable WHERE id=%i", loottable_id), errbuf, &result)) { safe_delete_array(query); if (mysql_num_rows(result) == 1) { row = mysql_fetch_row(result); uint32 mincash = atoi(row[1]); uint32 maxcash = atoi(row[2]); if (mincash > maxcash) { cerr << "Error in loottable #" << row[0] << ": mincash > maxcash" << endl; } else if (maxcash != 0) { uint32 cash = 0; if (mincash == maxcash) cash = mincash; else cash = (rand() % (maxcash - mincash)) + mincash; if (cash != 0) { uint32 coinavg = atoi(row[3]); if (coinavg != 0) { uint32 mincoin = (uint32) (coinavg * 0.75 + 1); uint32 maxcoin = (uint32) (coinavg * 1.25 + 1); *copper = (rand() % (maxcoin - mincoin)) + mincoin - 1; *silver = (rand() % (maxcoin - mincoin)) + mincoin - 1; *gold = (rand() % (maxcoin - mincoin)) + mincoin - 1; cash -= *copper; cash -= *silver * 10; cash -= *gold * 10; } *plat = cash / 1000; cash -= *plat * 1000; uint32 gold2 = cash / 100; cash -= gold2 * 100; uint32 silver2 = cash / 10; cash -= silver2 * 10; *gold += gold2; *silver += silver2; *copper += cash; } } } else { mysql_free_result(result); return; } mysql_free_result(result); } else { cerr << "Error in AddLootTableToNPC get coin query '" << query << "' " << errbuf << endl; safe_delete_array(query); return; } if (RunQuery(query, MakeAnyLenString(&query, "SELECT loottable_id, lootdrop_id, multiplier, probability FROM loottable_entries WHERE loottable_id=%i", loottable_id), errbuf, &result)) { safe_delete_array(query); while ((row = mysql_fetch_row(result))) { int multiplier = atoi(row[2]); for (int i = 1; i <= multiplier; i++) { if ( ((rand()%1)*100) < atoi(row[3])) { AddLootDropToNPC(atoi(row[1]), itemlist); } } } mysql_free_result(result); } else { cerr << "Error in AddLootTableToNPC get items query '" << query << "' " << errbuf << endl; safe_delete_array(query); return; } return; } // Called by AddLootTableToNPC // maxdrops = size of the array npcd void ZoneDatabase::AddLootDropToNPC(uint32 lootdrop_id, ItemList* itemlist) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; // This is Wiz's updated Pool Looting functionality. Eventually, the database format should be moved over to use this // or implemented to support both methods. (A unique identifier in lootable_entries indicates to roll for a pool item // in another table. #ifdef POOLLOOTING uint32 chancepool = 0; uint32 items[50]; uint32 itemchance[50]; uint16 itemcharges[50]; uint8 i = 0; for (int m=0;m < 50;m++) { items[m]=0; itemchance[m]=0; itemcharges[m]=0; } if (RunQuery(query, MakeAnyLenString(&query, "SELECT lootdrop_id, item_id, item_charges, equip_item, chance FROM lootdrop_entries WHERE lootdrop_id=%i order by chance desc", lootdrop_id), errbuf, &result)) { safe_delete_array(query); while (row = mysql_fetch_row(result)) { items[i] = atoi(row[1]); itemchance[i] = atoi(row[4]) + chancepool; itemcharges[i] = atoi(row[2]); chancepool += atoi(row[4]); i++; } uint32 res; i = 0; if (chancepool!=0) //avoid divide by zero if some mobs have 0 for chancepool { res = rand()%chancepool; } else { res = 0; } while (items[i] != 0) { if (res <= itemchance[i]) break; else i++; } const Item_Struct* dbitem = database.GetItem(items[i]); if (dbitem == 0) { LogFile->write(EQEMuLog::Error, "AddLootDropToNPC: dbitem=0, item#=%i, lootdrop_id=%i", items[i], lootdrop_id); } else { //printf("Adding item2: %i",item->item_id); //cout << "Adding item to Mob" << endl; ServerLootItem_Struct* item = new ServerLootItem_Struct; item->item_id = dbitem->ItemNumber; item->charges = itemcharges[i]; item->equipSlot = 0; (*itemlist).Append(item); } mysql_free_result(result); } #else if (RunQuery(query, MakeAnyLenString(&query, "SELECT lootdrop_id, item_id, item_charges, equip_item, chance FROM lootdrop_entries WHERE lootdrop_id=%i order by chance desc", lootdrop_id), errbuf, &result)) { safe_delete_array(query); while ((row = mysql_fetch_row(result))) { uint8 LootDropMod=1; // place holder till I put it in a database variable to make it configurable. if( (rand()%100) < ((atoi(row[4]) * LootDropMod)) ) { uint32 itemid = atoi(row[1]); const Item_Struct* dbitem = database.GetItem(itemid); if (dbitem == 0) { LogFile->write(EQEMuLog::Error, "AddLootDropToNPC: dbitem=0, item#=%i, lootdrop_id=%i", itemid, lootdrop_id); } else { printf("Adding item: %i",item->ItemNumber); ServerLootItem_Struct* item = new ServerLootItem_Struct; item->item_id = dbitem->item_id; item->charges = atoi(row[2]); item->equipSlot = 0; (*itemlist).Append(item); } //mysql_free_result(result); //return; } } mysql_free_result(result); } #endif else { LogFile->write(EQEMuLog::Error, "Error in AddLootDropToNPC query '%s' %s", query, errbuf); safe_delete_array(query); return; } return; } #endif*/