PDA

View Full Version : LDoN Open Button Support


cbodmer
08-29-2007, 11:58 PM
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
--- ../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
--- ../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
--- ../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
--- ../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
--- ../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
--- ../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
--- ../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
--- ../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

KLS
08-30-2007, 02:37 AM
Nice, ima go ahead and deal with these submissions on forum tonight I think and I'll look at this too.

Teppen
08-30-2007, 05:12 AM
IIRC, I think those LDON chests were also (or maybe it was random) but seemed to have traps on chests. I remember doing a LDON once and we were all medding up and the warrior saw a chest and said oh look a chest lets open it, and before the rogue could say WAIT!!! poof the warrior triggered the chest and got a nasty spell casted on him from not removing the trap first. It was horrible, we were just in our lower 20's and the spell that was casted on the warrior took a (might have bee higher..) level 49 cleric spell to disappear. lol. Cant remembe what spell it was but reduced all this stats to 1's or something like that.

Anyway, would there be away to put traps on these chests?

Good job btw, always look forward to new features like this.

CrabClaw
08-30-2007, 05:49 AM
Yes! Awsome :)

My whole guild used to do tons of LDoNs back in the day, it was our specialty actually instead of the usual big-time raiding.

The dreaded 'everyone-clear-out-the-instance-and-let-the-rogue-pick-it' chests! Poor guys....they were heroes if they got it off though.

The nasty little boxes would do a couple things, one cast a nasty AoE poison DoT that would eventually kill you regardless of level, make you uber-drunk, or blow up, and long term AoE fear (Which is why we waited till the end of the run to deal with them....). Not your choice, and the effects were uber-nasty, but made for some fun stories.

The chests were around trapped around 90% of the time (ballpark figure).

We usually cleared everyone out at the end and let the rogue handle them cause' the AoE effects filled most of the instance. They were though a great source of some neat instance specific drops, gems, coin and Augmentations. They had generous loot tables.

You just had to 'de-trap and pray' most of the time though.

cbodmer
08-30-2007, 08:26 AM
I'll see how traps could be done. I like the idea.
-Chris

John Adams
08-30-2007, 11:08 AM
Thank you, CB. My dreams of seeing LDoNs become a reality again are coming true. :)

Striat
08-31-2007, 02:28 PM
Nice addition. It will be very useful in the near future when conditions are implemented to restrict what is required to open a chest. But, it definitely is a nice start. Been missing this feature for a while.

KLS
08-31-2007, 02:51 PM
This along with a plethora of other things on this forum will go in tonight most likely.

..just have to finish making sure it all works.

gernblan
09-01-2007, 04:22 AM
I'll see how traps could be done. I like the idea.
-Chris

I would think that putting quests on the npcid of the particular chest with a random EVENT_DEATH trap might emulate them.

With a script, can also do proximity stuff to chests.... or whatever else.

Anyway, it may be more flexible to do it this way than to hardcode something.

Just a thought.

cbodmer
09-02-2007, 05:10 AM
ps: Mods, can you please edit my first post, remove all it says there and write something like "Obsolete - checked further down". Thanks.

Angelox
09-02-2007, 05:49 AM
Thread closed, See new " Update to LDoN Open Button Support"