Workaround guide for placing persistent NPC's via Text Edits
Preface
First, this guide is a workaround for the fact that nobody quite has a handle on the proper use of the in-game editor (accessed by typing /can_edit 1 followed by /edit 1 on an account with GM access levels). This workaround isn't intended to be a permanent solution to NPC placement, and may cause unseen issues. For those that are interested in improving this situation, some notes on using the editor are at Map Editing.
Second, this guide assumes that you are running a server setup that can read loose files, as described in [Server Setup for Making Bins|Bubble's guide].
Finding the necessary data
With those points out of the way, let's head to where the data lives, shall we?
We will be using Atlas Park for this example, so open the folder serverdata/maps/CityZones/City_01_01 and you will see that there is a file named "Atlas Park is in this dir.txt". This file is simply there to let you know which zone is defined in this particular folder. Most of the City_number_number folders contain a similar file.
The file that we will actually be working with is named "City_01_01_layer_PersistentNPC.txt". Taking a look at the contents of this file will show you a list of NPC's, each of which looks something like this:
Def grp_Henry_Peter_Wong Group Omni/_Persistent_NPC End Flags Ungroupable Property "PersistentNPC" "Contacts\Level_2\Henry_Peter_Wong.npc" 0 End
That is the initial definition of this persistent NPC, so I'll call it an "initial definition block". It sets flags and properties for the NPC. Note that it does not set the NPC's position or orientation. The block that defines those values is located further down the file, and it looks like this:
Group grp_Henry_Peter_Wong PYR 0 89 -0 Pos 314.499878 175.46051 8.401611 End
I'll call this a "location block" for clarity.
PYR is the orientation (Pitch, Yaw, Roll) of the NPC. As such you'll really only ever see the middle number with a value other than zero, lest the NPC be rotated onto their front/back or left/right sides.
The Pos line is what defines the entity's position on the map. Unfortunately, these coordinates do not quite match up to the in-game coordinates received when using /loc. That's because NPC locations are specified relative to the group containing them, so the coordinates obtained from /loc have an offset baked in. If you scroll back up in the file until you find Laurence Mansfield's location block, you can see the line Def grp_PersistentNPC. That is the start of the block that contains all of the Persistent NPC location blocks.
If you scroll down toward the end of the file, you will see this immediately following James Harvan's location block:
Property "Layer" "PersistentNPC" 0 End
That is the end of grp_PersistentNPC. Immediately afterward, the file ends with the following block, which gives the location for grp_PersistentNPC itself:
Ref grp_PersistentNPC Pos -25.999878 -159.365723 -899.901611 End
I'll refer to this last snippet as the "Ref block".
How to choose coordinates
So if you wanted to put your new NPC entity at a known in-game position obtained using /loc, then for each axis, the coordinate in the location block for the individual entity (E) should be set to the desired /loc coordinate on that axis (L), minus the coordinate from the Ref block (R) on that same axis. In other words, for each axis:
E = L - R
To get to the X-coordinate number in Henry's location block from his in-game X coordinate (L = 288.5), you would subtract the X coordinate in the Ref block (-25.999878). That is, 288.5 - (-25.999878), which simplifies to 288.5 + 25.999878, which equals 314.499878. If you scroll back, you'll see that this matches the first number in his location block. Repeat with the middle Y numbers and then the final Z numbers.
(To go the opposite direction, taking the two values from the "city_01_01_layer_PersistentNPC.txt" file, and obtaining the expected /loc value, you would use L = E + R. 314.499878 + (-25.999878) = 314.499878 - 25.999878 = 288.5
There are a few caveats. First, City of Heroes's coordinate system may give you fits. While increasing Y increases the altitude (as intuition suggests), increasing the X or Z coordinate will move the character west and south respectively (the opposite of what most people would expect).
Second, if you are trying to use the /loc coordinates of an existing entity, it is best if you obtain (or at least double-check) those coordinates in-game or from a demorecord, rather than relying on numbers published on a wiki or forum. Coordinates published on a wiki/forum can be imprecise, and are sometimes entirely wrong in ways that make you question your sanity and/or math skills. (Ask Laurence Mansfield how I know.)
Finally, note that some entities are contained inside multiple nested groups. In the Atlas Park file, you can see that various entities are inside grp_Atmospheric_NPCs and grp_Police_Drones, and then those groups are inside grp_PeresistentNPCs. In these cases, the offset from each of the containers would probably need to be combined.
Practical Example
For our example here, let's make Henry a new (and oddly identical) friend. Copy the initial definition block for the NPC (the first block shown above) and paste it into the City_01_01_layer_PersistentNPC.txt file somewhere near the existing one. Then change the Group name inside to something new (and preferably meaningful). Do the same copy-and-rename for the location block (taking special care to place this second snippet INSIDE the Def grp_PersistentNPC block.)
Lastly, you'll want to change the Pos coordinates of the new location block so that Henry's new friend doesn't spawn directly on top of him (they aren't quite that friendly). Remember, the first number is west/east positioning, the second number controls height, and the third number is south/north positioning.
Your new entries should look something like this:
Def grp_Henry_Peter_Wong_Clone Group Omni/_Persistent_NPC End Flags Ungroupable Property "PersistentNPC" "Contacts\Level_2\Henry_Peter_Wong.npc" 0 End
and
Group grp_Henry_Peter_Wong_Clone PYR 0 89 -0 Pos 314.499878 175.46051 -1.598389 End
The Pos line above will spawn a copy of Henry standing 10 feet to his north. If you wanted the clone to be standing on the roof of City Hall instead, you could try this line (numbers obtained by going in-game and using /loc while standing at the position, then applying E = L - P):
Pos 152.499878 264.865723 124.901611
NPC Behavior and Appearance
But I hear you saying, "What if I don't want a useless army of Clones to awkwardly inform me that maybe somebody else has work for me?"
Well then, that's as simple as pointing your new NPC to a different .npc file. NPC files hold pretty much all of the appearance and behaviors for a given NPC. I'm not going to go into detail on how they work here, as that's somewhat out of the scope of this guide. But here's an example where we set the NPC to a Freedom Corps store NPC:
Def grp_Henry_Peter_Wong_Clone Group Omni/_Persistent_NPC End Flags Ungroupable Property "PersistentNPC" "Contacts\StoreNPCs\FreedomCorp_City_02_02b.npc" 0 End
And, because their behavior is detailed inside the .npc file, this character will actually work as a store! Alright, but what if you want to mark them on the map as being a store? Well this is a pair of propertyies located inside the NPC's Initial Definition block City_01_01_layer_PersistentNPC.txt file, like so:
Def grp_Henry_Peter_Wong_Clone Group Omni/_Persistent_NPC End Flags Ungroupable Property "PersistentNPC" "Contacts\StoreNPCs\FreedomCorp_City_02_02b.npc" 0 Property "miniMapLocation" "StoreString" 0 Property "specialMapIcon" "Store" 0 End
There are a number of other flags and properties available as well, but I'll leave it to you to explore and figure out those on your own (look around at other NPCs). Finally, it is possible to create an NPC that functions as a custom worktable (it's actually how the merit vendors work) but I will be saving that for a future guide as it is fairly involved.