Add the following functions prototypes to zone.h
public:
int GetZoneObjectStatus(char);
void AddOpenedZoneObject(char, uint32);
void CloseZoneObject(char);
void CheckZoneObjects();
int GetZoneObjects(char *, ZoneObject_Struct*&

;
ZoneObject *GetZoneObject(char);
private:
int numzoneobjects;
LinkedList<ZoneObject*> ZoneObjects;
LinkedList<ZoneObject*> opened_zone_object_list;
And also add the following include to zone.h
#include "zoneobject.h"
Add the following line to the Zone constructor
numzoneobjects=GetZoneObjectsFromDB();
Add the following function definitions to zone.cpp
int Zone::GetZoneObjectsFromDB() {
    char *query = 0;
	int buf_len = 256;
    int chars = -1;
    MYSQL_RES *result;
    MYSQL_ROW row;
	int numzoneobjects;
	while (chars == -1 || chars >= buf_len)
	{
		if (query != 0)
		{
			delete[] query;
			query = 0;
			buf_len *= 2;
		}
		query = new char[buf_len];
		chars = snprintf(query, buf_len, "SELECT * FROM zoneobjects WHERE zonename='%s'", short_name);
	}
	if (!mysql_query(&database.mysql, query))
	{
		delete[] query;
		result = mysql_store_result(&database.mysql);
		if (result)
		{
			numzoneobjects=int(mysql_num_rows(result));
			for(int i=0; i< numzoneobjects; i++)
			{
				row = mysql_fetch_row(result);
				ZoneObject *newzoneobject=new ZoneObject;
				
				strcpy(newzoneobject->zoneobjectstruct.name, row[1]);
				newzoneobject->zoneobjectstruct.x=atof(row[2]);
				newzoneobject->zoneobjectstruct.y=atof(row[3]);
				newzoneobject->zoneobjectstruct.z=atof(row[4]);
				newzoneobject->zoneobjectstruct.heading=atof(row[5]);
				newzoneobject->zoneobjectstruct.unknown1=atof(row[6]);
				newzoneobject->zoneobjectstruct.unknown2=atof(row[7]);
				newzoneobject->zoneobjectstruct.zoneobject_id=atoi(row[8]);
				newzoneobject->zoneobjectstruct.type=atoi(row[9]);
				newzoneobject->zoneobjectstruct.initialstate=atoi(row[10]);
				newzoneobject->zoneobjectstruct.holdstateforever=atoi(row[11]);
				newzoneobject->zoneobjectstruct.unknown3=atof(row[12]);
				newzoneobject->zoneobjectstruct.unknown4=atof(row[13]);
				newzoneobject->zoneobjectstruct.unknown5=atof(row[14]);
				newzoneobject->zoneobjectstruct.unknown6=atof(row[15]);
				newzoneobject->item_nr=atoi(row[16]);
				newzoneobject->trigger_id=atoi(row[17]);
				newzoneobject->opentimer=atoi(row[18]);
				ZoneObjects.Append(newzoneobject);
			}
			cout << numzoneobjects << " Zone Objects Loaded" << endl;
		}
		mysql_free_result(result);
	}
	else
	{
		cerr << "Error in GetZoneObjects query '" << query << "' " << mysql_error(&database.mysql) << endl;
		delete[] query;
	}
	return numzoneobjects;
}
int Zone::GetZoneObjects(ZoneObject_Struct* &zos)
{
	LinkedListIterator<ZoneObject*> iterator(ZoneObjects);
	zos = new ZoneObject_Struct[numzoneobjects];
	int i=0;
	iterator.Reset();
	while (iterator.MoreElements())
	{
		zos[i]=iterator.GetData()->zoneobjectstruct;
		iterator.Advance();
		i++;
	}
	return numzoneobjects;
}
int Zone::GetZoneObjectStatus(char zoneobject_id)	// Is it open?
{
	LinkedListIterator<ZoneObject*> iterator(opened_zone_object_list);
	iterator.Reset();
	while (iterator.MoreElements())
	{
		if(iterator.GetData()->GetZoneObjectID()==zoneobject_id)
			return 1;
		iterator.Advance();
	}
	return 0;
}
void Zone::CheckZoneObjects()			// If timer has run out, close it
{
	LinkedListIterator<ZoneObject*> iterator(opened_zone_object_list);
	iterator.Reset();
	while (iterator.MoreElements())
	{
		if(iterator.GetData()->timer->Check())
		{
			ZoneObject *pzo;
			pzo=GetZoneObject(iterator.GetData()->GetZoneObjectID());
			if(!pzo->zoneobjectstruct.holdstateforever)
				pzo->zoneobjectstruct.initialstate=0;
			iterator.RemoveCurrent();
		}
		iterator.Advance();
	}
}
ZoneObject *Zone::GetZoneObject(char zoneobject_id)
{
	LinkedListIterator<ZoneObject*> iterator(ZoneObjects);
	iterator.Reset();
	while (iterator.MoreElements())
	{
		if(iterator.GetData()->GetZoneObjectID()==zoneobject_id)
			return iterator.GetData();
		iterator.Advance();
	}
	return 0;
}
void Zone::AddOpenedZoneObject(char zoneobject_id, uint32 opentimer)
{
	ZoneObject *newzoneobject=new ZoneObject;
	newzoneobject->SetZoneObjectID(zoneobject_id);
	newzoneobject->timer=new Timer(opentimer);
	opened_zone_object_list.Append(newzoneobject);
}
void Zone::CloseZoneObject(char zoneobject_id)
{
	LinkedListIterator<ZoneObject*> iterator(opened_zone_object_list);
	iterator.Reset();
	while (iterator.MoreElements())
	{
		if(iterator.GetData()->GetZoneObjectID()==zoneobject_id)
		{
			iterator.RemoveCurrent();
			return;
		}
		iterator.Advance();
	}
}
Add the Following 2 lines to eq_opcodes.h
#define OP_ZoneObjectOpenRequest	0x8d20
#define OP_ZoneObjectOpen			0x8e20
Add the Following structs to eq_packet_structs.h
struct ZoneObject_Struct
{
char    name[16];	// Filename of object? Determines what the object looks like
float   x;
float   y;
float   z;
float   heading;
float   unknown1;
float   unknown2;
char    zoneobject_id;		// uniquely identifies the object within the zone
char	type;				// determines animation (can do cool things with this)
char	initialstate;		// is it initially open,spinniing etc.
char	holdstateforever;	// stay open forever, or return to "closed" after 11 seconds
char	unknown3;
char	unknown4;
char	unknown5;
char	unknown6;
};
struct CompressedZoneObject_Struct
{
uint16 count; // number of objects
struct ZoneObject_Struct *zoneobjects;
};
struct ZoneObjectOpen_Struct
{
	char zoneobject_id;		// id within the zone of the door
	char action;		// 01
	char placeholder[2];
	uint16 item_nr;		// unique item number player is holding(key)
	char placeholder2[2];
	char unknown[2];
	char placeholder3[2];
};