Background:
With LDoN a special type of treasure chest was introduced, which players could "open" by clicking on the Open pushbutton. Most everyone should be familiar with this type of object.
Technically, chests are nothing other than a special type of NPC, having a spawnpoint and loot like any other mob. Opening a chest is instantly killing this mob, causing it to play its death animation (ie chest cracking open).
I've gone ahead and implemented an
initial version of this functionality. It's still vs 1030, but it should be no problem to apply to a later version, since it's mostly one line additions. Since I only have a Titanium client to test with, I've only updated the Titanium Client opcode list so this will NOT work out of the box for any other clients.
Patches:
common/classes.h
Code:
--- ../EQEmu-0.7.0-1030/common/classes.h 2006-05-13 04:35:58.000000000 +0200
+++ ../EQEmu/common/classes.h 2007-08-30 10:36:40.000000000 +0200
@@ -58,6 +58,7 @@
#define ADVENTURERECRUITER 60
#define ADVENTUREMERCHANT 61
#define CORPSE_CLASS 62 //only seen on Danvi's Corpse in Akheva so far..
+#define LDON_TREASURE 62 // LDON Treasure Chests, Barrels, etc.
#define TRIBUTE_MASTER 63
#define GUILD_TRIBUTE_MASTER 64 //not sure
#define warrior_1 1
common/emu_oplist.h
Code:
--- ../EQEmu-0.7.0-1030/common/emu_oplist.h 2007-08-05 23:13:12.000000000 +0200
+++ ../EQEmu/common/emu_oplist.h 2007-08-30 00:30:46.000000000 +0200
@@ -355,6 +355,7 @@
N(OP_ReclaimCrystals),
N(OP_Report),
N(OP_SenseHeading),
+N(OP_LDoNOpen),
N(OP_DynamicWall),
N(OP_RequestTitles),
N(OP_PurchaseLeadershipAA),
common/opcode_dispatch.h
Code:
--- ../EQEmu-0.7.0-1030/common/opcode_dispatch.h 2007-07-22 22:54:32.000000000 +0200
+++ ../EQEmu/common/opcode_dispatch.h 2007-08-30 00:28:06.000000000 +0200
@@ -129,6 +129,7 @@
INz(OP_FeignDeath); //?
INz(OP_Sneak); //?
INz(OP_Hide); //?
+INz(OP_LDoNOpen);
INv(OP_ChannelMessage, ChannelMessage_Struct);
IN(OP_WearChange, WearChange_Struct);
IN(OP_DeleteSpawn, EntityId_Struct); //client->server follows OP_SaveOnZoneReq
People with access to other clients should try and see if they can find the "Open" Opcode for their client and update the config files accordingly.
utils/patch_Titanium.conf
Code:
--- ../EQEmu-0.7.0-1030/utils/patch_Titanium.conf 2007-08-05 23:13:12.000000000 +0200
+++ ../patch_Titanium.conf 2007-08-30 13:34:44.000000000 +0200
@@ -398,6 +398,7 @@
OP_Sneak=0x74e1
OP_Fishing=0x0b36
OP_InstillDoubt=0x389e #intimidation
+OP_LDoNOpen=0x083b # CB: open LDoN chest 08/30/07
#Task packets
OP_CompletedTasks=0x76a2 # ShowEQ 10/27/05
zone/aggro.cpp
Code:
--- ../EQEmu-0.7.0-1030/zone/aggro.cpp 2007-07-17 03:03:48.000000000 +0200
+++ ../EQEmu/zone/aggro.cpp 2007-08-30 11:26:44.000000000 +0200
@@ -455,7 +455,10 @@
// solar: the format here is a matrix of mob type vs mob type.
// redundant ones are omitted and the reverse is tried if it falls through.
-
+ // cb: LDoN Treasure chests dont fight
+ if(this->GetClass() == LDON_TREASURE && this->GetBodyType() == BT_Boxes)
+ return(false);
+
// first figure out if we're pets. we always look at the master's flags.
// no need to compare pets to anything
mob1 = our_owner ? our_owner : this;
The follow patch looks huge; all it really does is prevent players from getting
EXP for their chest "kills". I did some code reformatting, that is why there's so many changes.
zone/attack.cpp
Code:
--- ../EQEmu-0.7.0-1030/zone/attack.cpp 2007-08-11 08:14:57.000000000 +0200
+++ ../EQEmu/zone/attack.cpp 2007-08-30 11:47:45.000000000 +0200
@@ -1102,11 +1102,13 @@
char tmp[20];
database.GetVariable("ServerType", tmp, 9);
- if(atoi(tmp)==1 && other != NULL && other->IsClient()){
+ if(atoi(tmp)==1 && other != NULL && other->IsClient())
+ {
char tmp2[10] = {0};
database.GetVariable("PvPreward", tmp, 9);
int reward = atoi(tmp);
- if(reward==3){
+ if(reward==3)
+ {
database.GetVariable("PvPitem", tmp2, 9);
int pvpitem = atoi(tmp2);
if(pvpitem>0 && pvpitem<200000)
@@ -1118,11 +1120,15 @@
new_corpse->SetPKItem(1);
else
new_corpse->SetPKItem(0);
- if(other->CastToClient()->isgrouped) {
+ if(other->CastToClient()->isgrouped)
+ {
Group* group = entity_list.GetGroupByClient(other->CastToClient());
- if(group != 0) {
- for(int i=0;i<6;i++) {
- if(group->members[i] != NULL) {
+ if(group != 0)
+ {
+ for(int i=0;i<6;i++)
+ {
+ if(group->members[i] != NULL)
+ {
new_corpse->AllowMobLoot(group->members[i],i);
}
}
@@ -1490,7 +1496,6 @@
safe_delete(app);
-
Mob *give_exp = hate_list.GetDamageTop(this);
if(give_exp == NULL)
give_exp = killer;
@@ -1501,30 +1506,36 @@
if(give_exp && give_exp->IsClient())
give_exp_client = give_exp->CastToClient();
- if (give_exp_client && !IsCorpse() && MerchantType == 0)
+ if(!(this->GetClass() == LDON_TREASURE && this->GetBodyType() == BT_Boxes))
{
- Group *kg = entity_list.GetGroupByClient(give_exp_client);
- if (give_exp_client->IsGrouped() && kg != NULL)
+ // cb: if we're not a LDON treasure chest, we give XP
+
+ if (give_exp_client && !IsCorpse() && MerchantType == 0)
{
- if(give_exp_client->GetAdventureID()>0){
- AdventureInfo AF = database.GetAdventureInfo(give_exp_client->GetAdventureID());
- if(zone->GetZoneID() == AF.zonedungeonid && AF.type==ADVENTURE_MASSKILL)
- give_exp_client->SendAdventureUpdate();
- else if(zone->GetZoneID() == AF.zonedungeonid && AF.type==ADVENTURE_NAMED && (AF.Objetive==GetNPCTypeID() || AF.ObjetiveValue==GetNPCTypeID()))
- give_exp_client->SendAdventureFinish(1, AF.points,true);
+ Group *kg = entity_list.GetGroupByClient(give_exp_client);
+ if (give_exp_client->IsGrouped() && kg != NULL)
+ {
+ if(give_exp_client->GetAdventureID()>0){
+ AdventureInfo AF = database.GetAdventureInfo(give_exp_client->GetAdventureID());
+ if(zone->GetZoneID() == AF.zonedungeonid && AF.type==ADVENTURE_MASSKILL)
+ give_exp_client->SendAdventureUpdate();
+ else if(zone->GetZoneID() == AF.zonedungeonid && AF.type==ADVENTURE_NAMED && (AF.Objetive==GetNPCTypeID() || AF.ObjetiveValue==GetNPCTypeID()))
+ give_exp_client->SendAdventureFinish(1, AF.points,true);
+ }
+ kg->SplitExp((EXP_FORMULA), this);
+ }
+ else
+ {
+ int conlevel = give_exp->GetLevelCon(GetLevel());
+ if (conlevel != CON_GREEN)
+ {
+ give_exp_client->AddEXP((EXP_FORMULA), conlevel); // Pyro: Comment this if NPC death crashes zone
+ }
}
- kg->SplitExp((EXP_FORMULA), this);
- }
- else
- {
- int conlevel = give_exp->GetLevelCon(GetLevel());
- if (conlevel != CON_GREEN)
- {
- give_exp_client->AddEXP((EXP_FORMULA), conlevel); // Pyro: Comment this if NPC death crashes zone
- }
}
}
+
//do faction hits even if we are a merchant, so long as a player killed us
if(give_exp_client)
hate_list.DoFactionHits(GetNPCFactionID());
zone/client_packet.cpp
Code:
--- ../EQEmu-0.7.0-1030/zone/client_packet.h 2007-07-21 22:39:54.000000000 +0200
+++ ../EQEmu/zone/client_packet.h 2007-08-30 00:41:01.000000000 +0200
@@ -57,6 +57,7 @@
void Handle_OP_Camp(const EQApplicationPacket *app);
void Handle_OP_Logout(const EQApplicationPacket *app);
void Handle_OP_SenseHeading(const EQApplicationPacket *app);
+ void Handle_OP_LDoNOpen(const EQApplicationPacket *app);
void Handle_OP_FeignDeath(const EQApplicationPacket *app);
void Handle_OP_Sneak(const EQApplicationPacket *app);
void Handle_OP_Hide(const EQApplicationPacket *app);
zone/client_packet.cpp
Code:
--- ../EQEmu-0.7.0-1030/zone/client_packet.cpp 2007-08-05 23:13:12.000000000 +0200
+++ ../EQEmu/zone/client_packet.cpp 2007-08-30 11:27:13.000000000 +0200
@@ -157,6 +157,7 @@
ConnectedOpcodes[OP_Camp] = &Client::Handle_OP_Camp;
ConnectedOpcodes[OP_Logout] = &Client::Handle_OP_Logout;
// ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_SenseHeading;
+ ConnectedOpcodes[OP_LDoNOpen] = &Client::Handle_OP_LDoNOpen;
ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath;
ConnectedOpcodes[OP_Sneak] = &Client::Handle_OP_Sneak;
ConnectedOpcodes[OP_Hide] = &Client::Handle_OP_Hide;
@@ -2312,6 +2313,18 @@
return;
}
+void Client::Handle_OP_LDoNOpen(const EQApplicationPacket *app)
+{
+ Mob * target = GetTarget();
+ if(target)
+ {
+ if(target->IsMob() && target->GetClass()==LDON_TREASURE && target->GetBodyType()==BT_Boxes)
+ target->Damage((Mob *)this, target->GetMaxHP()*2, SPELL_UNKNOWN, HAND_TO_HAND, false);
+ else
+ cout << "Illegal target: OP_LDoNOpen expects mob class " << LDON_TREASURE << ", bodytype " << BT_Boxes << endl;
+ }
+}
+
void Client::Handle_OP_Dye(const EQApplicationPacket *app)
{
if(app->size!=sizeof(DyeStruct))
To test it, create a mob with any race/level, class=62 (LDON Chest) and bodytype=33 (BT_Boxes). I've included the bodytype criteria in order to make sure that there's very little chance of there being actual mobs with both class=62 and bodytype=33 in your databases.
Enjoy,
-Chris