What is EQGuessr?

EQGuessr is a fan-made web game in a similiar vein to geoguessr and wowguessr. You are placed in a google streetview-esque location in Everquest and can move around. Once you figure out where you are, you place a pin on a map of EQ and are scored based on how close your guess was to the actual location.

Can I watch you play?

I gotchu:

Why?

EverQuest had such a generational lasting impression. The other day I was playing some random mobile game that had a general chat and people were talking about how they got into games watching their parent play EverQuest over dial-up. You might hop on Twitch and see a very famous streamer playing EQ stating that this is his chill, nostalgia game. Gamers of all ages still remember the drama of families being broken up, or people losing the jobs. While it is not top of mind for most, just about everyone will go Oh! That Game!

The first EQGuessr was a quick one month prototype - I took 300 screenshots by hand, downloaded all of EQAtlas and manually calculated where each location was on the amazing hand drawn maps. The website was maybe 5 pages total, no server, all the data (and answers) were downloaded up front. It could technically run offline if you copied the 300 images.

Reception was insane. Traffic peaked after a week or two but almost hit 100k views:

Web visitor stats showing 90k page hits

I knew I needed to go the distance and make what you are seeing now.

Making Of - Precursor

To make it more like google streetview, where you can move around with the big white arrows, I would need more locations. Like way more. Before I only had 2 locations per zone, maybe 4 in a favorite zone. I would need 300+ for *every zone*. Thinking of the manual labor involved in matching every location to an EQAtlas map was daunting. I need to automate.

How do you automate? Not going to risk my account and do it on live servers. P99 and kin are even stricter. Project EQ EMU though... that is an idea. If I could run my own little server then I can automate to my hearts content. PEQ docs are great and I was able to get it going as a GM character and could teleport around.. but spinning the camera to take screenshots was difficult to automate. At this point I was using AutoHotKey to focus the PEQ window and send keyboard commands -- ie type /teleport 0 0. This was SLOW. I needed something else.

I am sure most of you have seen Youtube videos of EQ in VR, EQ in Unreal 5, EQ on my toaster. That got me looking and there are two tools that were the answer: LanternEQ Extractor and LanternEQ UnityTools.

Making Of - LanternEQ

LanternEQ Extractor will turn the original game files, so you need a copy of the physical disks the games came on, into a 3D model format that can be used in modern apps like Blender, Unreal and Unity.

Obligory pic of my EQ stash:

Picture of my original EverQuest game boxes on a bookshelf

Okay, now I have all the zones, objects, and character model files. What's next? LanternEQ UnityTools. It is a starter Unity project that has scripts to get you going: an EQ shader, lighting, a sky and fog system, and importers that make prefabs of all the pieces together; where it connects a model with its textures and animations.

At this point I have a Unity project with a giant palette of assets that I can drag and drop into the game world. Awesome!

Here is Plane of Time B, no monsters, just the zone prefab plopped into Unity:

Picture of Plane of Time B zone inside the Unity Editor

Making Of - Spawns

No way I am going to drag and drop every monster into every zone. Time to automate! How did PEQ send what to spawn where down to the client? Turns out people have an open source MySQL database of every spawn point! Theres actually two! Project Al kabor also had a MySQL database available.

So I did what any javascript developer would do when presented with a database... turn the tables into json files! Then I wrote a nodejs script that loaded all the json files into memory and combined the relational data into a flat data shape. Reason being is that instead of a zone object that has a list of spawn points ids, then looking up the spawn point id in the spawn point list, but a spawn point only has a list of possible spawn ids, so you need to finding the matching spawn id. Spawn ids have an /location and npc id, and then the npc id has the npc name and texture etc, you see where this is going. I made it into 1 list that had all that info combined.

Part of combining that data had to deal with EQ having spawn chances, so when the spawn timer ends it decides what should spawn (from a list) and those have a percentage chance. So Emperor Crush might have a 10% chance and an Orc Legionniare has 90% chance. I wanted all the named/rare/unique spawns, so my script made decisions on identifying whats an interesting spawn. Rare mob, starts with capital letter, has a low spawn chance, etc.

Now I have a single list of every spawn in a single json file. Now to automate Unity. Thankfully, Unity has a concept of Editor scripts and it was easy to read my json file and create prefab instances of every npc into every zone. There was some hiccups as Unity units are not the same as EQ units, so if the EQ npc should be scaled to be size 6, well thats actually 4.2 in Unity (made up numbers), so a little fudging here and there and now we have monsters in our zones!

Here is Skyshrine, with all the npcs spawned!

Picture of the Skyshrine zone inside the Unity Editor, you can see dragons and cubes

Making Of - Waypoints

Back to trying to make a google street view. The idea I had was to basically place Waypoints that are linked to each other. Then a script would teleport to each Waypoint, take a screenshot, record its location in Unity space, and move on to the next Waypoint. Easy right? Turns out you can create what Unity calls Gizmos: you can visualize an item using basic shapes and they only render in the Editor - when you run the game they are invisible. Wrote a quick script that listened to hotkeys and would insert a new gizmo and connect them.

Enjoy a sped up video of me in Unity dropping the Waypoints for Lower Guk:

At some point I made more helpers - auto align a sphere to the ground, auto connect any waypoints within x units, etc.

Making Of - Screenshots

172 zones. 37,412 waypoints. Next up is one of the last Unity scripts I wrote that traversed all the waypoints and took 360 degree screenshots. Those screenshots totalled to 333GB:

Screenshot of Folder Properties showing 333GB disk

Loading a 4k 360 degree panoramic image all at once is slow for the web, but thankfully the open source photosphere viewer has built in support for splitting the massive image into squares. If you split a 4k image into 256 squares... you end up with a lot of squares. Like a lot lot. Like R.I.P. my SSD. The reduced size was from converting from PNG to WEBP with a tiny bit of compression.

Screenshot of Folder Properties showing over 5 million files

Making Of - World Map

Lets be honest, the map in the first EQGuessr was not great. People expect google maps. So we need to build google maps. To define that expectation: it is a birds eye view of a map aka isometric view, that allows zooming in. Unity can lock the game view to an isometric projection very easily - first part taken care of.

Isometric view of Lake of Ill Omen:

Screenshot of Lake of Ill Omen zone taken from a tall birds eye view

That second part, the zooming in.. that is an interesting concept. Google maps (and open street view, bing, etc) typically offer 22 levels of zoom. Where the first level of zoom is the entire map in a 256px square image. Next level of zoom is making it into x4 256px tiles -- if you think of it in rows and columns its now 2 rows x 2 cols creating in essence a 512x512 image. Zoom level three is double that, 4x4, 16 total tiles, 1024x1024 image. And so on up to zoom 22nd at 536 million pixels square image. Leafletjs has a great overview.

I could not get to that level of detail, but I did get up to 10 zoom levels, ie 131072x131072.

In Unity, I loaded all 172 zone models and all 37k waypoints into one Unity level (scene in Unity jargon) and arranged the zones how I wanted them. My PC at the time was a 4090 and 96GB of DDR5 RAM and it still struggled like crazy to do this.

Called TileMeBaby.cs I wrote an Editor script that would simply move the camera to the needed x/y, adjust the zoom (size in Unity), look straight down and take a 256px screenshot. Adjust the x/y (ie move to the right) and do it again. All said and done, this generated 349,525 tiles. This got me all the tiles needed for 1 level. As we all know though, EQ zones often have multiple levels, how do you handle a dungeon with multiple floors?

Google Maps has layers - road, satelite, terrian - I could do that for EQ! Make three layers to have three versions of the map. Quick adjustment to the TileMeBaby script so that at the start of each layer it actually moves the zone models closer to the camera, to the point where ceilings and top floors would clip through and you would see deeper in. And yes, I sat down and decided on 172 x 2 heights so every zone had an initial height, closer, and then closest. That brings our total map tiles count to 1,048,575!

Making Of - Website

This one is not that interesting, used an open source photosphere viewer, leafletjs for the map, material-ui for some inputs, and slap some CSS paint.

Hosting nearly 6 million images though.. that was much more intense than I thought. S3 bucket for the win, but definitely use a CLI tool that has built in retry to upload all the files. Took my connection two weeks to upload everything.

Last piece is the database as that is the only way to track progress and create private leaderboards. Always wanted to try postgresql so went with that. Stuck with OAuth2 for logins. Easy peasy lemon squeezy. EQGuessr 2.0 done!