Ver Fonte

Rest of Chest Traps and Disarm Chest Traps implementation

Fixes #24 - chest traps and the ability to disarm is implemented.  Can restrict by zone (or applicable to all zones using -1), min and max chest difficulties.

1 = Small Chest (no trigger)
2 =  Treasure Chest (trigger)
3 = Ornate Chest (trigger)
5 = Exquisite Chest (trigger)

DB/updates/chest_traps_tableandsamples.txt included - this will allow you to source in the chest_traps and some 'example' traps (not real ones just testing ones).

Disarm command also required for right click option on chest: insert into commands set type=0,command='disarm',subcommand='',handler=510,required_status=0;
Image há 4 anos atrás
pai
commit
d7a00658d1

+ 57 - 0
DB/updates/chest_traps_tableandsamples.txt

@@ -0,0 +1,57 @@
+-- MariaDB dump 10.17  Distrib 10.4.12-MariaDB, for Win64 (AMD64)
+--
+-- Host: localhost    Database: eq2emu
+-- ------------------------------------------------------
+-- Server version	10.4.12-MariaDB
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8mb4 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `chest_traps`
+--
+
+DROP TABLE IF EXISTS `chest_traps`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `chest_traps` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `applicable_zone_id` int(10) NOT NULL DEFAULT 0,
+  `chest_min_difficulty` int(10) unsigned NOT NULL DEFAULT 0,
+  `chest_max_difficulty` int(10) unsigned NOT NULL DEFAULT 0,
+  `spell_id` int(10) unsigned NOT NULL DEFAULT 0,
+  `spell_tier` int(10) unsigned NOT NULL DEFAULT 0,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `chest_traps`
+--
+
+LOCK TABLES `chest_traps` WRITE;
+/*!40000 ALTER TABLE `chest_traps` DISABLE KEYS */;
+INSERT INTO `chest_traps` VALUES (1,-1,0,0,230160,1),(2,-1,0,0,230116,1),(3,-1,0,0,230080,1),(4,-1,0,0,230103,1);
+/*!40000 ALTER TABLE `chest_traps` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2020-03-16 13:16:05
+
+insert into commands set type=0,command='disarm',subcommand='',handler=510,required_status=0;

+ 1 - 0
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -1998,6 +1998,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			break;
 									 }
 		case COMMAND_LOOT_CORPSE:
+		case COMMAND_DISARM:
 		case COMMAND_LOOT:{
 			if (cmdTarget && ((Entity*)cmdTarget)->IsNPC() && cmdTarget->Alive())
 			{

+ 1 - 0
EQ2/source/WorldServer/Commands/Commands.h

@@ -871,6 +871,7 @@ private:
 
 #define COMMAND_OPEN					508
 #define COMMAND_CASTSPELL				509
+#define COMMAND_DISARM					510
 
 #define GET_AA_XML						751
 #define ADD_AA							752

+ 1 - 0
EQ2/source/WorldServer/Items/Loot.cpp

@@ -430,6 +430,7 @@ NPC* Entity::DropChest() {
 	chest->SetShowLevel(0);
 	chest->SetShowName(1);
 	chest->SetTargetable(1);
+	chest->SetLevel(GetLevel());
 
 	// Set the brain to a blank brain so it does nothing
 	chest->SetBrain(new BlankBrain(chest));

+ 3 - 0
EQ2/source/WorldServer/Skills.cpp

@@ -487,6 +487,9 @@ int Skill::CheckDisarmSkill(int16 targetLevel, int8 chest_difficulty)
 	if (chest_difficulty < 2) // no triggers on this chest type
 		return 1;
 
+	if (targetLevel < 1)
+		targetLevel = 1;
+
 	int	chest_diff_result = targetLevel * chest_difficulty;
 	float base_difficulty = 15.0f;
 	float fail_threshold = 10.0f;

+ 23 - 0
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -44,6 +44,7 @@ along with EQ2Emulator.  If not, see <http://www.gnu.org/licenses/>.
 #include "Languages.h"
 #include "Traits/Traits.h"
 #include "ClientPacketFunctions.h"
+#include "Zone/ChestTrap.h"
 
 extern Classes classes;
 extern Commands commands;
@@ -64,6 +65,7 @@ extern GuildList guild_list;
 extern MasterCollectionList master_collection_list;
 extern RuleManager rule_manager;
 extern MasterLanguagesList master_languages_list;
+extern ChestTrapList chest_trap_list;
 
 //devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp
 #if defined(__GNUC__)
@@ -6583,3 +6585,24 @@ void WorldDatabase::FindSpell(Client* client, char* findString)
 			client->Message(CHANNEL_COLOR_YELLOW, "End Spell Results for %s", findString);
 		}
 }
+
+void WorldDatabase::LoadChestTraps() {
+	chest_trap_list.Clear();
+	int32 index = 0;
+	Query query;
+	MYSQL_ROW row;
+	MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, applicable_zone_id, chest_min_difficulty, chest_max_difficulty, spell_id, spell_tier FROM chest_traps");
+	if (result && mysql_num_rows(result) > 0) {
+		Title* title = 0;
+		while (result && (row = mysql_fetch_row(result))) {
+			int32 dbid = atoul(row[0]);
+			sint32 applicable_zone_id = atoi(row[1]);
+			int32 mindifficulty = atoul(row[2]);
+			int32 maxdifficulty = atoul(row[3]);
+			int32 spellid = atoul(row[4]);
+			int32 tier = atoul(row[5]);
+			ChestTrap* trap = new ChestTrap(dbid,applicable_zone_id,mindifficulty,maxdifficulty,spellid,tier);
+			chest_trap_list.AddChestTrap(trap);
+		}
+	}
+}

+ 2 - 0
EQ2/source/WorldServer/WorldDatabase.h

@@ -568,6 +568,8 @@ public:
 	void                LoadTransmuting();
 
 	void				FindSpell(Client* client, char* findString);
+
+	void				LoadChestTraps();
 private:
 	DatabaseNew			database_new;
 	map<int32, string>	zone_names;

+ 204 - 0
EQ2/source/WorldServer/Zone/ChestTrap.cpp

@@ -0,0 +1,204 @@
+#include "ChestTrap.h"
+#include <vector>
+#include <algorithm>    // std::random_shuffle
+
+int32 ChestTrapList::Size() {
+
+	MChestTrapList.readlock(__FUNCTION__, __LINE__);
+	int32 size = chesttrap_list.size();
+	MChestTrapList.releasereadlock(__FUNCTION__, __LINE__);
+	return size;
+}
+
+void ChestTrapList::AddChestTrap(ChestTrap* trap) {
+	if (trap->GetDBID() < 1)
+		return;
+
+	MChestTrapList.writelock(__FUNCTION__, __LINE__);
+	if (chesttrap_list.count(trap->GetDBID()) > 0)
+	{
+		ChestTrap* tmpTrap = chesttrap_list[trap->GetDBID()];
+		chesttrap_list.erase(trap->GetDBID());
+		safe_delete(tmpTrap);
+	}
+
+	chesttrap_list[trap->GetDBID()] = trap;
+	MChestTrapList.releasewritelock(__FUNCTION__, __LINE__);
+}
+
+ChestTrap::ChestTrapInfo ChestTrapList::GetChestTrap(int32 id) {
+	ChestTrap* res = 0;
+	MChestTrapList.readlock(__FUNCTION__, __LINE__);
+	if (chesttrap_list.count(id) > 0)
+		res = chesttrap_list[id];
+
+	ChestTrap::ChestTrapInfo cti;
+	memset(&cti, 0, sizeof(ChestTrap::ChestTrapInfo));
+	if (res)
+		memcpy(&cti, &res->GetChestTrapInfo(), sizeof(ChestTrap::ChestTrapInfo));
+	MChestTrapList.releasereadlock(__FUNCTION__, __LINE__);
+
+	return cti;
+}
+
+ChestTrap::ChestTrapInfo ChestTrapList::GetNextTrap(int32 zoneid, int32 chest_difficulty)
+{
+	MChestListsInUse.writelock(__FUNCTION__, __LINE__);
+	ChestTrapList* zoneTrapList = GetChestListByZone(zoneid);
+	ChestTrapList* zoneDifficultyTrapList = zoneTrapList->GetChestListByDifficulty(chest_difficulty);
+
+	ChestTrap::ChestTrapInfo nextTrap = zoneTrapList->GetNextChestTrap();
+	MChestListsInUse.releasewritelock(__FUNCTION__, __LINE__);
+
+	return nextTrap;
+}
+
+void ChestTrapList::Clear() {
+	MChestListsInUse.writelock(__FUNCTION__, __LINE__);
+	ClearTraps();
+	ClearTrapList();
+	MChestListsInUse.releasewritelock(__FUNCTION__, __LINE__);
+}
+
+ChestTrap::ChestTrapInfo ChestTrapList::GetNextChestTrap() {
+	if (cycleItr == chesttrap_list.end())
+	{
+		//re-shuffle the map, we reached the end
+		shuffleMap(this);
+		cycleItr = chesttrap_list.begin();
+	}
+
+	ChestTrap* trap = cycleItr->second;
+
+	ChestTrap::ChestTrapInfo cti;
+	memset(&cti, 0, sizeof(ChestTrap::ChestTrapInfo));
+	if (trap)
+		memcpy(&cti, &trap->GetChestTrapInfo(), sizeof(ChestTrap::ChestTrapInfo));
+
+	cycleItr++;
+
+	return cti;
+}
+
+ChestTrapList* ChestTrapList::GetChestListByDifficulty(int32 difficulty) {
+	ChestTrapList* usedList = GetChestTrapList(ChestTrapBaseList::DIFFICULTY);
+	if (usedList && usedList->IsListLoaded())
+		return usedList;
+	else if (!usedList)
+		return NULL;
+
+	MChestTrapList.writelock(__FUNCTION__, __LINE__);
+	map<int32, ChestTrap*>::iterator itr;
+	for (itr = chesttrap_list.begin(); itr != chesttrap_list.end(); itr++) {
+		ChestTrap* curTrap = itr->second;
+		if ((curTrap->GetMinChestDifficulty() <= difficulty && difficulty <= curTrap->GetMaxChestDifficulty()) ||
+			(curTrap->GetMinChestDifficulty() == 0 && curTrap->GetMaxChestDifficulty() == 0))
+			usedList->AddChestTrap(curTrap);
+	}
+
+	shuffleMap(usedList);
+	usedList->SetListLoaded(true);
+
+	MChestTrapList.releasewritelock(__FUNCTION__, __LINE__);
+
+	return usedList;
+}
+
+ChestTrapList* ChestTrapList::GetChestListByZone(int32 zoneid) {
+	ChestTrapList* usedList = GetChestTrapList(ChestTrapBaseList::ZONE);
+	if (usedList && usedList->IsListLoaded())
+		return usedList;
+	else if (!usedList)
+		return NULL;
+
+	MChestTrapList.writelock(__FUNCTION__, __LINE__);
+	map<int32, ChestTrap*>::iterator itr;
+	for (itr = chesttrap_list.begin(); itr != chesttrap_list.end(); itr++) {
+		ChestTrap* curTrap = itr->second;
+		if (curTrap->GetApplicableZoneID() == zoneid || curTrap->GetApplicableZoneID() == -1)
+			usedList->AddChestTrap(curTrap);
+	}
+
+	shuffleMap(usedList);
+	usedList->SetListLoaded(true);
+
+	MChestTrapList.releasewritelock(__FUNCTION__, __LINE__);
+
+	return usedList;
+}
+
+map<int32, ChestTrap*>* ChestTrapList::GetAllChestTraps() { return &chesttrap_list; }
+bool	ChestTrapList::IsListLoaded() { return ListLoaded; }
+void	ChestTrapList::SetListLoaded(bool val) { ListLoaded = val; }
+
+ChestTrapList* ChestTrapList::GetChestTrapList(ChestTrapBaseList listName) {
+	ChestTrapList* ctl = 0;
+	MChestTrapInnerList.readlock(__FUNCTION__, __LINE__);
+	if (chesttrap_innerlist.count(listName) > 0)
+		ctl = chesttrap_innerlist[listName];
+	MChestTrapInnerList.releasereadlock(__FUNCTION__, __LINE__);
+
+	return ctl;
+}
+
+void ChestTrapList::ClearTraps() {
+	MChestTrapList.writelock(__FUNCTION__, __LINE__);
+	map<int32, ChestTrap*>::iterator itr;
+	for (itr = chesttrap_list.begin(); itr != chesttrap_list.end(); itr++)
+		safe_delete(itr->second);
+	chesttrap_list.clear();
+	MChestTrapList.releasewritelock(__FUNCTION__, __LINE__);
+}
+
+void ChestTrapList::ClearTrapList() {
+	MChestTrapInnerList.writelock(__FUNCTION__, __LINE__);
+	map<int32, ChestTrapList*>::iterator itr2;
+	for (itr2 = chesttrap_innerlist.begin(); itr2 != chesttrap_innerlist.end(); itr2++)
+		safe_delete(itr2->second);
+	chesttrap_innerlist.clear();
+	MChestTrapInnerList.releasewritelock(__FUNCTION__, __LINE__);
+
+	// reinstantiate the base lists (zone/difficulty/etc)
+	InstantiateLists(ChestTrapParent);
+}
+
+void ChestTrapList::SetupMutexes()
+{
+	MChestTrapList.SetName("ChestTrapList");
+	MChestTrapInnerList.SetName("MChestTrapInnerList");
+	MChestListsInUse.SetName("MChestListsInUse");
+}
+
+void ChestTrapList::InstantiateLists(bool parent)
+{
+	difficultyList = new ChestTrapList(parent);
+	zoneList = new ChestTrapList(parent);
+	MChestTrapInnerList.writelock(__FUNCTION__, __LINE__);
+	chesttrap_innerlist[ChestTrapBaseList::DIFFICULTY] = difficultyList;
+	chesttrap_innerlist[ChestTrapBaseList::ZONE] = zoneList;
+	MChestTrapInnerList.releasewritelock(__FUNCTION__, __LINE__);
+}
+
+void ChestTrapList::shuffleMap(ChestTrapList* list) {
+	std::vector<ChestTrap*> tmp_chests;
+
+	map<int32, ChestTrap*>::iterator itr;
+	for (itr = chesttrap_list.begin(); itr != chesttrap_list.end(); itr++) {
+		ChestTrap* curTrap = itr->second;
+		tmp_chests.push_back(curTrap);
+	}
+
+	std::random_shuffle(tmp_chests.begin(), tmp_chests.end());
+
+	chesttrap_list.clear();
+
+	int count = 0;
+
+	for (std::vector<ChestTrap*>::iterator it = tmp_chests.begin(); it != tmp_chests.end(); ++it)
+	{
+		chesttrap_list[count] = *it;
+		count++;
+	}
+
+	cycleItr = chesttrap_list.begin();
+}

+ 135 - 0
EQ2/source/WorldServer/Zone/ChestTrap.h

@@ -0,0 +1,135 @@
+#include <string>
+#include <map>
+#include <list>
+#include "../../common/Mutex.h"
+#include "../../common/types.h"
+#pragma once
+
+using namespace std;
+
+enum ChestTrapBaseList {
+	DIFFICULTY = 0,
+	ZONE = 1
+};
+
+class ChestTrap {
+public:
+	struct ChestTrapInfo {
+		int32	id;
+		int32	applicable_zone_id;
+		int32	min_chest_difficulty;
+		int32	max_chest_difficulty;
+		int32	spell_id;
+		int32	spell_tier;
+	};
+
+	//Constructors **must** always set all ChestTrapInfo as we don't memset so a data value will be wack if not set!
+	ChestTrap(int32 dbid, sint32 zoneid, int32 mindifficulty, int32 maxdifficulty, int32 spellid, int32 tier)
+	{
+		s_ChestTrapInfo.id = dbid;
+		s_ChestTrapInfo.applicable_zone_id = zoneid;
+		s_ChestTrapInfo.min_chest_difficulty = mindifficulty;
+		s_ChestTrapInfo.max_chest_difficulty = maxdifficulty;
+		s_ChestTrapInfo.spell_id = spellid;
+		s_ChestTrapInfo.spell_tier = tier;
+	}
+
+	ChestTrap(ChestTrap* parent)
+	{
+		s_ChestTrapInfo.id = parent->GetDBID();
+		s_ChestTrapInfo.applicable_zone_id = parent->GetApplicableZoneID();
+		s_ChestTrapInfo.min_chest_difficulty = parent->GetMinChestDifficulty();
+		s_ChestTrapInfo.max_chest_difficulty = parent->GetMaxChestDifficulty();
+		s_ChestTrapInfo.spell_id = parent->GetSpellID();
+		s_ChestTrapInfo.spell_tier = parent->GetSpellTier();
+	}
+
+	int32	GetDBID() { return s_ChestTrapInfo.id; }
+	sint32	GetApplicableZoneID() { return s_ChestTrapInfo.applicable_zone_id; }
+	int32	GetMinChestDifficulty() { return s_ChestTrapInfo.min_chest_difficulty; }
+	int32	GetMaxChestDifficulty() { return s_ChestTrapInfo.max_chest_difficulty; }
+	int32	GetSpellID() { return s_ChestTrapInfo.spell_id; }
+	int32	GetSpellTier() { return s_ChestTrapInfo.spell_tier; }
+
+	ChestTrapInfo GetChestTrapInfo() { return s_ChestTrapInfo; }
+private:
+	ChestTrapInfo s_ChestTrapInfo;
+};
+
+class ChestTrapList {
+public:
+	ChestTrapList() {
+		SetupMutexes();
+
+		ChestTrapParent = true;
+		// instantiate the parent lists for zone/difficulty/etc, later on we will do the inverse of each map, zone->difficulty and difficulty->zone
+		InstantiateLists(true);
+		ListLoaded = true;
+	}
+
+	// not to be called externally from ChestTrapList/ChestTrap
+	ChestTrapList(bool parentClass) {
+		SetupMutexes();
+
+		ChestTrapParent = parentClass;
+
+		// instantiates one inner layer to the top layer of chest trap lists
+		if (parentClass)
+			InstantiateLists(false);
+
+		ListLoaded = false;
+	}
+
+	~ChestTrapList() {
+			Clear();
+	}
+
+	int32 Size();
+
+	void AddChestTrap(ChestTrap* trap);
+
+	ChestTrap::ChestTrapInfo GetChestTrap(int32 id);
+
+	ChestTrap::ChestTrapInfo GetNextTrap(int32 zoneid, int32 chest_difficulty);
+
+	void Clear();
+private:
+	// randomized maps so we just iterate the map for our next 'random' result
+	ChestTrap::ChestTrapInfo GetNextChestTrap();
+
+	ChestTrapList* GetChestListByDifficulty(int32 difficulty);
+
+	ChestTrapList* GetChestListByZone(int32 zoneid);
+
+	map<int32, ChestTrap*>* GetAllChestTraps();
+	bool	IsListLoaded();
+	void	SetListLoaded(bool val);
+
+	void	SetCycleIterator(map<int32, ChestTrap*>::iterator itr);
+
+	ChestTrapList* GetChestTrapList(ChestTrapBaseList listName);
+
+	void ClearTraps();
+	void ClearTrapList();
+
+	void SetupMutexes();
+
+	void InstantiateLists(bool parent);
+	
+	void shuffleMap(ChestTrapList* list);
+
+	bool ChestTrapParent;
+	bool ListLoaded;
+	map<int32, ChestTrap*> chesttrap_list;
+	map<int32, ChestTrapList*> chesttrap_innerlist;
+
+	ChestTrapList* difficultyList;
+	ChestTrapList* zoneList;
+
+	map<int32, ChestTrap*>::iterator cycleItr;
+
+	Mutex	MChestTrapList;
+	Mutex	MChestTrapInnerList;
+
+	Mutex	MChestListsInUse;
+};

+ 26 - 4
EQ2/source/WorldServer/client.cpp

@@ -38,6 +38,8 @@ along with EQ2Emulator.  If not, see <http://www.gnu.org/licenses/>.
 #include "Titles.h"
 #include "IRC/IRC.h"
 #include "Chat/Chat.h"
+#include "SpellProcess.h"
+#include "Zone/ChestTrap.h"
 
 //#include "Quests.h"
 
@@ -117,6 +119,7 @@ extern IRC irc;
 extern Chat chat;
 extern MasterAAList master_aa_list;
 extern MasterAAList master_tree_nodes;
+extern ChestTrapList chest_trap_list;
 
 using namespace std;
 
@@ -4099,6 +4102,8 @@ void Client::OpenChest(Entity* entity)
 
 	if (chest_difficulty > 0 && !entity->HasTrapTriggered())
 	{
+		ChestTrap::ChestTrapInfo nextTrap = chest_trap_list.GetNextTrap(GetCurrentZone()->GetZoneID(), chest_difficulty);
+
 		Skill* disarmSkill = GetPlayer()->GetSkillByName("Disarm Trap", false);
 		firstChestOpen = true;
 		entity->SetTrapTriggered(true);
@@ -4106,20 +4111,37 @@ void Client::OpenChest(Entity* entity)
 		{
 			if (disarmSkill->CheckDisarmSkill(entity->GetLevel(), chest_difficulty) < 1)
 			{
-				//Spell* spell = master_spell_list.GetSpell(spellid, tier);
+				Spell* spell = master_spell_list.GetSpell(nextTrap.spell_id, nextTrap.spell_tier);
+				if (spell)
+				{
+					// Get the current zones spell process
+					SpellProcess* spellProcess = GetCurrentZone()->GetSpellProcess();
 
-				//GetPlayer()->GetZone()->GetSpellProcess()->CastInstant(spell, (Entity*)GetPlayer(), (Entity*)GetPlayer());
+					spellProcess->CastInstant(spell, entity, (Entity*)GetPlayer());
+				}
 
 				SimpleMessage(CHANNEL_COLOR_YELLOW, "You failed to disarm the chest.");
 			}
 			else
 			{
 				SimpleMessage(CHANNEL_COLOR_YELLOW, "You have disarmed the chest.");
-				GetPlayer()->GetSkillByName("Disarm Trap", true);
 			}
+
+			// despite fail/succeed we should always try to increase skill if disarm is available
+			GetPlayer()->GetSkillByName("Disarm Trap", true);
 		}
-		else
+		else // no disarm skill, always fail
 		{
+			Spell* spell = master_spell_list.GetSpell(nextTrap.spell_id, nextTrap.spell_tier);
+
+			if (spell)
+			{
+				// Get the current zones spell process
+				SpellProcess* spellProcess = GetCurrentZone()->GetSpellProcess();
+
+				spellProcess->CastInstant(spell, entity, (Entity*)GetPlayer());
+			}
+
 			SimpleMessage(CHANNEL_COLOR_YELLOW, "You triggered the chest.");
 		}
 	}

+ 1 - 0
EQ2/source/WorldServer/makefile.a64

@@ -141,6 +141,7 @@ SRC= ../common/Condition.o \
 	World.o \
 	WorldDatabase.o \
 	Zone/SPGrid.o \
+	Zone/ChestTraps.o \
 	zoneserver.o
 
 

+ 5 - 0
EQ2/source/WorldServer/net.cpp

@@ -55,6 +55,7 @@ using namespace std;
 #include "Traits/Traits.h"
 #include "IRC/IRC.h"
 #include "Transmute.h"
+#include "Zone/ChestTrap.h"
 
 #ifdef WIN32
 	#include <process.h>
@@ -107,6 +108,7 @@ int last_signal = 0;
 RuleManager rule_manager;
 MasterTitlesList master_titles_list;
 MasterLanguagesList master_languages_list;
+ChestTrapList chest_trap_list;
 extern MasterAchievementList master_achievement_list;
 extern map<int16, int16> EQOpcodeVersions;
 PatchServer patch;
@@ -334,6 +336,9 @@ int main(int argc, char** argv) {
 	LogWrite(WORLD__INFO, 0, "World", "Loading Transmuting Data...");
 	database.LoadTransmuting();
 
+	LogWrite(WORLD__INFO, 0, "World", "Loading Chest Trap Data...");
+	database.LoadChestTraps();
+
 	if (threadedLoad) {
 		LogWrite(WORLD__INFO, 0, "World", "Waiting for load threads to finish.");
 		while (!world.items_loaded || !world.spells_loaded /*|| !world.achievments_loaded*/)

+ 5 - 3
EQ2/win/VC10Projects/EQ2World.vcxproj

@@ -117,7 +117,7 @@
     </Midl>
     <ClCompile>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WORLD;DEBUG;_EQDEBUG;WIN32;_CONSOLE;EQ2;EQN_DEBUG;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WORLD;_DEBUG;_EQDEBUG;WIN32;_CONSOLE;EQ2;EQN_DEBUG;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ExceptionHandling>Sync</ExceptionHandling>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@@ -147,7 +147,7 @@
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
       <OmitFramePointers>
       </OmitFramePointers>
-      <MinimalRebuild>true</MinimalRebuild>
+      <MinimalRebuild>false</MinimalRebuild>
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
     </ClCompile>
     <ResourceCompile>
@@ -219,7 +219,7 @@
       <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
       <ShowIncludes>false</ShowIncludes>
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
-      <MinimalRebuild>true</MinimalRebuild>
+      <MinimalRebuild>false</MinimalRebuild>
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
       <PrecompiledHeaderFile>
       </PrecompiledHeaderFile>
@@ -530,6 +530,7 @@
     <ClCompile Include="..\..\source\common\TCPConnection.cpp" />
     <ClCompile Include="..\..\source\common\timer.cpp" />
     <ClCompile Include="..\..\source\common\xmlParser.cpp" />
+    <ClCompile Include="..\..\source\WorldServer\Zone\ChestTrap.cpp" />
     <ClCompile Include="..\..\source\WorldServer\Zone\SPGrid.cpp" />
   </ItemGroup>
   <ItemGroup>
@@ -663,6 +664,7 @@
     <ClInclude Include="..\..\source\common\types.h" />
     <ClInclude Include="..\..\source\common\version.h" />
     <ClInclude Include="..\..\source\common\xmlParser.h" />
+    <ClInclude Include="..\..\source\WorldServer\Zone\ChestTrap.h" />
     <ClInclude Include="..\..\source\WorldServer\Zone\SPGrid.h" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />