/* * This file is part of UAS2. * * UAS2 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; either version 2 of the License, or * (at your option) any later version. * * UAS2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; 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 UASv1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * @file Corpse.cpp * Implements functionality for corpses. * * This class is referenced whenever a corpse is created, used, or assessed. * * Author: Cubem0j0 */ #include "Client.h" #include "MasterServer.h" #include "Object.h" #include "WorldManager.h" #include "TreasureGen.h" /*************** * constructors **************/ /** * Handles the creation of corpses. * * Called whenever a corpses object should be initialized. */ cCorpse::cCorpse( WORD type, DWORD dwGUID, cLocation& Loc, char *szName, char *szDesc) { //SetLocation( pcLoc ); m_dwGUID = dwGUID;//cWorldManager::NewGUID_Object( ); m_strName.assign( szName ); m_strDescription.assign( szDesc ); m_dwModel = type; m_dwType = 0; m_fStatic = false; m_dwDoorState = 0x0CL; CopyMemory( &m_Location, &Loc, sizeof( cLocation ) ); //Set the objects state this->SetState(0); } /********** * methods *********/ /** * Handles the message sent for the creation of a corpse in the world. * * This function is called whenever a corpse should be created in the world for a client. * @return cMessage - Returns a server message to the client. */ cMessage cCorpse::CreatePacket( ) { cMessage cmCreate; unsigned char bUnknown[] = { 0x00, 0x00, 0x3D, 0x00, 0x02, 0xF0, 0xB5, 0x02, 0x11, 0x00, 0x00, 0x00, }; cModels *pcMonster = cModels::FindModel(this->m_MonsterModelID); #ifdef _DEBUG cMasterServer::ServerMessage( ColorYellow,NULL,"m_dwModel = %d",this->m_MonsterModelID); #endif if(pcMonster) { cmCreate << 0xF745L << m_dwGUID << BYTE(0x11); //0x11 is a constant cmCreate << pcMonster->m_bPaletteChange << pcMonster->m_bTextureChange << pcMonster->m_bModelChange; // The Model Vectors if ( pcMonster->m_bPaletteChange != 0) { for (int i = 0; i < pcMonster->m_bPaletteChange; i++) { cmCreate.pasteData((UCHAR*)&pcMonster->m_vectorPal[i],sizeof(pcMonster->m_vectorPal[i])); } } if (pcMonster->m_bPaletteChange != 0) { cmCreate << WORD( pcMonster->m_wUnknown1 ); } if ( pcMonster->m_bTextureChange != 0) { for (int i = 0; i < pcMonster->m_bTextureChange; i++) { cmCreate.pasteData((UCHAR*)&pcMonster->m_vectorTex[i],sizeof(pcMonster->m_vectorTex[i])); } } if ( pcMonster->m_bModelChange != 0) { for (int i = 0; i < pcMonster->m_bModelChange; i++) { cmCreate.pasteData((UCHAR*)&pcMonster->m_vectorMod[i],sizeof(pcMonster->m_vectorMod[i])); } } } cmCreate.pasteAlign(4); DWORD dwFlags = 0x00019883L; cmCreate << dwFlags; cmCreate << WORD( 0x0414 ); cmCreate << WORD( 0x000C ); // MASK 0x00010000 unknown Bytes - Starting Animation cmCreate << 0x0CL; cmCreate.pasteData(bUnknown,sizeof(bUnknown)); cmCreate << 0x0L; // MASK 0x00008000 -- Location if ( !m_fIsOwned ) cmCreate.CannedData( (BYTE *)&m_Location, sizeof( cLocation ) ); // MASK 0x00000002 -- Animation Set DWORD dwAnimC = 0x09000000L + m_wAnimConfig; cmCreate << dwAnimC; // MASK 0x00000800 -- Sound Set DWORD dwSoundSet = 0x20000000L + m_wSoundSet; cmCreate << dwSoundSet; // MASK 0x00001000 -- Particle Effects (unknown_blue) cmCreate << 0x3400006EL;//0x00000000L; // MASK 0x00000001 -- ModelNumber DWORD dwModel = 0x02000000L + m_dwModel; cmCreate << dwModel; cmCreate << float(m_flScale); // SeaGreens WORD wNuminteracts = 0x0; WORD wNumbubbles = 0x0; WORD wNumJumps = 0x0; WORD wNumOverrides = 0x0; WORD wUnkFlag8 = 0x0; WORD wUnkFlag10 = 0x0; cmCreate << m_wPositionSequence << m_wNumAnims //wNuminteracts << wNumbubbles << wNumJumps << m_wNumPortals << m_wNumAnims << wNumOverrides << wUnkFlag8 << m_wNumLogins << wUnkFlag10; DWORD dwFlags2 = 0x00200036; cmCreate << dwFlags2 << m_strName.c_str( ) << WORD(m_wModel) << WORD(m_wIcon);//WORD( 0x019C ) << WORD( 0x1317 );;//WORD( 0x0116 ) << WORD( 0x1317 ); /* Category of object: 0x00000001 Melee Weapon 0x00000002 Armor 0x00000004 Clothing 0x00000008 Jewelry 0x00000010 Creature (Player/NPC/Monster) 0x00000020 Food 0x00000040 Pyreals 0x00000080 Miscellaneous 0x00000100 Missile Weapons/Ammunition 0x00000200 Containers 0x00000400 Wrapped Fletching Supplies, House Decorations 0x00000800 Gems, Pack dolls, Decorative Statues 0x00001000 Spell Components 0x00002000 Books, Parchment, Scrolls, Signs, Statues 0x00004000 Keys, Lockpicks 0x00008000 Casting Item (wand, orb, staff) 0x00010000 Portal 0x00020000 Lockable 0x00040000 Trade Notes 0x00080000 Mana Stones, Mana Charges 0x00100000 Services 0x00200000 unknown (no longer plants) 0x00400000 Cooking Ingredients and Supplies, Plants, Dye Pots 0x00800000 Loose Fletching Supplies 0x01000000 unknown 0x02000000 unknown 0x04000000 Alchemy Ingredients and Supplies, Oils, Dye Vials 0x08000000 unknown 0x10000000 Lifestone 0x20000000 Ust 0x40000000 Salvage 0x80000000 unknown */ DWORD dwObjectFlags1 = 0x0200L; cmCreate << dwObjectFlags1; /* Behavior of object: 0x00000001 can be opened (false if locked) 0x00000002 can be inscribed 0x00000004 cannot be picked up 0x00000008 is a player 0x00000010 is not an npc 0x00000020 unknown 0x00000040 unknown 0x00000080 cannot be selected 0x00000100 can be read 0x00000200 is a merchant 0x00000400 is a pk altar 0x00000800 is an npk altar 0x00001000 is a door 0x00002000 is a corpse 0x00004000 can be attuned to (lifestone) 0x00008000 adds to health, stamina or mana 0x00010000 is a healing kit 0x00020000 is a lockpick 0x00040000 is a portal 0x00800000 is a foci 0x04000000 has an extra flags DWORD */ DWORD dwObjectFlags2 = 0x2015L; cmCreate << dwObjectFlags2; /* Masked against dwFlags2 in reverse order: 0x00000002 - Item Slots 0x00000004 - Pack Slots 0x00000008 - Value 0x00000010 - Unknown1 0x00000020 - Approach Distance 0x00200000 - Burden */ cmCreate << BYTE (0x78); //78 is item slots. (120) cmCreate << BYTE (0x00); //00 is pack slots //cmCreate << DWORD (0x000009C4); //Value cmCreate << DWORD (0x00000030); //Unknown cmCreate << float(3.0); //Approach Distance. cmCreate << WORD (0x0000); //Burden return cmCreate; } /** * Handles the actions of corpse objects. * * This function is called whenever a corpse is used. */ void cCorpse::Action(cClient* who) { //Logic for open/close switch (GetState()) { case 0: { /* char szCommand[100]; RETCODE retcode; int ItemType; DWORD dwItemModelID; int test; test = 70; sprintf( szCommand, "SELECT * FROM monsters_loot where dwModelID = %d;", this->GetType()); #ifdef _DEBUG cMasterServer::ServerMessage( ColorYellow,NULL,"This GUID: %d, This Monster Model: %d",this->GetGUID(),this->GetMonsterModelID()); #endif retcode = SQLPrepare( cDatabase::m_hStmt, (unsigned char *)szCommand, SQL_NTS ); CHECKRETURN(1, SQL_HANDLE_STMT, cDatabase::m_hStmt, NULL) retcode = SQLExecute( cDatabase::m_hStmt ); int iCol = 3; retcode = SQLBindCol( cDatabase::m_hStmt, iCol++, SQL_C_LONG, &ItemType, sizeof( INT ), NULL ); CHECKRETURN(1, SQL_HANDLE_STMT, cDatabase::m_hStmt, NULL) retcode = SQLBindCol( cDatabase::m_hStmt, iCol++, SQL_C_LONG, &dwItemModelID, sizeof( DWORD ), NULL ); CHECKRETURN(1, SQL_HANDLE_STMT, cDatabase::m_hStmt, NULL) who->m_pcAvatar->m_CorpseTarget = this->GetGUID(); cMessage cmSetPackContents; cmSetPackContents << 0xF7B0L << who->m_pcAvatar->GetGUID( ) << ++who->m_dwF7B0Sequence << 0x0196L << m_dwGUID; for ( int i = 0; SQLFetch( cDatabase::m_hStmt ) == SQL_SUCCESS; i++ ) { //Cubem0j0: We find the model number so we can get some vars. cItemModels *pcModel = cItemModels::FindModel(dwItemModelID); switch(ItemType) { case 1: { cWeapon* aWeapon = new cWeapon(cWorldManager::NewGUID_Object(),this->GetGUID(),dwItemModelID,1.0,TRUE,pcModel->m_wIcon,pcModel->m_strName,pcModel->m_strDescription,pcModel->m_wBurden,pcModel->m_dwValue,pcModel->m_bWieldType); who->AddPacket( WORLD_SERVER, aWeapon->CreatePacketContainer(this->GetGUID(),pcModel->m_dwModelID),3); this->AddInventory(aWeapon); cmSetPackContents << 0x01L << aWeapon->GetGUID() << 0x0L; #ifdef _DEBUG cMasterServer::ServerMessage( ColorYellow,NULL,"Case 1"); #endif break; } case 2: { cFood* aFood = new cFood(cWorldManager::NewGUID_Object(),this->GetGUID(),dwItemModelID,pcModel->m_wIcon,pcModel->m_strName,pcModel->m_strDescription,pcModel->m_wBurden,pcModel->m_dwValue); who->AddPacket( WORLD_SERVER, aFood->CreatePacketContainer(this->GetGUID(),pcModel->m_dwModelID),3); this->AddInventory(aFood); cmSetPackContents << aFood->GetGUID() << 0x0L; break; } case 3: { cArmor* aArmor = new cArmor(cWorldManager::NewGUID_Object(),this->GetGUID(),dwItemModelID,1.0,TRUE,pcModel->m_wIcon,pcModel->m_strName,pcModel->m_strDescription,pcModel->m_dwValue,pcModel->m_wBurden); who->AddPacket( WORLD_SERVER, aArmor->CreatePacketContainer(this->GetGUID(),pcModel->m_dwModelID),3); this->AddInventory(aArmor); cmSetPackContents << aArmor->GetGUID() << 0x0L; break; } case 8: { cWands* aWand = new cWands(cWorldManager::NewGUID_Object(),this->GetGUID(),dwItemModelID,1.0,TRUE,pcModel->m_wIcon,pcModel->m_strName,pcModel->m_strDescription,pcModel->m_dwValue,pcModel->m_wBurden); who->AddPacket( WORLD_SERVER, aWand->CreatePacketContainer(this->GetGUID(),pcModel->m_dwModelID),3); this->AddInventory(aWand); cmSetPackContents << aWand->GetGUID() << 0x0L; break; } case 13: { cSpellComps* aReg = new cSpellComps(cWorldManager::NewGUID_Object(),this->GetGUID(),dwItemModelID,1.0,TRUE,pcModel->m_wIcon,pcModel->m_strName,pcModel->m_strDescription,pcModel->m_dwValue,pcModel->m_wBurden); who->AddPacket( WORLD_SERVER, aReg->CreatePacketContainer(this->GetGUID(),pcModel->m_dwModelID),3); this->AddInventory(aReg); //cmSetPackContents << 0x01L << aReg->GetGUID() << 0x0L; break; } } who->AddPacket(WORLD_SERVER,cmSetPackContents,4); } */ //I just explicitly use a model ID here, I will change this to a function later. cItemModels *pcModel = cItemModels::FindModel(147); //Use the Item Container classes to create the item in a corpse/chest. cLockpicks* Lockpicks = new cLockpicks(cWorldManager::NewGUID_Object(),this->GetGUID(),pcModel->m_dwModelNumber,1.0,TRUE,pcModel->m_wIcon,pcModel->m_strName,pcModel->m_strDescription,pcModel->m_wUses,pcModel->m_wUseLimit); //Need a way for the cObject class to find the Item Model ID Lockpicks->m_dwItemModelID = pcModel->m_dwModelID; //Send the create packet who->AddPacket(WORLD_SERVER,Lockpicks->CreatePacketContainer(this->GetGUID(),Lockpicks->GetItemModelID()),3); #ifdef _DEBUG cMasterServer::ServerMessage( ColorYellow,NULL,"ItemModelID is: %d",Lockpicks->GetItemModelID()); #endif //Add the item to the corpse inventory. this->AddInventory(Lockpicks); //SQL Done // retcode = SQLCloseCursor( cDatabase::m_hStmt ); CHECKRETURN( 0, SQL_HANDLE_STMT, cDatabase::m_hStmt, NULL ) // retcode = SQLFreeStmt( cDatabase::m_hStmt, SQL_UNBIND ); CHECKRETURN( 0, SQL_HANDLE_STMT, cDatabase::m_hStmt, NULL ) //Set the GUID of the corpse on the avatar (this is used in find the item in the corpses inventory) who->m_pcAvatar->m_CorpseTarget = this->GetGUID(); if(this->FindInventory(Lockpicks->GetGUID())) { #ifdef _DEBUG cMasterServer::ServerMessage( ColorYellow,NULL,"Item Found"); #endif } //Display the item slots in the chest (0x0196) cMessage cmSetPackContents; cmSetPackContents << 0xF7B0L << who->m_pcAvatar->GetGUID( ) << ++who->m_dwF7B0Sequence << 0x0196L << m_dwGUID << 0x01L << Lockpicks->GetGUID() << 0x0L; who->AddPacket(WORLD_SERVER,cmSetPackContents,4); cMessage cmActionComplete; cmActionComplete << 0xF7B0L << who->m_pcAvatar->GetGUID( ) << ++who->m_dwF7B0Sequence << 0x01C7L << 0L; who->AddPacket(WORLD_SERVER,cmActionComplete,4); this->SetState(1); break; } case 1: { #ifdef _DEBUG cMasterServer::ServerMessage(ColorYellow,NULL,"m_wState is %d",m_wState); #endif //Close Container cMessage cmCloseContainer; cmCloseContainer << 0xF7B0L << who->m_pcAvatar->GetGUID( ) << ++who->m_dwF7B0Sequence << 0x0052L << m_dwGUID; who->AddPacket(WORLD_SERVER,cmCloseContainer,4); cMessage cmActionComplete; cmActionComplete << 0xF7B0L << who->m_pcAvatar->GetGUID( ) << ++who->m_dwF7B0Sequence << 0x01C7L << 0L; who->AddPacket(WORLD_SERVER,cmActionComplete,4); this->SetState(0); break; } } } /** * Handles the assessment of corpse objects. * * This function is called whenever a corpse is assessed by a client. * Returns a server message to the client. */ void cCorpse::Assess(cClient *pcAssesser) { cMessage cmAssess; DWORD flags = 0x00000009; cmAssess << 0xF7B0L << pcAssesser->m_pcAvatar->GetGUID() << ++pcAssesser->m_dwF7B0Sequence << 0xC9L << m_dwGUID << flags << 0x01L //Success = 0x01, Failure = 0x00 << WORD(0x0002) << WORD(0x0010) << 0x13L //Value << 0x00L //Corpse always zero. << 0x05L //Burden << 0xCCL //for now, static amount it eventuall should add up all the item bu's << WORD(0x0001) //Count (as above) total number of DWORDS << WORD(0x0008) //Unknown << 0x10L << this->m_strDescription.c_str(); //This is the "Killed by" message when ID'd. pcAssesser->AddPacket(WORLD_SERVER,cmAssess,4); }