I searched and searched and found people who say they have figured this out but I never could find their actual solution. So I have been working on one. I am almost there. I think I have cracked most of the nuts but wanted to get some input from smarter folks.
So my solution to not making zones static, is to simply spawn ships based on $zonetime. If players are in that zone, great ships spawn and move around. If zone is empty, nothing happens until a player comes in, then the script checks to see where a ship should be and puts it there. If all players leave and the ships are up and now stop moving, no problem we check for that the next time a player comes in zone and we move the ship and restart its new grid.
The work I have done so far seems to work fine. If anyone sees any obvious holes in the scripts that would make this fail, feel free to post a fix. Please go easy on my, my grasp of perl is about as tenuous as my grasp on logic and reality. I wrote some of this under the influence of Modelo as well. But please don't tell me this is just garbage and not offer a solution.
Some basics first:
Server - Akkadius PEQ via installer
Client - Titanium
Expansions - Classic, Kunark, Velious (for now)
Siren's Bane has some issues. Falling through it was the big one right? So in Ocean of Tears we set race = 72, texture = 1, gender =0. This gives us a ship you can walk on (you can still walk through walls and fall through poop deck). But the ship is also gigantic and difficult to maneuver without colliding with the docks and rocks in Butcherblock and East Freeport.
There are a couple solutions. The difficult one is to bring the later expansion ship models into your client everquest directory, add them to your global load and use those models. The easier solution is to use launches to take your players out to the zone line and move them to the waiting ship on the other side.
The easier solution just requires that you globally load the _chr files that have the launch model in them. I was not sure so I added butcher, erudnext, erudxing, firiona, halas, overthere, and timorous. It doesn't eat too much resource, but it would be best to find exactly which one has the launch. I will look later, pretty sure it is erudnext_chr.
So that means modifying your client's Everquest\Resources\GlobalLoad.txt file. For now just append this to the end. Or if it does exist in erudnext_chr like I think, just add that one and forget the others so your player's client doesn't have to load the extras.
1,0,TFFF,butcher_chr,Loading Characters
1,0,TFFF,erudnext_chr,Loading Characters
1,0,TFFF,erudsxing_chr,Loading Characters
1,0,TFFF,firiona_chr,Loading Characters
1,0,TFFF,halas_chr,Loading Characters
1,0,TFFF,overthere_chr,Loading Characters
1,0,TFFF,timorous_chr,Loading Characters
So now that I can use the launch models in East Freeport we simply set up a schedule. I am an old retired Operations Specialist, so I like it when my ship sails on time, so on my server the ship has a schedule and they stick to it. The shuttle from East Freeport leaves the pier at 3:00am (I will use standard time for you sand crabs even though the server uses a 24 hour clock), and 3:00pm. The Sirens Bane receives passengers in Ocean of Tears and sails east at precisely 3:30am and 3:30pm daily. It zones passengers into Butcherblock at around 7:57am and 7:57pm and drops the players onto the launch to the pier which drops them at the dock, waits until precisely 9:00am or pm before transiting back to the zone line to deliver them to the waiting Sirens Bane in Ocean of tears. Sirens Bane will then transit back to East Freeport.
After you modify your GlobalLoad.txt you are ready to set up your boats.
1. Create your zone line controllers
/zone freporte
#goto -2090 -38 -76
#spawn fpecontroller
/targ fpecontroller
#npcspawn create
/zone oot
#goto 10239 1272 -42
#spawn ootfpecontroller
/targ ootfpecontroller
#npcspawn create
#goto -10258 302 -42
#spawn ootbutcontroller
/targ ootbutcontroller
#npcspawn create
/zone butcher
#goto 3683 1611.7 -13
#spawn butcontroller
/targ butcontroller
#npcspawn create
You will need to edit each spawn point that you just created to have them face the direction the ship or shuttle will face when it spawns.
You will need to edit each npc you created to enable Quest Globals. Eventually you will want to change their race to Invisible Man (127) and body type to No Target (11). But while testing it is best to leave them as is.
2. Set up your ships and launches
Butcher - Find the Butcher Block SirensBane in your db. In my install it is 68228. Make sure it is race 73 (launch), Texture 1, and Gender 0. This will set it to look like the Captain's Skiff that takes players out to Maiden's Voyage.
Find the spawngroup for SirensBane and delete any spawn points. Keep the spawn group, just delete the spawn point.
Change run speed to 3.5
Freeport - Zone to East Freeport (freporte) and do the same for SirensBane there (10183). Delete any spawnpoints.
Change run speed to 3.5
Ocean of Tears - In OOT you will want to modify SirensBane (69134) to race 72 (ship), texture 1, gender 0. Again find the spawn group and delete any spawn points.
Change run speed to 3.856. Run speeds will vary depending on your grid length. You can experiment using the information I posted below if you want to lengthen or shorten your round trip times.
Make sure to enable Quest Globals for all of your ships.
3. Create your scripts.
You are going to need a few perl scripts. One for each controller, one for each ship, and one for each harbor master. You can name them after the npcname or after the npcid. I will list them here with npc name to avoid confusion.
Freeport Zone Controller
fpecontroller.pl
Code:
# Zone to Ocean of Tears
# Zone: freporte
# Raghnar
sub EVENT_SPAWN {
$x = $npc->GetX();
$y = $npc->GetY();
#Set proximity
quest::set_proximity($x - 40,$x + 40,$y - 40,$y + 40);
quest::settimer(1,1);
quest::settimer(3,30);
}
sub EVENT_ENTER {
if ($qglobals{"fozoneopen"} == 1 ) {
quest::setglobal("ooteast",1,7,"F"); #let oot side zone know to change race
quest::setglobal("boatrider",1,5,"S60"); #let oot player enter event know to lev player
quest::movepc(69,10239,1272,10,0);
}
elsif ($qglobals{"ootwest"} == 1) { #boat illusion so players are not hovering over water
quest::npcrace(73);
quest::npctexture(1);
quest::npcgender(0);
}
else {
quest::shout("Zone is not open");
}
}
sub EVENT_TIMER {
if ($timer == 1 && $zonetime == 210) {
quest::stoptimer($timer);
quest::settimer(2,3);
quest::spawn(10183,23,0,-2090,-38,-76);
quest::npcrace(127);
quest::setglobal("ofzoneopen",2,7,"F"); #close zone to any players still in oot
quest::setglobal("ootwest",2,7,"F"); #keep extra shuttles from spawning
quest::signalwith(10192,1); #tell harbor master to let folks know where boat is
}
if ($timer == 1 && $zonetime == 1410) {
quest::stoptimer($timer);
quest::settimer(2,3);
quest::spawn(10183,23,0,-2090,-38,-76);
quest::npcrace(127);
quest::setglobal("ofzoneopen",2,7,"F"); #close oot to fpe zone line just in case
quest::setglobal("ootwest",2,7,"F"); #keeps extra shuttles from spawning
quest::signalwith(10192,2); #tell harbor master to let folks know where boat is
}
if ($timer == 1 && $zonetime == 300) { #boat leaves dock at this time
quest::stoptimer($timer);
quest::settimer(2,3);
quest::setglobal("fozoneopen",1,7,"F"); #open zone so players can zone to oot
}
if ($timer == 1 && $zonetime == 1500) { #boat leaves dock at this time
quest::stoptimer($timer);
quest::settimer(2,3);
quest::setglobal("fozoneopen",1,7,"F"); #open zone so players can zone to oot
}
if ($timer == 1 && $zonetime == 330) { #boat has spawned in oot
quest::stoptimer($timer);
quest::settimer(2,3);
quest::setglobal("fozoneopen",2,7,"F"); #close zone to players remaining in fpe
}
if ($timer == 1 && $zonetime == 1530) { #boat has spawned in oot
quest::stoptimer($timer);
quest::settimer(2,3);
quest::setglobal("fozoneopen",2,7,"F"); #close zone to players remaining in fpe
}
if ($timer == 2) {
quest::stoptimer($timer);
quest::settimer(1,1);
}
}
Ocean of Tears to Freeport Zone Controller
ootfpecontroller.pl
Code:
# Zone to East Freeport
# Zone: oot
# Raghnar
sub EVENT_SPAWN {
$x = $npc->GetX();
$y = $npc->GetY();
#Set proximity
quest::set_proximity($x - 150,$x + 150,$y - 150,$y + 150);
quest::settimer(4,1);
quest::settimer(6,1);
}
sub EVENT_ENTER {
if ($qglobals{"ofzoneopen"} == 1) {
quest::setglobal("ootwest",1,7,"F");
quest::setglobal("boatrider",1,5,"S60");
quest::movepc(10,-2090,-38,-55);
}
elsif ($qglobals{"ooteast"} == 1) {
quest::npcrace(72);
quest::npctexture(1);
quest::npcgender(0);
}
else {
quest::shout("Zone is not open");
}
}
sub EVENT_TIMER {
if ($timer == 4 && $zonetime == 330) {
quest::stoptimer($timer);
quest::settimer(5,3);
quest::spawn2(69134,60,0,10239,1272,-42,128);
quest::npcrace(127);
quest::setglobal("fozoneopen",2,7,"F"); #close zone in fpe if players still exist there
quest::setglobal("ooteast",2,7,"F"); #no more race changes for controller
}
if ($timer == 4 && $zonetime == 1530) {
quest::stoptimer($timer);
quest::settimer(5,3);
quest::spawn2(69134,60,0,10239,1272,-42,128);
quest::npcrace(127);
quest::setglobal("fozoneopen",2,7,"F"); #close zone in fpe if players still exist there
quest::setglobal("ooteast",2,7,"F");#no more race changes for controller
}
if ($timer == 4 && $zonetime == 125) { #open zone so players can zone to fpe on boat
quest::stoptimer($timer);
quest::settimer(5,3);
quest::setglobal("ofzoneopen",1,7,"F");
}
if ($timer == 4 && $zonetime == 1325) { #open zone so players can zone to fpe on boat
quest::stoptimer($timer);
quest::settimer(5,3);
quest::setglobal("ofzoneopen",1,7,"F");
}
if ($timer == 4 && $zonetime == 210) { #close the zone to fpe if no players zoned
quest::stoptimer($timer);
quest::settimer(5,3);
quest::setglobal("ofzoneopen",2,7,"F");
}
if ($timer == 4 && $zonetime == 1410) { #close the zone to fpe if no players zoned
quest::stoptimer($timer);
quest::settimer(5,3);
quest::setglobal("ofzoneopen",2,7,"F");
}
if ($timer == 5) {
quest::stoptimer($timer);
quest::settimer(4,1);
}
}
Ocean of Tears to Butcher Zone Controller
ootbutcontroller.pl
Code:
# Zone to Butcherblock Mountains
# Zone: oot
# Raghnar
sub EVENT_SPAWN {
$x = $npc->GetX();
$y = $npc->GetY();
#Set proximity
quest::set_proximity($x - 150,$x + 150,$y - 150,$y + 150);
quest::settimer(7,1);
}
sub EVENT_ENTER {
if ($qglobals{"obzoneopen"} == 1) {
quest::setglobal("buteast",1,7,"F"); #let oot zone know to change race
quest::setglobal("boatrider",1,5,"S60"); #let oot player enter know to lev player
quest::movepc(68,3683,1611.7,14,0);
}
elsif ($qglobals{"ootwest"} == 1) { #boat illusion
quest::npcrace(72);
quest::npctexture(1);
quest::npcgender(0);
}
else {
quest::shout("Zone is not open");
}
}
sub EVENT_TIMER {
if ($timer == 7 && $zonetime == 930) {
quest::stoptimer($timer);
quest::settimer(8,3);
quest::spawn2(69134,61,0,-10258,302,-42,64);
quest::npcrace(127);
quest::setglobal("bozoneopen",2,7,"F"); #close zone in butcher if players still exist there
quest::setglobal("ootwest",2,7,"F"); #no more race changes for controller
}
if ($timer == 7 && $zonetime == 2130){
quest::stoptimer($timer);
quest::settimer(8,3);
quest::spawn2(69134,61,0,-10258,302,-42,64);
quest::npcrace(127);
quest::setglobal("bozoneopen",2,7,"F"); #close zone in butcher if players still exist there
quest::setglobal("ootwest",2,7,"F"); #no more race changes for controller
}
if ($timer == 7 && $zonetime == 715) { #open zone for players to zone to butcher
quest::stoptimer($timer);
quest::settimer(8,3);
quest::setglobal("obzoneopen",1,7,"F");
}
if ($timer == 7 && $zonetime == 1915) { #open zone for players to zone to butcher
quest::stoptimer($timer);
quest::settimer(8,3);
quest::setglobal("obzoneopen",1,7,"F");
}
if ($timer == 7 && $zonetime == 810) { #close the zone to butcher if no players zoned
quest::stoptimer($timer);
quest::settimer(8,3);
quest::setglobal("obzoneopen",2,7,"F");
}
if ($timer == 7 && $zonetime == 2010) { #close the zone to butcher if no players zoned
quest::stoptimer($timer);
quest::settimer(8,3);
quest::setglobal("obzoneopen",2,7,"F");
}
if ($timer == 8) {
quest::stoptimer($timer);
quest::settimer(7,1);
}
}
Butcher to Ocean of Tears Zone Controller
butcontroller.pl
Code:
# Zone to Ocean of Tears
# Zone: butcher
# Raghnar
sub EVENT_SPAWN {
$x = $npc->GetX();
$y = $npc->GetY();
#Set proximity
quest::set_proximity($x - 40,$x + 40,$y - 40,$y + 40);
quest::settimer(9,1);
quest::settimer(11,1);
}
sub EVENT_ENTER {
if ($qglobals{"bozoneopen"} == 1 ) {
quest::setglobal("ootwest",1,7,"F");
quest::setglobal("boatrider",1,5,"S60");
quest::movepc(69,-10258.1,302,20,0);
}
elsif ($qglobals{"buteast"} == 1) {
quest::npcrace(73);
quest::npctexture(1);
quest::npcgender(0);
}
else {
quest::shout("Zone is not open");
}
}
sub EVENT_TIMER {
if ($timer == 9 && $zonetime == 810) {
quest::stoptimer($timer);
quest::settimer(10,3);
quest::spawn(68228,224,0,3683,1611.7,-13);
quest::npcrace(127);
quest::setglobal("obzoneopen",2,7,"F");
quest::setglobal("buteast",2,7,"F"); #keep extra shuttles from spawning
quest::signalwith(68233,1);
}
if ($timer == 9 && $zonetime == 2010) {
quest::stoptimer($timer);
quest::settimer(10,3);
quest::spawn(68228,224,0,3683,1611.7,-13);
quest::npcrace(127);
quest::setglobal("obzoneopen",2,7,"F");
quest::setglobal("buteast",2,7,"F"); #keeps extra shuttles from spawning
quest::signalwith(68233,2);
}
if ($timer == 9 && $zonetime == 900) { #boat leaves dock at this time
quest::stoptimer($timer);
quest::settimer(10,3);
quest::setglobal("bozoneopen",1,7,"F"); #open zone so players can zone to oot
}
if ($timer == 9 && $zonetime == 2100) { #boat leaves dock at this time
quest::stoptimer($timer);
quest::settimer(10,3);
quest::setglobal("bozoneopen",1,7,"F"); #open zone so players can zone to oot
}
if ($timer == 9 && $zonetime == 0930) { #boat spawns in oot
quest::stoptimer($timer);
quest::settimer(10,3);
quest::setglobal("bozoneopen",2,7,"F"); #close zone to players still in butcher
}
if ($timer == 9 && $zonetime == 2130) { #boat spawns in oot
quest::stoptimer($timer);
quest::settimer(10,3);
quest::setglobal("bozoneopen",2,7,"F"); close zone to players still in butcher
}
if ($timer == 10) {
quest::stoptimer($timer);
quest::settimer(9,1);
}
}
Sirens Bane
SirensBane.pl
Code:
sub EVENT_SPAWN {
$x = $npc->GetX();
$y = $npc->GetY();
#Set proximity
quest::set_proximity($x - 40,$x + 40,$y - 40,$y + 40);
}
sub EVENT_EXIT {
quest::delglobal("boatrider");
quest::FlyMode(0);
}
sub EVENT_SIGNAL {
if ($signal == 1) {
quest::depop();
}
}
Player
player.pl
Code:
sub EVENT_ENTERZONE {
if ($qglobals{"boatrider"} == 1) {
quest::FlyMode(2);
}
}
4. Set up your grids.
Create new grids with Wander Type of One Way and Pause Type of Full
Ocean of Tears West to East
x y z h pause
10239 1272 -42 129 0
10014 -636 -42 -1 0
7500 -1000 -42 -1 0
7450.82 -2090.26 -42 128 30
7357 -2644 -42 -1 0
2612.25 -1756.4 -42 -1 0
1152.41 -701.18 -42 -1 0
-6000 -200 -42 -1 0
-8179.47 204.64 -42 -1 0
-9196.82 183 -42 192 30
-9996.37 190.36 -42 -1 0
-10258.1 302.68 -42 -1 0
Ocean of Tears East to West
-10258.1 302.68 -42 64 0
-9996.37 190.36 -42 -1 0
-9196.82 183 -42 64 30
-8179.47 204.64 -42 -1 0
-6000 -200 -42 -1 0
1152.41 -701.18 -42 -1 0
2612.25 -1756.4 -42 -1 0
7357 -2644 -42 -1 0
7450.82 -2090.26 -42 255 30
7500 -1000 -42 -1 0
10014 -636 -42 -1 0
10239 1272 -42 -1 0
These grids will take a ship at speed 3.8566 approximately 4 hours and 25 game minutes to cross from zone in to zone out.
Butcher
1 3683 1611.7 -13 193 63
2 3275.9 1528.6 -13 -1 0
3 3279.6 1353.4 -13 0 63
4 3275.9 1528.6 -13 -1 0
5 3683 1611.7 -13 -1 0
Freeport
1 -2090 -38 -76 64 45
2 -1550 -38 -76 -1 0
3 -980 -38 -76 192 66
4 -1550 -38 -76 -1 0
5 -2098 -38 -76 -1 0
That will make your ships cycle. There are a few bugs yet to work out. When a ship is up in a zone and all players leave the zone, the ship seems to stop. If players return before the shutdown delay kills the zone, the ship will start moving again but it will not be on schedule. I need to write a script to recognize this and depop them. My pre-edit post had an attempt at this, but it was messed up and shut down spawns and zone lines in ways I had not predicted.
I also need to write a script that will check to see if a boat should be up but never spawned because the zone was empty. This script will figure out where the boat should be and where the next logical spawn location is, wait for that time, spawn the boat on a different truncated grid.
I will post those when I get them tested.