Loading...   

[Show Table of Contents]


§What is this System All About?

  • The expedition system that I have built is not typical to that of Live, but offers all of the wants/needs that Live had in regards to Expeditions and it gives you a lot more flexibility
  • This system allows you to take a version of a zone, whether it be the regular zone or an completely custom version of a zone, and make an instance out of it
  • This allows you to make dungeon oriented combat that can be repeatable and grinded just like all modern MMO's
  • Allows you to put a lockout on content for a specified period of time if you wanted to use it for a raid

§Warranty

  • Given the complex nature of this plugin, there may be issues or bugs that arise. I can fix issues given solid data on what is occuring. The good news is that I've given this code a really good spin and ironed out most issues that I've not already thought of ahead of time.
  • This is as is - a wonderful free plugin I've given everyone with 150+ hours spent getting it to the point that it is at. 
  • I plan to make feature expansions over time, but right now this is pretty packed as an initial release

§Expedition Features:

  • Task Synchronization - Will sync task progress for all members in the task if you set it to
  • Enforces Level Requirements
  • Enforces Player Count Requirements
    • ​Enforced minimum player requirement as well as maximum
  • Supports as small as 1 player, to 80+ players
  • Will not allow more than the specified players to travel to the instance
  • Supports Task Lockouts - If lockout is set - players can't do the instance for the specified time frame until the lockout is lifted

§Expedition Commands (Available to players)

  • #expedition leave - Leaves expedition
  • #expedition destroy - If you are leader - will destroy your expedition
    • When destroying expedition, it will force all members inside of the instance to be booted to the NPC or place the expedition was initiated
  • #expedition memberlist - Will get your expedition memberlist in window format
  • #expedition groupinvite - Will invite the group of the playername specified
  • #expedition invite - Will send an invite request to playername specified
  • #expedition makeleader - Makes specified player leader of expedition
  • #expsay - Will send a message to Expedition chat

§Logic

  • When a leader goes offline, a new leader is automagically elected
  • When player level requirements are enforced, an expedition can not be given
  • When a player recieves a lockout for the instance identifier, they cannot get it again 
  • When shared tasks are enabled, and a person within the expedition receives an update, everyone else is queued to see if their task stage matches theirs
    • When a person goes offline and comes back online, their task progress is synchronized (Only when the expedition is active)
    • When a person is zoning, when they enter zone their task progress is synchronized
  • When a person is removed from an expedition, their task that is tied to the expedition will also be removed
  • When a person is invited to an expedition, the task tied to the expedition will also be added
  • All common calls made in Perl leverage the use of comma separated qglobals that contain a bunch of data. This is stored in memory and referenced super fast across zones for scalability
  • When members are removed or added from an instance, other members are notified via quest::crosszonemessageplayerbyname
  • The memberlist window will display time remaining on an expedition

§To Activate/Install

sub EVENT_ENTERZONE {
	ExpdHandler_EVENT_ENTERZONE(); #::: Expedition Enter Zone Handler
}

sub EVENT_POPUPRESPONSE{
	ExpdHandler_EVENT_POPUPRESPONSE(); #::: Expedition system - EVENT_POPUPRESPONSE
}

sub EVENT_SAY{
	ExpdHandler_EVENT_SAY(); #::: Expedition Parser - EVENT_SAY
}

sub EVENT_SIGNAL{
	ExpdHandler_EVENT_SIGNAL();  #::: Expedition system
}

sub EVENT_TASK_COMPLETE{
	ExpdHandler_EVENT_TASK_COMPLETE(); #::: Has to be last in EVENT_TASK_COMPLETE
}
sub EVENT_TASK_UPDATE{
	ExpdHandler_EVENT_TASK_UPDATE(); #::: EVENT_TASK_UPDATE Expedition processing
}

sub EVENT_DISCONNECT{
	ExpdHandler_EVENT_DISCONNECT();
	plugin::DumpPlayerCache(0); #::: Dump Player Info into Cache 
}

sub EVENT_CONNECT{
	plugin::DumpPlayerCache(1); #::: Dump Player Info into Cache
}

§The Database Tables

  • You will need the following custom tables in order for this to work:
    • cust_inst_players - Contains the players and what instances they belong to (Expeditions)
    • cust_ext_instances - Contains general instance info (Expeditions)
    • cust_expd_player_cache - Contains quick table lookup information so the character_ table is not queried. Contains Level, Character Name as well as online/offline status
    • cust_expd_completion_records - Only if you use the completion function in plugins will this table come into any sort of reference
    • cust_ext_hist_lockouts - Contains player lockouts for an expedition (History reference only)
    • cust_ext_lockouts - Contains enforced player lockouts, meaning a player cannot actually get another expedition of a similar 'Namespace' until that expedition has expired
  • Shared tasks info is done completely through multiple complex qglobals. This makes the system very fast since all common data is referenced in quest globals (Memory)
SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for cust_expd_completion_records
-- ----------------------------
DROP TABLE IF EXISTS `cust_expd_completion_records`;
CREATE TABLE `cust_expd_completion_records` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `expedition` varchar(80) DEFAULT NULL,
  `completed_secs` int(11) DEFAULT NULL,
  `completed_mins` int(11) DEFAULT NULL,
  `completion_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1686 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for cust_expd_player_cache
-- ----------------------------
DROP TABLE IF EXISTS `cust_expd_player_cache`;
CREATE TABLE `cust_expd_player_cache` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(75) NOT NULL DEFAULT '',
  `level` int(11) DEFAULT NULL,
  `timelaston` int(11) unsigned DEFAULT NULL,
  `online` int(11) DEFAULT NULL,
  PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for cust_ext_hist_lockouts
-- ----------------------------
DROP TABLE IF EXISTS `cust_ext_hist_lockouts`;
CREATE TABLE `cust_ext_hist_lockouts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `player` varchar(75) NOT NULL DEFAULT '',
  `lockout_name` varchar(75) NOT NULL DEFAULT '',
  `lockout_expire` int(11) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2453 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for cust_ext_instances
-- ----------------------------
DROP TABLE IF EXISTS `cust_ext_instances`;
CREATE TABLE `cust_ext_instances` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `inst_id` int(11) NOT NULL DEFAULT '0',
  `identifier` varchar(100) DEFAULT NULL,
  `type` varchar(25) DEFAULT NULL,
  `limit` int(11) DEFAULT '0',
  `task_assoc` int(11) DEFAULT '0',
  `inst_name` varchar(40) DEFAULT NULL,
  `zonesn` varchar(40) DEFAULT NULL,
  `x` float DEFAULT NULL,
  `y` float DEFAULT NULL,
  `z` float DEFAULT NULL,
  `return_zone` int(11) DEFAULT NULL,
  `return_version` int(11) DEFAULT NULL,
  `return_instanceid` int(11) DEFAULT NULL,
  `return_x` float DEFAULT NULL,
  `return_y` float DEFAULT NULL,
  `return_z` float DEFAULT NULL,
  `heading` float DEFAULT NULL,
  `avglevelreq` int(11) DEFAULT NULL,
  `req_players` int(11) DEFAULT '0',
  `shared_task` int(11) DEFAULT '0',
  `duration` int(11) DEFAULT '0',
  `expdate` int(11) DEFAULT '0',
  `lockout` int(11) DEFAULT '0',
  `boot_on_completion` int(11) DEFAULT '0',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for cust_ext_lockouts
-- ----------------------------
DROP TABLE IF EXISTS `cust_ext_lockouts`;
CREATE TABLE `cust_ext_lockouts` (
  `player` varchar(75) NOT NULL DEFAULT '',
  `lockout_name` varchar(75) NOT NULL DEFAULT '',
  `lockout_expire` int(11) DEFAULT '0',
  PRIMARY KEY (`player`,`lockout_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for cust_inst_players
-- ----------------------------
DROP TABLE IF EXISTS `cust_inst_players`;
CREATE TABLE `cust_inst_players` (
  `inst_id` int(11) NOT NULL DEFAULT '0',
  `char_id` int(11) DEFAULT NULL,
  `player_name` varchar(40) NOT NULL DEFAULT '',
  `pending_invite` int(1) DEFAULT '0',
  `is_leader` int(1) DEFAULT '0',
  `time_invited` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`inst_id`,`player_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

§Using the System

  • Once you have everything in place and installed, the engine is carried by a simple initialization quest
  • Everything you need to create your instance and everything that it enforces happens on plugin::InitInstanceQueue

§Example

sub EVENT_SAY{
        if($text=~/hail/i){
            plugin::DiaWind("{r}Bark! Bark! Grrr~. I remember a time when we envaded {y}Qeynos~ because a {r}crooked guard killed one of our kind~, 
            it caused them to {r}declare war~ and they {lb}completely annihilated our entire clan~. It was not good... [I will investigate> +44+");
        }
        if($text=~/i will investigate/i){
			if($status > 200){ $ReqPlayer = 1; } else { $ReqPlayer = 4; } #::: Testing
			@AdvInfo = plugin::LoadExpeditionInfo2(); plugin::CheckForStaleInstances();
            if($AdvInfo[0] > 0){ plugin::DisplayInstanceQueue($AdvInfo[0]);  }
			else{
				plugin::InitInstanceQueue("Blackburrow Invasion", #Instance Identifier
					"Group", #Description of Adventure (Ex: Group)
					6, #Number of Players
					168, #Task ID Association
					"Blackburrow Invasion", #Description of Adventure
					21600, #Duration Timer (Seconds)
					1, #Version
					"oldblackburrow", #ZoneSN
					24, #Instance Destination X
					-369, #Instance Destination Y
					46, #Instance Destination Z
					0, #Instance Destination H
					190, #Required Average Level
					$ReqPlayer, #Minimum # of Players
					1, #Is Shared Task (0/1)
					1800, #Lockout Duration
					0, ### Boot on completion
					721, # X Compass Loc
					-746, # Y Compass Loc
					206, # Z Compass Loc
				);
			}
        }
    }
}
  • You can initiate an expedition from any event, it could be on proximity walkup of an NPC, it could be clicking a door. As long as you have the same general format that I have listed above you will be solid
  • The moment you hail or trigger plugin::InitInstanceQueue, you will get a popup window that gives you the exact details of the expedition

sub EVENT_SAY{
	if($status > 200){ $ReqPlayer = 1; } else { $ReqPlayer = 4; }
	@AdvInfo = plugin::LoadExpeditionInfo2(); plugin::CheckForStaleInstances();
	if($AdvInfo[0] > 0){ plugin::DisplayInstanceQueue($AdvInfo[0]);  }
	else{
		plugin::InitInstanceQueue("Velketors Assignment", #Instance Identifier
			"Small Raid", #Description of Adventure (Ex: Group)
			24, #Number of Players
			707, #Task ID Association
			"Velketors Assignment", #Description of Adventure
			7200, #Duration Timer (Seconds)
			0, #Version
			"velketor", #ZoneSN
			-65, #Instance Destination X
			581, #Instance Destination Y
			-152, #Instance Destination Z
			191, #Instance Destination H
			50, #Required Average Level
			$ReqPlayer, #Minimum # of Players
			1, #Is Shared Task (0/1)
			86400, #Lockout Duration
			0, #:::  Boot on completion
			17, # X Compass Loc
			79, # Y Compass Loc
			3, # Z Compass Loc
		);
	}
}

 

  • When you hail the NPC again, since you have an adventure you will then trigger plugin::DisplayInstanceQueue, this will bring up the window that will queue you to go into the instance.

  • At this point we've entered a 'Version 0' of Velketor which is our own private instance/expedition
  • If you have the Shared Task set to 1 on the Initialize plugin when you started the expedition, all of your players will be synchronized, it doesn't matter if they are in different zones or not
    • If you have 40+ players in an instance, they will all recieve the update

  • To speed things up, I have updated my task and got a task completion and an Expedition lockout for 2 hours
    • If I had 'Boot on Completion' set to 1, I would have been booted regardless of whether I had things to do in the instance or not
  • If I were to go back to Nexus where the NPC was to give me the task, if I or someone else had a lockout for the identifier 'Velketors Assignment', then I would get a message such as: