Qhimm.com Forums
Miscellaneous Forums => Scripting and Reverse Engineering => Topic started by: codemann8 on 2019-01-20 07:36:12
-
This was meant to be a PM to DLPB, but it seems he has PMs disabled.
I noticed a couple forums he was involved in (ha, I know that narrows it down, right?) involving world map data, so I'm guessing he the's the right person to ask.
So, I'm working on a project that will take a walkthrough and split it into small sections (as it's kinda already done, by different parts of the story) but will basically attach all the field map IDs associated with that part of the walkthru and then display all the enemies that can be encountered during that specific portion. All that is great, but things get a bit trickier when involving the world map. Basically for field maps, I have a "map of maps", linking all the maps to the ones that can be accessed from (by walking off the screen to another). I want to extend this to how field maps will transfer you to the world map (and vice-versa) and also how areas of the world map lead to other areas (ie. walking from Midgar Area Dirt to Midgar Area Grass) and continue my map of maps so it includes world map areas. Another complication is the restrictive nature of some areas, like areas only the buggy can penetrate, but I want to consider those scenarios as my walkthru project will consider GameMoment to determine things like if you have the Buggy or Highwind (can jump from Grass to Grass areas) or Gold Chocobo is possible (after you have Tifa as your main party member, then can jump from any world encounter area to any other, although to cut down on the verbose-ness of the possible enemies you can encounter, it will only show the new ones you couldn't encounter prior). Hopefully you're catching my drift.
A foreword tho: I know that there's a world_us.lgp file for PC, but I do not have this file. I'm working with original PSX files, so if you know where to look there, that would help.
In a nutshell, I'm missing 2 things:
1) Where world map encounter rates/battle IDs are stored on PSX (I'm surprised none of this is on the Wiki, not even the world_us.lgp structure)
2) Where the scripting is located for world map areas, similar to how field maps have scripts to jump to another map when crossing a walkmesh threshold, I assume the world map has similar scripts to change the encounter rates at the point of crossing a threshold.
Hopefully someone can help point me in the right direction. As long as I know I should be working with a specific file, I should be good to get started, I am quite good as analyzing binary data structures. Problem is, I'm spending so much time deciphering files just to realize it's not the right file.
-
I'm not actually well up on the world map module and definitely not on PSX.
I can answer your second question - but only for PC. On PC, the scripting is in world_us in the .ev files. There are 3. One for snowfield, one for overworld, one for underworld (submarine).
The calls to create the windows are there - but the text is in a file called "mes"
As to where the PSX files are - I haven't a clue. Your best bet is Green_Goblin, who ported my translation project to the PSX.
The world map module is by far the least understood... and you're right that it's strange.
-
I did read that wm0 is the overworld, wm1 is underworld, wm2 is the snowfield, however, in the FIELD folder, there are 64 WMx.DAT files (where x is replaced by a number 0-63) and all 64 files have identical contents, seems wasteful to me but also serves to stump me as to what's actually in there. But it's also not the same structure as the other field maps, so it still remains a mystery to me. There's also a WORLD folder which probably is where I'm supposed to be looking.
I do know that the lgp files made for the PC are simply repackaged archives that contain what would show up as multiple files under PSX. Perhaps if I knew more about all the things that CAN be found and what's still a mystery within world_us.lgp, perhaps that could help.
I will also definitely reach out to green_goblin as well.
-
If you do manage to discover where the WM formation data is stored in the PSX file, then try also to find the Yuffie Encounters. They've always alluded me, and I've had to resort to TFerguson's FAQS for that info.
Anyway, field map IDs 1 ~ 64 are all shortcut-jumps to various places on the World Map. Using a placeholder ID for the World Maps as a Field Map means that the devs could easily assign a Field Map ID as the jump, without having to input the x,y,&z coordinates for the WM each time.
01 - WM0 - Midgar Southern Entrance
02 - WM1 - Kalm Entrance
03 - WM2 - Chocobo Ranch Entrance
04 - WM3 - Mythril Mine Entrance Swamp Side
05 - WM4 - Mythril Mine Entrance Junon Side
06 - WM5 - Fort Condor Entrance
07 - WM6 - Junon Entrance
08 - WM7 - Temple of The Ancients Entrance
09 - WM8 - Sleeping Man's Cave Entrance
10 - WM9 - Blacksmith's House Entrance
11 - WM10 - Mideel Entrance
12 - WM11 - Mideel Materia Cave Entrance
13 - WM12 - Costa Del Sol Entrance
14 - WM13 - Mt. Corel Entrance
15 - WM14 - North Corel Entrance
16 - WM15 - Corel Prison Entrance
17 - WM16 - Gongaga Entrance
18 - WM17 - Cosmo Canyon Entrance
19 - WM18 - Nibelheim Southern Entrance
20 - WM19 - Rocket Town Entrance
21 - WM20 - Lucrecia's Waterfall Entrance
22 - WM21 - North Corel Materia Cave Entrance
23 - WM22 - Wutai Entrance
24 - WM23 - Wutai Materia Cave Entrance
25 - WM24 - Bone Village Entrance
26 - WM25 - Corral Valley Entrance Snow Side
27 - WM26 - Icicle Inn Entrance Southern Side
28 - WM27 - Chocobo Sage's House Entrance
29 - WM28 - Round Island Materia Cave Entrance
30 - WM29 - Underwater (Last Coordinates)
31 - WM30 - Underwater Gelnika Entrance
32 - WM31 - Last World Map Coordinates
33 - WM32 - Last World Map Coordinates
34 - WM33 - Last World Map Coordinates
35 - WM34 - Last World Map Coordinates
36 - WM35 - Junon Ship Leaving Junon (Main Event)
37 - WM36 - Junon Ship Arriving at Costa Del Sol
38 - WM37 - Junon Ship Leaving Junon
39 - WM38 - Junon Ship Leaving Costa Del Sol
40 - WM39 - Wutai Shores
41 - WM40 - Highwind Outside Junon (After Escape)
42 - WM41 - Underwater By Junon's Reactor (After Mission)
43 - WM42 - Nibelheim Northern Entrance
44 - WM43 - Mt. Nibel Entrance Nibelheim Side
45 - WM44 - Last World Map Coordinates
46 - WM45 - Mt. Nibel Entrance Rocket Town Side
47 - WM46 - Icicle Inn Entrance Northern Side
48 - WM47 - Great Glacier Entrance
49 - WM48 - Rocket Town North of Entrance
50 - WM49 - Last World Map Coordinates
51 - WM50 - Diamond Weapon Surfaces (Event)
52 - WM51 - Last World Map Coordinates
53 - WM52 - Last World Map Coordinates
54 - WM53 - Highwind Outside Junon
55 - WM54 - Ancient Forest Entrance
56 - WM55 - Underwater (Last Coordinates)
57 - WM56 - Corral Valley Entrance Ravine Side
58 - WM57 - Forgotten Capitol Entrance
59 - WM58 - Highwind Over North Crater
60 - WM59 - Icicle Area Snowstorm Northern Entrance
61 - WM60 - Icicle Area Snowstorm Eastern Entrance
62 - WM61 - Icicle Area Snowstorm Southern Entrance
63 - WM62 - Icicle Area Snowstorm Western Entrance
64 - WM63 - Icicle Area Snowstorm Cavern Entrance
-
I believe all Yuffie encounters are hardcoded into scripts, versus defined in all the other fixed encounter data, but I have yet to find it, this is among the things I'm definitely looking for.
Very interesting about the WM fields, that makes perfect sense, and thank you, that info will help me reverse engineer this. I do still wonder why they needed to have all 64 DAT files in the directory, when they are all the same, probably just a lazy approach. But now it makes me all that more curious as to what's actually in those DATs.
You wouldn't happen to know where those corresponding x,y,z values are stored, do you? My first guess is within those DATS and then the scripts also in the DAT to transport you to the xyz location.
-
Yuffie's will likely be hard coded in PC. And it will be in the executable.
That's where I assume it will be anyway. A lot of the world map stuff is.
-
But PSX doesn't have an executable, just a bunch of assembly/scripts scattered everywhere, right?
-
It does have an executable - the sces scus file etc from what i recall... but who knows where the data was in psx.
-
For future reference, if anyone else is searching for this, regarding the IDs of field maps:
Per Myster6, the field IDs are in order as they are arranged physically on the game disc, although IDs 1-64 are reserved for 64 dummy field maps (wm0, etc) that are used as entry points to the world map (for example, when you leave a city, a "world field map" is used as a placeholder to transport your character to a certain coordinates on the world map, usually on the south side of a city), then ID 65 and on are the rest of the fields.
I can only assume that the PC version defines this somehow thru the flevel.lgp file, albeit with many of the unused fields missing. And I believe ID 0 is the black background dummy field.
-
@DynamixDJ Do you know which TFerg guide mentions Yuffie Encounters? I looked thru all his guides, I don't seem to see any part mentioning Yuffie/Mystery Ninja. Looking at his findings may narrow my search in the end.
-
The flevel names are listed in maplist file inside the lgp. The entry coords to fields from world map are in a table in the exe. The exits are defined in the fields and exit to 1-64 (also probably in a table in exe to define where those tags lead).
Some of the field name handling stuff is hard coded.
-
I could always find the names of fields, it was always the IDs I had a hard time finding. Which is how fields are referenced inside the scripts.
But also, as far as specific coordinates, if I happen to find them, it'd be cool, but that's really just a "nice to have" at this point.
My current goal is to build a full "map of maps" (think graph theory, or Google maps) and be able to divide it into subsections and display the enemies for a general area (specific point in the game) along with attaching sections of a walkthrough to each subsection.
-
For those who like this sort of thing, I created a graph of the maps of maps thing I was talking about. I thought it was interesting to share. Sorry for the overlapping, I didn't have a whole lot of control over the placement of the nodes, but also, there's just a sheer number of entry/exit points everywhere, particularly Gold Saucer (in yellow) and Great Glacier (in blue). The Northern Crater (in green) I thought was interesting cuz you can actually see the left/right path. Also, the black dot is the train station when you first start the game.
Link to larger version (http://ff7.codemann8.com/share/fieldmap-markup.png)
(http://ff7.codemann8.com/share/fieldmap-markup.png)
-
Oooooo I like it very mucho!
One correction, though. Lastmap does not go back to any other map.
-
There may be false positives in the results, as there may be unused scripts. But lastmap must either have a map jump defined in a script that's never invoked or a walkmesh trigger gateway that's impossible to reach.
Anyone have an idea how I can determine if a script will never be run? That would help narrow down on a more truthful graph.
-
Hmm the only way there would be to check the code to see if a call is made. If no call, then script unused.
-
Are field scripts only called from within, or can things invoke from the outside? Is it that every script group calls its init (script 0) and its main (script 1)? Or a subset of the script groups, if so which ones? I'd imagine that ordering matters too, as a map jump could occur before it executes the rest of the scripts. To further complicate things, I also assume things run in parallel too, as multiple models need to animate at the same time.
-
I am soon to actually do a video on this. I saw how the game does it at assembly level. See here:
http://forums.qhimm.com/index.php?topic=15956.msg250079#msg250079
-
One correction, though. Lastmap does not go back to any other map.
There's a scene at las4_0 after the Safer fight and before the final Sephiroth fight.
-
Great post! I think a video would be great! When you say soon, how soon? Lol, reason I ask, is I'm looking to move forward on some things and wondering if it's worth waiting for that so I better understand it before writing logic around it.
So, if I understand this correctly. When a field is loaded, all groups will one by one execute their Script 0 in order sequentially. After all have been run, with each of the IPs (instruction pointer) are set on start of each Script 1 (since Script 0 and 1 are actually the same script just with a RETURN to indicate a pause in execution while the IP increments, clever way to handle that), the Script 1s start executing each group's Line 1 in sequential order, then Line 2, and so on. But every 8 instructions will advance the next frame. But each group will have it's own context (stack and IP). Anything missing or wrong? I'm guessing so since there was mention of if a Script 1 hasn't begun executing on a certain group and there is a call to that same group's Script >1, it will execute the script and then follow it up with Script 1, but how can that be if there is only one IP, or is it technically the caller's IP? If so, how does it handle ASync calls? And also, it seems like that would be an extremely rare case since all Script 1s seem to execute relatively at the same time, the execute call would have to come very early, like the first line.
Also, I'm not sure how models are tied in with groups, and if there's any special rules or things that only apply to models when running scripts.
-
There's a scene at las4_0 after the Safer fight and before the final Sephiroth fight.
That's a point. Though I guess I never saw it that way due to the battle that comes before it. But yeah field would be calling that jump.
-
As for your query... it's much easier to just show you. ;D
The field script doesn't have a stack or threads. It's a very efficient and well designed script-based solution for the field module.
-
Oh, so you're working on a video that soon then? Sweet!
Interesting tho, so without a stack how does it keep track of calls? Or does it just have the callee (is that a word) track it's execution while the caller continues it's own execution? If so, how does it know that Script 1 hasn't executed yet if the IP changes on the callee? Or maybe I'm interchanging the terms stack pointer and frame pointer inappropriately, and it uses a frame pointer? But it sounds like not. Sorry, it's been awhile since I briefly studied low level language in college, although I am a coder by trade.
-
It just advances through script from byte to byte. Each instruction is defined and it can jump back and forward a number of bytes (jump instructions). It keeps track of script execution via a table from what I can see. The field module isn't stack based or assembly based (though obviously the hard code is). It's script based.
The world map module is stack based.
-
I'm looking forward to understanding more after your video. However, as I'm trying to quick get a working method of ignoring unused scripts, I need a little bit more to get started. In MR, the different groups/entities have a "Type" and the scripts seem to have differ, based on the first opcode in Script 0. The pseudocode logic is this:
switch(opcode) {
case Opcode::PC:
type = Model;
_character = charID;
return;
case Opcode::CHAR:
type = Model;
_character = 0xFF;
break;
case Opcode::LINE:
if(_character==-1) type = Location;
return;
case Opcode::BGPDH:
case Opcode::BGSCR:
case Opcode::BGON:
case Opcode::BGOFF:
case Opcode::BGROL:
case Opcode::BGROL2:
case Opcode::BGCLR:
if(_character==-1) type = Animation;
return;
case Opcode::MPNAM:
if(_character==-1) type = Main;
return;
default:
break;
}
And based on what type each group/entity is, the different scripts are given labels:
if (type == Model) {
Script 2 = "S1 - Talk";
Script 3 = "S2 - Contact";
}
if (type = Location) {
Script 2 = "S1 - [OK]";
Script 3 = "S2 - Move";
Script 4 = "S3 - Move";
Script 5 = "S4 - Go";
Script 6 = "S5 - Go 1x";
Script 7 = "S6 - Go away";
}
So, getting to the point: I know the all Script 0s and 1s execute (assuming they don't get shortcutted based on Jumps) on their own and those two scripts can execute other scripts in other entities. However, I'm assuming that the Location type scripts (the ones that create lines in their first opcode) also execute scripts on their own (probably when your character crosses a threshold). My question is, which ones execute specifically? Are there any other scripts that are also executed by other external forces?
I'm basically trying to weed out unused scripts, but first I need to know ALL the ways these scripts can be invoked. If you know this, it would help out a lot.
-
That's only of the type is set. In cases where a "line" instruction has been added, S2-S6 are predefined - as above. I am not sure of the full process there. But normal groups are simply 32 general purpose scripts. Model groups have the talk and contact scripts.
Edit:
As it says above - location and model have a few predefined actions. For location, if you cross a line or move away from a line, for example. These are actioned by hard code.
-
Do you know who I can ask what the specific rules are? Knowing those are going to be essential in my tasks ahead.
-
Myst6re may have an idea but odds are no one does - because testing on that has never been thoroughly done as far as I am aware.
But with lines and models... if you see map jumps on the predefined scripts above, you will have no idea if they are ever called. As those will usually be user initiated. So perhaps use different colour spider diagram line for those.
-
I made a bit of a discovery on my quest to better understand the world map scripts. Let me first explain about some stuff that is already known and in the wiki as a primer to my later-mentioned discoveries. I extracted an EV-type file out of Section 4 of the WM0.TXZ file (as described here (http://wiki.ffrtt.ru/index.php/FF7/World_Map/TXZ)). One first thing to note is that it is definitely NOT a copy of the WM0.EV file as the wiki suggests, but I haven't dug into exactly how or what is different about it, this will probably be something I investigate soon. An EV file contains a call table which is a listing of all the functions/scripts that exist on the world map.
There are 3 types of scripts:
- a system call - which will be called by various game functions, like when the world map is loaded or every frame tick (likely for things like animations of the water)
- a model call - which will be scripts that are called by models, like when a model is loaded onto the map (like a Chocobo or the Highwind, etc)
- a walkmesh call - which is called when your character enters a specific square on the world map
I mostly want to dive into the walkmesh calls, because I've been on a quest to figure out how your character transitions back and forth between the world map and field maps, and I have figured this out.
For each walkmesh call, there is also a set of coordinates attached to it, so it knows to execute only the portion of code that pertains to the area you're in. The coordinates here are NOT the coordinates on the world map, but instead it is the mesh coordinate. The world map is divided into exactly 1,008 equal squares (meshes), 28x36 meshes. So the coordinates here will be 0-27 and 0-35. Not all meshes have a script associated with it, but a few do. We'll come back to coordinates later.
I'm looking specifically for code that deals with jumping to field maps, and it just so happens that there is one opcode that handles this, and many of them are called within these Walkmesh calls, the problem is, the opcode takes in two byte-size parameters, call them parameter A and B. Parameter A is a number that never exceeds 0x40 and the parameter B is either a 0 or 1. Field map IDs range from 0-768, so this exceeds the possible values that could come from these 2 parameters. So, what are these numbers? And how do they eventually reach a field map?
Moving back to the mesh coordinate system, I took the world map image from Black Chocobo and split it into a 28x36 grid. I then took all the Walkmesh calls that contained a field map jump code and put a letter/symbol up on the world map at the coordinate that the call occurs and I came up with this map:
(http://ff7.codemann8.com/share/worldmapBCWalkmeshCoords.png)
The first thing you can notice is that all the spots you see marked are all places we all recognize as points of entry into the various towns on the map. (Note: You'll see that some of them seem to be slightly off, we can definitely attribute this to the world map image from Black Chocobo not being the absolute source of truth, but instead a good guide to point us in the right direction) What I then decided to do was to start with the code that has a parameter A of 1, which is notated on the map as 'M' (Midgar), then the code where parameter A is 2, which is 'J' (Kalm). I made a list and kept going:
- M - Midgar
- J - Kalm
- U - Chocobo Farm
- 4 - Mythril Mine (Marsh side)
- 9 - Mythril Mine (Condor side)
- # - Fort Condor
- Y - Junon
- & - TOTA
- T - Sleeping Man
Notice a pattern? It's the same exact list that DynamixDJ posted earlier in this thread, all 64 (or the ones that exist) match up exactly with the WM* field maps. This is definitely interesting, but still not enough information to land us to how the game gets us to the final destination since the data contained within those WM*.DAT files (which are all identical byte-for-byte) is not enough space to fit all the scripting required to tell the game where to jump to for all 64 cases. Instead, I took note of all the field map names and Field IDs of where these SHOULD be going, then searching the binary to see if I see those same values anywhere. Here's the list I started:
- M - 170 - 0x00AA - Midgar
- J - 335 - 0x014F - Kalm
- U - 343 - 0x0157 - Chocobo Farm
- 4 - 350 - 0x015E - Mythril Mine (Marsh side)
- 9 - 349 - 0x015D - Mythril Mine (Condor side)
- # - 353 - 0x0161 - Fort Condor
- Y - 428 - 0x01AC - Junon
- & - 600 - 0x258 - TOTA
- T - 78 - 0x004E - Sleeping Man
I then found the FIELD.TBL file located within the /WORLD/ folder and it starts like this:
C7 02 8C F6 10 00 AA 00 84 84 84 84 00 00 00 00 00 00 00 00 00 00 00 00
01 FE B2 FC 77 00 4F 01 74 74 74 74 00 00 00 00 00 00 00 00 00 00 00 00
1E 02 E5 FB 0A 00 57 01 98 98 98 98 EF 04 B9 05 4F 00 57 01 CC CC CC CC
6C 02 CA 02 73 00 5E 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
AF 00 08 00 17 00 5D 01 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 DF FD 09 00 61 01 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00
A9 02 9A 02 1D 00 AC 01 CC CC CC CC 00 00 00 00 00 00 00 00 00 00 00 00
FA 01 59 FA 00 00 58 02 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00
02 00 1A 00 03 00 4E 00 80 80 80 80 00 00 00 00 00 00 00 00 00 00 00 00
If you notice, the first line contains 00AA, second line contains 014F, and so on thru the previous list, all of them at offset 0x6. This file is just the map we are looking for! When the opcode for jumping to field maps is called, it uses parameter A as what I call a Field Table Id, which is an ID value associated with records in this file. There are 24 bytes per record. The only thing left is to figure out what the second parameter B is. Most of the time it is 0, but sometimes it is a 1. In the case of the Chocobo Farm, the code looks like this:
Walkmesh(16, 29).FunctionType(4)
IF ((((GetSpecial(0008) == #0000) || (GetSpecial(0008) == #0001)) || (GetSpecial(0008) == #0002))) {
IF (Bank0[007d].Bit(0)) {
EnterFieldScene(#0003, #0001)
GOTO Label 1
}
EnterFieldScene(#0003, #0000)
Label 1:
GOTO Label 2
}
IF ((GetSpecial(0008) == #0013)) {
WRITE Bank0[007e].Bit(0) = #0000
WRITE Bank0[007e].Bit(2) = #0000
WRITE Bank0[007e].Bit(3) = #0000
WRITE Bank0[007e].Bit(4) = #0000
WRITE Bank0[007e].Bit(5) = #0000
WRITE Bank0[007e].Bit(6) = #0000
WRITE Bank0[007e].Bit(1) = #0000
WRITE Bank0[007c] = #0000
IF (Bank0[007d].Bit(0)) {
EnterFieldScene(#0003, #0001)
GOTO Label 2
}
EnterFieldScene(#0003, #0000)
}
Label 2:
RETURN
Most of this you can ignore, but note the two possible ways to jump to the field map, notice one has a parameter B of 0, and the other 1. Parameter B indicates whether an alternate scenario should occur. If we look back at the 3rd record of the FIELD.TBL file, you'll notice that the second half of the bytes aren't zero like some of the other records. That is because these records can be split into 2 12-byte pieces, each containing a potentially different Field ID to jump to, in this case it's the same. What's interesting in the Chocobo Farm case, is that the first time we enter the farm, we enter from the bottom of the screen, while subsequent times we enter from the right side. This FIELD.TBL file is how it handles that. I'm not exactly sure how, but somewhere in the remaining 10 bytes of each scenario are the coordinates that the character should begin on. An example of one area potentially leading to a completely different field map is North Corel. This ONLY happens if you fail to stop the train during your mission there, you instead are transported to a map that has the crashed train in it.
And there you have it, the mystery of transporting from the WM to a field map is solved. And all my findings have already been added to the wiki.
For those curious: I've made a list of all possible field maps that can lead to the world map (left side of the wm field), and all the ways you can get back to a field map (right side of the wm field), sorry for the formatting:
fromMapId name menuText fieldTableId wmField toMapId name menuText
NULL NULL NULL 1 wm0 170 mds5_5 Slum Outskirts
170 mds5_5 Slum Outskirts 1 wm0 170 mds5_5 Slum Outskirts
335 elm Kalm 2 wm1 335 elm Kalm
345 frcyo Chocobo Ranch 3 wm2 343 farm Chocobo farm
343 farm Chocobo farm 3 wm2 343 farm Chocobo farm
350 psdun_2 Mythril Mine 4 wm3 350 psdun_2 Mythril Mine
349 psdun_1 Mythril Mine 5 wm4 349 psdun_1 Mythril Mine
353 condor1 Base of Fort Condor 6 wm5 353 condor1 Base of Fort Condor
370 junonl1 Lower Junon 7 wm6 428 ujunon1 Under Junon
428 ujunon1 Under Junon 7 wm6 428 ujunon1 Under Junon
600 jtempl Temple of the Ancients 8 wm7 600 jtempl Temple of the Ancients
78 zz1 Old man's house 9 wm8 78 zz1 Old man's house
79 zz2 Weapon seller A wm9 79 zz2 Weapon seller
712 itown1a Mideel B wm10 712 itown1a Mideel
712 itown1a Mideel B wm10 714 itown1b Mideel
713 itown12 Mideel B wm10 712 itown1a Mideel
713 itown12 Mideel B wm10 714 itown1b Mideel
714 itown1b Mideel B wm10 712 itown1a Mideel
714 itown1b Mideel B wm10 714 itown1b Mideel
84 zz7 Materia Cave C wm11 84 zz7 Materia Cave
443 del2 Costa del Sol D wm12 443 del2 Costa del Sol
458 mtcrl_0 Mt. Corel E wm13 458 mtcrl_0 Mt. Corel
450 ncorel North Corel F wm14 450 ncorel North Corel
450 ncorel North Corel F wm14 451 ncorel2 North Corel
451 ncorel2 North Corel F wm14 450 ncorel North Corel
451 ncorel2 North Corel F wm14 451 ncorel2 North Corel
452 ncorel3 North Corel F wm14 450 ncorel North Corel
452 ncorel3 North Corel F wm14 451 ncorel2 North Corel
481 desert1 10 wm15 482 desert2 Corel Desert
482 desert2 Corel Desert 10 wm15 482 desert2 Corel Desert
512 crcin_2 Waiting Room 10 wm15 482 desert2 Corel Desert
518 gongaga Gongaga Village 11 wm16 515 gonjun2 Jungle
514 gonjun1 Jungle 11 wm16 515 gonjun2 Jungle
515 gonjun2 Jungle 11 wm16 515 gonjun2 Jungle
525 cos_btm Cosmo Canyon 12 wm17 525 cos_btm Cosmo Canyon
284 nivl_3 Nibelheim 13 wm18 284 nivl_3 Nibelheim
285 nivl_4 13 wm18 284 nivl_3 Nibelheim
551 rckt2 Rocket Town 14 wm19 551 rckt2 Rocket Town
551 rckt2 Rocket Town 14 wm19 557 rckt Rocket Town
557 rckt Rocket Town 14 wm19 551 rckt2 Rocket Town
557 rckt Rocket Town 14 wm19 557 rckt Rocket Town
81 zz4 Lucrecia's Cave 15 wm20 81 zz4 Lucrecia's Cave
83 zz6 Materia Cave 16 wm21 83 zz6 Materia Cave
574 yougan3 Plains 17 wm22 574 yougan3 Plains
574 yougan3 Plains 17 wm22 579 uutai1 Wutai
581 yufy1 Yuffie's House 17 wm22 574 yougan3 Plains
581 yufy1 Yuffie's House 17 wm22 579 uutai1 Wutai
579 uutai1 Wutai 17 wm22 574 yougan3 Plains
579 uutai1 Wutai 17 wm22 579 uutai1 Wutai
82 zz5 Materia Cave 18 wm23 82 zz5 Materia Cave
617 bonevil Bone Village 19 wm24 617 bonevil Bone Village
629 sandun_2 Corral Valley Cave 1A wm25 629 sandun_2 Corral Valley Cave
654 snow Icicle Inn 1B wm26 654 snow Icicle Inn
80 zz3 Mystery House 1C wm27 80 zz3 Mystery House
85 zz8 Materia Cave 1D wm28 85 zz8 Materia Cave
NULL NULL NULL 1E wm29 425 semkin_5 Underwater Reactor
88 q_1 Hallway 1F wm30 88 q_1 Hallway
348 sichi Marshes 20 wm31 348 sichi Marshes
573 yougan2 Plains 21 wm32 573 yougan2 Plains
572 yougan Wilderness 22 wm33 572 yougan Wilderness
NULL NULL NULL 23 wm34 572 yougan Wilderness
382 jundoc1a Junon Dock 24 wm35 439 shpin_2 Cargo Ship
440 shpin_3 Cargo Ship 25 wm36 441 del1 Costa del Sol Harbor
382 jundoc1a Junon Dock 26 wm37 441 del1 Costa del Sol Harbor
429 ujunon2 Dolphin Offing 26 wm37 441 del1 Costa del Sol Harbor
441 del1 Costa del Sol Harbor 27 wm38 382 jundoc1a Junon Dock
86 sea 28 wm39 NULL NULL NULL
70 fship_23 Highwind 29 wm40 NULL NULL NULL
406 subin_1b Submarine Bridge 2A wm41 NULL NULL NULL
284 nivl_3 Nibelheim 2B wm42 284 nivl_3 Nibelheim
285 nivl_4 2B wm42 284 nivl_3 Nibelheim
311 mtnvl2 Mt. Nibel 2C wm43 311 mtnvl2 Mt. Nibel
70 fship_23 Highwind 2D wm44 NULL NULL NULL
71 fship_24 Bridge 2D wm44 NULL NULL NULL
72 fship_25 Highwind 2D wm44 NULL NULL NULL
313 mtnvl4 Mt. Nibel 2E wm45 313 mtnvl4 Mt. Nibel
NULL NULL NULL 2F wm46 654 snow Icicle Inn
658 hyou1 30 wm47 658 hyou1
662 icedun_2 Frostbite Cave 30 wm47 658 hyou1
NULL NULL NULL 31 wm48 NULL NULL NULL
NULL NULL NULL 32 wm49 70 fship_23 Highwind
NULL NULL NULL 32 wm49 72 fship_25 Highwind
NULL NULL NULL 33 wm50 72 fship_25 Highwind
NULL NULL NULL 34 wm51 71 fship_24 Bridge
NULL NULL NULL 35 wm52 347 fr_e
405 subin_1a 36 wm53 NULL NULL NULL
622 anfrst_3 Ancient Forest 37 wm54 622 anfrst_3 Ancient Forest
624 anfrst_5 Ancient Forest 37 wm54 622 anfrst_3 Ancient Forest
405 subin_1a 38 wm55 405 subin_1a
405 subin_1a 38 wm55 406 subin_1b Submarine Bridge
406 subin_1b Submarine Bridge 38 wm55 405 subin_1a
406 subin_1b Submarine Bridge 38 wm55 406 subin_1b Submarine Bridge
626 sango2 Corral Valley 39 wm56 626 sango2 Corral Valley
630 lost1 Forgotten Capital 3A wm57 630 lost1 Forgotten Capital
NULL NULL NULL 3B wm58 744 las0_1 Highwind, On deck
686 gaiafoot Base of Gaea's Cliff 3C wm59 686 gaiafoot Base of Gaea's Cliff
670 move_s 3D wm60 670 move_s
670 move_s 3E wm61 670 move_s
670 move_s 3F wm62 670 move_s
682 hyou12 40 wm63 682 hyou12
Another discovery I found was that of the Model calls (as described near the top of this post), some of the Model IDs are already known, such as Cloud being ID = 0, Tifa = 1, Cid = 2, Highwind = 11, etc. Basically all 3D models used on the WM have an ID. Previously ID 8 was unknown, but I found that to be the Cargo Ship as there is a Model script for ID 8 that deals with all 4 field map jumps pertaining to the Cargo Ship transports.
Thru similar reverse engineering, we can see that wm52 and fr_e are used for the Diamond Weapon battle and the dialogue that follows. This also illuminates another unknown Model ID, which Diamond Weapon is ID 10.
Another area of interest is wm32 is where Yuffie Encounters end up after the battle. wm34 is where Yuffie betrays your team on the southern part of the western continent.
What I don't know is how the game knows which coordinates on the world map to drop the character when entering the WM from a particular town. For the sake of my current project, finding that is out of scope for now, but possibly something I may dive into later.
-
So is it possible to edit the position on the wm when you get out front town? I have a mod that changes kalm location on the worldmap. To enter to kalm is fine but when you get out the character appears at the coordinates that kalm should be
-
Ah - yes.... Sorry about that. I spotted the table file some time ago!
Nice work, btw.
What I don't know is how the game knows which coordinates on the world map to drop the character when entering the WM from a particular town
It will likely be hard coded it not in that table file. Best bet try searching for the X Y Z value from world map. You may have to divide by 4096.
-
Right, and my problem is right now that I'm exclusively dealing with PSX at this point. My Steam account has issues currently and won't let me make purchases on it, so I can't get Steam FF7 atm. So if I wanted to get actual coordinates of the drop points for each town, I'd be a huge process and hassle that I'm not willing to do atm.
You're correct tho, the WM coordinates are definitely NOT in the FIELD.TBL file, only the field coordinates are.
-
Hi,
I'm not sure if that will help but here is the reversed code (in C) to the World module of the windows version of FF7: https://github.com/ergonomy-joe/ff7-worldmap
-
More cool stuff. I created a rather large image of the world map walkmesh wireframe for your viewing pleasure:
I should warn you, it's over 5MB
Overworld (http://ff7.codemann8.com/share/worldMapWireMesh.png)
Underwater (http://ff7.codemann8.com/share/worldMapWireMesh2.png)
-
Another world map image. I filled in all the triangles with color pertaining to what kind of walkmesh it is. Perhaps someday I'll actually apply the real texture, but for now that's the best I can do.
(http://ff7.codemann8.com/share/worldMap.png)
-
Does anyone have any detail on the structure of the world.bin file?
-
I found a bit more. Thanks to both ergonomy_joe and JBedford. I found the enc_w.bin file within world.bin (after decompressed: offset 0x1D948 size:0x8A0 header:0xA0), which is the file that contains all the encounters on the world map (probability and enemy formations). The enc_w.bin file contains 64 possible configurations for encounters. These are divided into 16 different areas on the map (Midgar Area, Nibel Area, etc) indicated by the text that shows up in the MENU. The text for these 16 areas are first 16 texts that are defined in a file called "mes", which is another file that exists within world.bin (offset:0x1E5F0 size:0x1000) This leaves 4 possible configurations per area, which are divided amongst some of the different terrain types that exist within that area, also known as walkmesh statuses, listed at the bottom of this page (http://wiki.ffrtt.ru/index.php/FF7/WorldMap_Module#Walkmap) in the Wiki. If you look at the world map walkmesh image I posted earlier (the Overworld link), you'll see a bunch of small triangles, each individual triangle has a walkmesh status associated with it.
This enc_w.bin is not a new file to us, it has been deciphered before, even with notes indicating which walkmesh statuses are the ones that are used for encounters. This, however, can be found by simple trial and error and manually writing down this information while playing and checking against the available encounter configurations.
I, however, was looking to find how the game knows which walkmesh statuses are valid ones to use and look up in the configurations. I thought maybe this was information that was in the 0xA0 header of the enc_w.bin file, but that lead to a dead end. I sent a PM over to JBedford, who told me this had to be determined within the EXE, which for me is the last area of my expertise. From there, I had a look thru the world map code that was reverse engineered by ergonomy_joe and did indeed find a table (inside his NEWFF7/C_00766B70.cpp) that contained some similar-looking numbers which looked a lot like the common walkmesh status IDs that encounters use. Since joe's code is based on the PC version of the game, I wanted to find where it could be located in the PSX version. Since the walkmesh statuses could've been stored a good number of ways (they only take up 5 bits), this would've been very difficult to find on my own, but with joe's code, I could see how the game stored these values, which was in 8-bit segments (leaving 3 unused). I searched and found it in the PSX version, again in the world.bin file (offset: 0x272B4 size:0x40)
Here is the list, divided in groups of 4 (each area):
00 09 00 11 Midgar Area
00 00 00 11 Grasslands Area
00 09 01 11 Junon Area
00 14 00 11 Corel Area
00 09 08 11 Gold Saucer Area
00 00 19 11 Gongaga Area
00 09 13 11 Cosmo Area
00 00 01 11 Nibel Area
00 00 01 11 Rocket Launch Pad Area
00 09 0E 11 Wutai Area
00 09 19 11 Woodlands Area
00 0A 09 11 Icicle Area
00 09 19 11 Mideel Area
00 00 08 0B North Corel Area
00 00 08 00 Cactus Island
00 00 01 00 Goblin Island
One thing to note, every area has a Grass status configuration, which is a walkmesh status ID of 00. If you look at every area, they all begin with 00. So the first configuration in each area in enc_w.bin shows you the enemies you'll encounter in the Grass parts. However, you'll notice that some of the areas contain multiple references to Grass statuses. I believe the game only uses the first one it finds, and effectively marks any subsequent ones as "unused". It's unclear, as I haven't tested it, if the game REQUIRES each area to have a Grass status or not. Either way, if the enc_w.bin Grass configuration for one area is all 00s, that effectively disables encounters for the area anyways. My thought though is that you could have an area with 4 statuses that AREN'T a Grass status and it would work, but not tested.
Another important thing to note is the Icicle Area, the 3rd status referenced is ID 09, which is a Dirt/Wasteland status. You'll notice there is no dirt area in the Icicle Area...or is there? This is the configuration that is used for encounters in the snowfield area in Great Glacier! Even though it looks like snow, that's just because a different texture ID is used instead of the usual Dirt texture. They couldn't use the Snow status there because it's already used by the Snow area on the main world map.
So, here is the list of the statuses that are used in each area:
00 09 00 11 Midgar Area (Grass, Dirt, <unused>, Beach)
00 00 00 11 Grasslands Area (Grass, <unused>, <unused>, Beach)
00 09 01 11 Junon Area (Grass, Dirt, Forest, Beach)
00 14 00 11 Corel Area (Grass, Mountain Pass, <unused>, Beach)
00 09 08 11 Gold Saucer Area (Grass, Dirt, Desert, Beach)
00 00 19 11 Gongaga Area (Grass, <unused>, Jungle, Beach)
00 09 13 11 Cosmo Area (Grass, Dirt, Canyon, Beach)
00 00 01 11 Nibel Area (Grass, <unused>, Forest, Beach)
00 00 01 11 Rocket Launch Pad Area (Grass, <unused>, Forest, Beach)
00 09 0E 11 Wutai Area (Grass, Dirt, Wutai Bridge, Beach)
00 09 19 11 Woodlands Area (Grass, Dirt, Jungle, Beach)
00 0A 09 11 Icicle Area (Grass, Snow, Snowfield (Dirt), Beach)
00 09 19 11 Mideel Area (Grass, Dirt, Jungle, Beach)
00 00 08 0B North Corel Area (Grass, <unused>, Desert, Riverside)
00 00 08 00 Cactus Island (Grass, <unused>, Desert, <unused>)
00 00 01 00 Goblin Island (Grass, <unused>, Forest, <unused>)
-
OK, coords set from field to world map are stupid. Really stupid and probably a remnant of porting from psx badly (I think it's just a calc to make use of 24 bit data).
The coords are not stored as a 3 or 4 byte integer... instead, they are stored in two separate two byte values and then operations are done to make the final 3 byte integer
Let's take Kalm, for example. When exiting Kalm, Cloud will appear at X coord value decimal 201868
This is arrived at by the two byte value 0x18 and 0x13D6
0x18 is multiplied by 0x2000 to create 0x30000
And then the two byte value 0x13D6 is added to this to create 0x313D6 (decimal 201868).
See the following data at 00DF5C28 [You can find this from 0x764D24]. X is in red. Y is in blue.
18 00 10 01 0D 00 08 03 00 01 10 01 D6 13 10 01 20 19
Z and character direction must close by (Z may be being calculated based on X and Y). Z is signed, so watch out for that.
This data is written to from elsewhere (and my debugger won't tell me where). Likely this data is raw in one of the world_us files.
Search for the above byte order in a file and you should find it. IN the meantime, I am going to continue looking into this.
Edit.
wm0.ev (event file in world_us). 0x7e0. This is where the data is stored. Of course... being that you'll need to use my tool Ochu to get the coords (by leaving each town) and then do the math on each coord and look it up in the event file, it's going to be annoying.
-
As to your question about encounters, I have added the bridge battles back some time ago (for example) for R06.
It does work on IDs as you have spotted, but the ID also has a "can battle occur here" flag attached to it. See my own code here as to how I added wutai bridge battles back:
Procedure WorldMapTerrainBattleFlag7621C0; stdcall; // Only if result equals 0, allow world map battles.
Begin
asm
mov ecx,[$E3A7D0]
cmp ecx,00
je @Label1 // unknown check
movsx ecx,word ptr [ecx+$4A] // Terrain ID
cmp ecx, $122E
je @Label1 // if terrain is Wutai Bridge, allow battles
sar ecx,05
and ecx,07
mov eax,ecx // eax now contains terrain battle flag
jmp @End
@LABEL1:
xor eax,eax // This will also allow battles
@END:
end;
End;
The game also determines if you are able to use menu by the same principle.
-
This is where the data is stored. Of course... being that you'll need to use my tool Ochu to get the coords (by leaving each town) and then do the math on each coord and look it up in the event file, it's going to be annoying.
No worries, I have my own tool that sifts thru WM scripts, but really I've already enlisted some help in finding the first three WM coordinates, from there I should be able to spot a pattern. The way I have my code set up, it's easy to extract data from these scripts and feed them into my database.
-
Ok, now that I had a chance to sit down and unfold everything DLPB noted; I figured this out. That was my initial thought, that these coordinates were defined and executed using WM scripts, however, I think there was some comment somewhere that led me away from searching WM scripts so I eliminated that from my search.
But here it is:
Apparently, and I don't know how THIS part is managed, but there are certain variables that the game maintains for various functionality throughout the WM, called Special Variables. There are 20 possible variables that can be referenced, most of which are unknown, per the Wiki. One of these variables stores the field ID that jumped to the WM; this is the "wm" field map, not the field map that jumps to the "wm" field. This variable was previously unknown on the Wiki, but I have found it to be ID 6, this will be explained later. There, then, is an opcode that loads up that value and uses it to determine IF and WHERE any coordinate changes should occur. This check happens every time the WM is loaded. That's half of what's needed, but important to note.
The other half deals with the actual coordinates that it should move your active entity to. As DLPB alluded to earlier, it handles this in a rather funky way, but there is a method to it's madness, it actually relates to that world map coordinate image I posted earlier. DLPB referred to 2 values which are in the EV file (which is where the WM scripts are located), 0x18 and 0x13D6. 0x18 first needs to be multiplied by 0x2000 to get 0x30000 and then added to 0x13D6, coming out to be 0x313D6 or 201686, which is the final destination X coordinate. DLPB is correct in that they AREN'T stored in 3 or 4-byte chunks, that is because they can't. In the WM scripts, it is stack-based and the entire WM script file is stored and referenced in increments of 2-bytes, no more and no less. So, the game designers could've just dealt with status quo and have the WM utilize either a smaller dimension space, or drastically reduce the image quality of the WM imaging. The workaround was to split the WM into smaller chunks (which we know as meshes) which are previously known as our X and Z coordinate system described in my earlier post with the WM coordinate image. The 0x18 value, or 24 in decimal, is actually the mesh X coordinate, and it is to be multiplied by 0x2000 because there are 0x2000 pixels per each mesh. If the entire WM would be restricted to 2-byte coordinates, the entire map would have to fit within an 8x8 section (about the size of the eastern continent). The WM could theoretically be much much larger (aka better quality), however, there are likely limitations outside stuff that can be referenced inside WM scripts, but definitely something that could be looked into to create a WM HD Mod. Getting back to DLPB's coordinates, the second number is the number of pixels within that mesh that the active entity should move to. Putting them together, the 0x18 number will tell the game which mesh it should load (along with some of the surrounding meshes), then the 0x13D6 number tells which pixel location to put the character.
Here's the section of the WM script that pertains the part that transports to world coordinates:
00 01 'CLEARs the Stack
1B 01 06 00 'Loads SPECIAL Variable 6 value to the Stack
10 01 02 00 'Adds CONSTANT value of 2 to the Stack (this is Field ID of 2, aka wm1/Kalm)
70 00 'Consumes the 2 previous Stack values, adds boolean value (0 or 1) to the Stack if the 2 values are EQUAL
01 02 00 02 'JUMPs ahead to the next Field ID check (ID 3 @ 0x200 in the WM script) if the previous operation was false (if not equal)
00 01 'CLEARs the Stack
10 01 18 00 'Adds CONSTANT value of 24 to the Stack (this is the mesh X coordinate)
10 01 0D 00 'Adds CONSTANT value of 13 to the Stack (this is the mesh Z coordinate)
08 03 'Consumes the 2 previous Stack values, then SETs the ACTIVE ENTITY's MESH (loads the mesh, and surrounding meshes)
00 01 'CLEARs the Stack
10 01 D6 13 'Adds CONSTANT value of 5078 to the Stack (this is the X coordinate within the mesh)
10 01 20 19 'Adds CONSTANT value of 6432 to the Stack (this is the Z coordinate within the mesh)
09 03 'Consumes the 2 previous Stack values, then SETs the ACTIVE ENTITY's COORDINATES (puts character at coordinates within the current mesh)
00 01 'CLEARs the Stack
10 01 00 00 'Adds CONSTANT value of 0 to the Stack (value 0-255: this is the direction the character is facing, 0 is south)
04 03 'Consumes the previous Stack value, then SETs the ACTIVE ENTITY's DIRECTION (sets the character's direction that it's facing)
00 02 3D 08 'JUMPs ahead to section of the script that is after all the Special Variable Field ID checks (@0x83D)
So, to modify any of these is as simple as tweaking those specific values within the WM scripts, System call On WM Load (This is function type 0, function ID 0, usually the first "valid" entry in the call table). The actual part of the script that pertains to world coordinate jumping is at 0x364 byte offset relative to the start of the function (0x398 from the start of the entire script section, 0x798 from the start of the entire EV file)
The Kalm example is the most simple example, sometimes some of these are conditional and can do to different coordinates. Here is a copy of some pseudocode of the WM script as it pertains to transitioning from field map to world map, I got this from DynamixDJ, who got it from someone else. Some of this I know and can fill in the blanks for, but there's just too much to do, but it should be easy to see the parts requiring modifying, it will take someone a little work to map the below logic to the binary EV file:
01d2 IF ((GetSpecial(0006) == #0001)) {
01d9 ActiveEntity.SetMeshCoordsXZ(#0016, #000f)
01df ActiveEntity.SetCoordsInMeshXZ(#1524, #01bd)
01e3 ActiveEntity.SetDirectionAndFacing(#0000)
01e4 GOTO 083d
}
01ec IF ((GetSpecial(0006) == #0002)) {
01f3 ActiveEntity.SetMeshCoordsXZ(#0018, #000d)
01f9 ActiveEntity.SetCoordsInMeshXZ(#13d6, #1920)
01fd ActiveEntity.SetDirectionAndFacing(#0000)
01fe GOTO 083d
}
0206 IF ((GetSpecial(0006) == #0003)) {
020b IF (Bank0[007e].Bit(1)) {
0210 IF (Bank0[007e].Bit(0)) {
0215 LoadAndInitModel(#0004)
0216 EnterVehicle()
}
021a IF (Bank0[007e].Bit(2)) {
021f LoadAndInitModel(#0013)
0220 EnterVehicle()
}
0224 IF (Bank0[007e].Bit(3)) {
0229 LoadAndInitModel(#0013)
022a EnterVehicle()
}
022e IF (Bank0[007e].Bit(4)) {
0233 LoadAndInitModel(#0013)
0234 EnterVehicle()
}
0238 IF (Bank0[007e].Bit(5)) {
023d LoadAndInitModel(#0013)
023e EnterVehicle()
}
0242 IF (Bank0[007e].Bit(6)) {
0247 LoadAndInitModel(#0013)
0248 EnterVehicle()
}
}
024e ActiveEntity.SetMeshCoordsXZ(#001d, #0010)
0254 ActiveEntity.SetCoordsInMeshXZ(#0712, #1d23)
0258 ActiveEntity.SetDirectionAndFacing(#0000)
0259 GOTO 083d
}
0261 IF ((GetSpecial(0006) == #0004)) {
0268 ActiveEntity.SetMeshCoordsXZ(#001a, #0012)
026e ActiveEntity.SetCoordsInMeshXZ(#073d, #15ba)
0272 ActiveEntity.SetDirectionAndFacing(#0060)
0273 GOTO 083d
}
027b IF ((GetSpecial(0006) == #0005)) {
0282 ActiveEntity.SetMeshCoordsXZ(#0019, #0013)
0288 ActiveEntity.SetCoordsInMeshXZ(#0fdd, #095c)
028c ActiveEntity.SetDirectionAndFacing(#00e4)
028d GOTO 083d
}
0295 IF ((GetSpecial(0006) == #0006)) {
029c ActiveEntity.SetMeshCoordsXZ(#0018, #0014)
02a2 ActiveEntity.SetCoordsInMeshXZ(#1a1d, #19ff)
02a6 ActiveEntity.SetDirectionAndFacing(#0000)
02a7 GOTO 083d
}
02af IF ((GetSpecial(0006) == #0007)) {
02b4 IF (Bank0[007f].Bit(0)) {
02b9 IF (Bank0[007f].Bit(1)) {
02be LoadAndInitModel(#0006)
02bf EnterVehicle()
}
}
02c5 ActiveEntity.SetMeshCoordsXZ(#0014, #0011)
02cb ActiveEntity.SetCoordsInMeshXZ(#175d, #1e0e)
02cf ActiveEntity.SetDirectionAndFacing(#0000)
02d0 GOTO 083d
}
02d8 IF ((GetSpecial(0006) == #0008)) {
02df ActiveEntity.SetMeshCoordsXZ(#0014, #0016)
02e5 ActiveEntity.SetCoordsInMeshXZ(#1814, #0d4d)
02e9 ActiveEntity.SetDirectionAndFacing(#0000)
02ea GOTO 083d
}
02f2 IF ((GetSpecial(0006) == #0009)) {
02f9 ActiveEntity.SetMeshCoordsXZ(#0018, #0010)
02ff ActiveEntity.SetCoordsInMeshXZ(#0a8b, #09d1)
0303 ActiveEntity.SetDirectionAndFacing(#0038)
0304 GOTO 083d
}
030c IF ((GetSpecial(0006) == #000a)) {
0313 ActiveEntity.SetMeshCoordsXZ(#000f, #0015)
0319 ActiveEntity.SetCoordsInMeshXZ(#1908, #07f1)
031d ActiveEntity.SetDirectionAndFacing(#0000)
031e GOTO 083d
}
0326 IF ((GetSpecial(0006) == #000b)) {
032d ActiveEntity.SetMeshCoordsXZ(#001a, #0018)
0334 IF ((Bank0_16bit[0000] >= #04ad)) {
033b ActiveEntity.SetCoordsInMeshXZ(#1a25, #18d8)
033c GOTO 0344
}
0343 ActiveEntity.SetCoordsInMeshXZ(#1bd7, #153b)
0347 ActiveEntity.SetDirectionAndFacing(#0000)
0348 GOTO 083d
}
0350 IF ((GetSpecial(0006) == #000c)) {
0357 ActiveEntity.SetMeshCoordsXZ(#0021, #0011)
035d ActiveEntity.SetCoordsInMeshXZ(#0f64, #05e8)
0361 ActiveEntity.SetDirectionAndFacing(#0000)
0362 GOTO 083d
}
036a IF ((GetSpecial(0006) == #000d)) {
036f IF (Bank0[007f].Bit(0)) {
0374 IF (Bank0[007f].Bit(1)) {
0379 LoadAndInitModel(#0006)
037a EnterVehicle()
}
}
0380 ActiveEntity.SetMeshCoordsXZ(#0011, #000f)
0386 ActiveEntity.SetCoordsInMeshXZ(#0607, #0cb2)
038a ActiveEntity.SetDirectionAndFacing(#0000)
038b GOTO 083d
}
0393 IF ((GetSpecial(0006) == #000e)) {
039a ActiveEntity.SetMeshCoordsXZ(#000d, #000e)
03a0 ActiveEntity.SetCoordsInMeshXZ(#1f1d, #1aa5)
03a4 ActiveEntity.SetDirectionAndFacing(#0044)
03a5 GOTO 083d
}
03ad IF ((GetSpecial(0006) == #000f)) {
03b4 ActiveEntity.SetMeshCoordsXZ(#000d, #000f)
03ba ActiveEntity.SetCoordsInMeshXZ(#0a98, #0c93)
03be ActiveEntity.SetDirectionAndFacing(#00fa)
03bf GOTO 083d
}
03c7 IF ((GetSpecial(0006) == #0010)) {
03cc IF (Bank0[007f].Bit(0)) {
03d1 IF (Bank0[007f].Bit(1)) {
03d6 LoadAndInitModel(#0006)
03d7 EnterVehicle()
}
}
03dd ActiveEntity.SetMeshCoordsXZ(#000e, #0012)
03e3 ActiveEntity.SetCoordsInMeshXZ(#1615, #1e6e)
03e7 ActiveEntity.SetDirectionAndFacing(#0000)
03e8 GOTO 083d
}
03f0 IF ((GetSpecial(0006) == #0011)) {
03f7 ActiveEntity.SetMeshCoordsXZ(#000d, #0016)
03fd ActiveEntity.SetCoordsInMeshXZ(#18dc, #117a)
0401 ActiveEntity.SetDirectionAndFacing(#0000)
0402 GOTO 083d
}
040a IF ((GetSpecial(0006) == #0012)) {
0411 ActiveEntity.SetMeshCoordsXZ(#000a, #0014)
0417 ActiveEntity.SetCoordsInMeshXZ(#14d9, #1c20)
041b ActiveEntity.SetDirectionAndFacing(#0000)
041c GOTO 083d
}
0424 IF ((GetSpecial(0006) == #0013)) {
042b ActiveEntity.SetMeshCoordsXZ(#000b, #0010)
0431 ActiveEntity.SetCoordsInMeshXZ(#09f1, #17f0)
0435 ActiveEntity.SetDirectionAndFacing(#0000)
0436 GOTO 083d
}
043e IF ((GetSpecial(0006) == #0014)) {
0445 ActiveEntity.SetMeshCoordsXZ(#000a, #000e)
044b ActiveEntity.SetCoordsInMeshXZ(#0741, #1a88)
044f ActiveEntity.SetDirectionAndFacing(#0000)
0450 GOTO 083d
}
0458 IF ((GetSpecial(0006) == #0015)) {
045f ActiveEntity.SetMeshCoordsXZ(#000c, #0011)
0465 ActiveEntity.SetCoordsInMeshXZ(#0e5d, #094a)
0469 ActiveEntity.SetDirectionAndFacing(#0000)
046a GOTO 083d
}
0472 IF ((GetSpecial(0006) == #0016)) {
0479 ActiveEntity.SetMeshCoordsXZ(#000e, #000d)
047f ActiveEntity.SetCoordsInMeshXZ(#061c, #0ce0)
0483 ActiveEntity.SetDirectionAndFacing(#0000)
0484 GOTO 083d
}
048c IF ((GetSpecial(0006) == #0017)) {
0493 ActiveEntity.SetMeshCoordsXZ(#0004, #000a)
0499 ActiveEntity.SetCoordsInMeshXZ(#141b, #1c3f)
049d ActiveEntity.SetDirectionAndFacing(#0000)
049e GOTO 083d
}
04a6 IF ((GetSpecial(0006) == #0018)) {
04ad ActiveEntity.SetMeshCoordsXZ(#0006, #000f)
04b3 ActiveEntity.SetCoordsInMeshXZ(#0f9f, #14e9)
04b7 ActiveEntity.SetDirectionAndFacing(#00bc)
04b8 GOTO 083d
}
04c0 IF ((GetSpecial(0006) == #0019)) {
04c7 ActiveEntity.SetMeshCoordsXZ(#0013, #000a)
04cd ActiveEntity.SetCoordsInMeshXZ(#03c0, #1f72)
04d1 ActiveEntity.SetDirectionAndFacing(#0000)
04d2 GOTO 083d
}
04da IF ((GetSpecial(0006) == #001a)) {
04e1 ActiveEntity.SetMeshCoordsXZ(#0013, #0008)
04e7 ActiveEntity.SetCoordsInMeshXZ(#0720, #171b)
04eb ActiveEntity.SetDirectionAndFacing(#00c0)
04ec GOTO 083d
}
04f4 IF ((GetSpecial(0006) == #001b)) {
04fb ActiveEntity.SetMeshCoordsXZ(#000f, #0008)
0501 ActiveEntity.SetCoordsInMeshXZ(#1817, #1d0b)
0505 ActiveEntity.SetDirectionAndFacing(#0000)
0506 GOTO 083d
}
050e IF ((GetSpecial(0006) == #001c)) {
0515 ActiveEntity.SetMeshCoordsXZ(#0011, #0008)
051b ActiveEntity.SetCoordsInMeshXZ(#1cf4, #10a9)
051f ActiveEntity.SetDirectionAndFacing(#0060)
0520 GOTO 083d
}
0528 IF ((GetSpecial(0006) == #001d)) {
052f ActiveEntity.SetMeshCoordsXZ(#0020, #0001)
0535 ActiveEntity.SetCoordsInMeshXZ(#0477, #16ad)
0539 ActiveEntity.SetDirectionAndFacing(#00e7)
053a GOTO 083d
}
0542 IF ((GetSpecial(0006) == #0020)) {
0547 IF (Bank0[007e].Bit(1)) {
054c IF (Bank0[007e].Bit(0)) {
0551 LoadAndInitModel(#0004)
0552 EnterVehicle()
}
0556 IF (Bank0[007e].Bit(2)) {
055b LoadAndInitModel(#0013)
055c EnterVehicle()
}
0560 IF (Bank0[007e].Bit(3)) {
0565 LoadAndInitModel(#0013)
0566 EnterVehicle()
}
056a IF (Bank0[007e].Bit(4)) {
056f LoadAndInitModel(#0013)
0570 EnterVehicle()
}
0574 IF (Bank0[007e].Bit(5)) {
0579 LoadAndInitModel(#0013)
057a EnterVehicle()
}
057e IF (Bank0[007e].Bit(6)) {
0583 LoadAndInitModel(#0013)
0584 EnterVehicle()
}
}
058a ActiveEntity.SetMeshCoordsXZ(GetSpecial(0000), GetSpecial(0001))
0590 ActiveEntity.SetCoordsInMeshXZ(GetSpecial(0002), GetSpecial(0003))
0594 ActiveEntity.SetDirectionAndFacing(GetSpecial(0004))
0595 GOTO 083d
}
059d IF ((GetSpecial(0006) == #0021)) {
05a2 IF (Bank0[007f].Bit(0)) {
05a7 IF (Bank0[007f].Bit(1)) {
05ac LoadAndInitModel(#0006)
05ad EnterVehicle()
}
}
05b3 ActiveEntity.SetMeshCoordsXZ(GetSpecial(0000), GetSpecial(0001))
05b9 ActiveEntity.SetCoordsInMeshXZ(GetSpecial(0002), GetSpecial(0003))
05bd ActiveEntity.SetDirectionAndFacing(GetSpecial(0004))
05be GOTO 083d
}
05c6 IF ((GetSpecial(0006) == #0022)) {
05cb IF (Bank0[007e].Bit(1)) {
05d0 IF (Bank0[007e].Bit(0)) {
05d5 LoadAndInitModel(#0004)
05d6 EnterVehicle()
}
05da IF (Bank0[007e].Bit(2)) {
05df LoadAndInitModel(#0013)
05e0 EnterVehicle()
}
05e4 IF (Bank0[007e].Bit(3)) {
05e9 LoadAndInitModel(#0013)
05ea EnterVehicle()
}
05ee IF (Bank0[007e].Bit(4)) {
05f3 LoadAndInitModel(#0013)
05f4 EnterVehicle()
}
05f8 IF (Bank0[007e].Bit(5)) {
05fd LoadAndInitModel(#0013)
05fe EnterVehicle()
}
0602 IF (Bank0[007e].Bit(6)) {
0607 LoadAndInitModel(#0013)
0608 EnterVehicle()
}
}
060e ActiveEntity.SetMeshCoordsXZ(GetSpecial(0000), GetSpecial(0001))
0614 ActiveEntity.SetCoordsInMeshXZ(GetSpecial(0002), GetSpecial(0003))
0618 ActiveEntity.SetDirectionAndFacing(GetSpecial(0004))
0619 GOTO 083d
}
0621 IF ((GetSpecial(0006) == #0024)) {
0626 LoadAndInitModel(#0008)
0627 SetCurrentEntityAsPlayerEntity()
0628 GOTO 083d
}
0630 IF ((GetSpecial(0006) == #0025)) {
0635 LoadAndInitModel(#0008)
0636 SetCurrentEntityAsPlayerEntity()
0637 GOTO 083d
}
063f IF ((GetSpecial(0006) == #0026)) {
0644 LoadAndInitModel(#0008)
0645 SetCurrentEntityAsPlayerEntity()
0646 GOTO 083d
}
064e IF ((GetSpecial(0006) == #0027)) {
0653 LoadAndInitModel(#0008)
0654 SetCurrentEntityAsPlayerEntity()
0655 GOTO 083d
}
065d IF ((GetSpecial(0006) == #0028)) {
0662 IF (Bank0[007f].Bit(2)) {
0667 LoadAndInitModel(#0005)
0668 EnterVehicle()
}
066e ActiveEntity.SetMeshCoordsXZ(#0006, #0013)
0674 ActiveEntity.SetCoordsInMeshXZ(#1c3b, #0268)
0678 ActiveEntity.SetDirectionAndFacing(#0000)
0679 GOTO 083d
}
0681 IF ((GetSpecial(0006) == #0029)) {
0686 IF (Bank0[007f].Bit(4)) {
068b LoadAndInitModel(#0003)
068c EnterVehicle()
}
0692 ActiveEntity.SetMeshCoordsXZ(#0014, #0011)
0698 ActiveEntity.SetCoordsInMeshXZ(#11d1, #18c9)
0699 GOTO 083d
}
06a1 IF ((GetSpecial(0006) == #002b)) {
06a8 ActiveEntity.SetMeshCoordsXZ(#000b, #0010)
06ae ActiveEntity.SetCoordsInMeshXZ(#095a, #1074)
06b2 ActiveEntity.SetDirectionAndFacing(#0080)
06b3 GOTO 083d
}
06bb IF ((GetSpecial(0006) == #002c)) {
06c2 ActiveEntity.SetMeshCoordsXZ(#000b, #000f)
06c8 ActiveEntity.SetCoordsInMeshXZ(#0f70, #0e98)
06cc ActiveEntity.SetDirectionAndFacing(#0000)
06cd GOTO 083d
}
06d5 IF ((GetSpecial(0006) == #002d)) {
06da IF (Bank0[007f].Bit(4)) {
06df IF (Bank0[007f].Bit(5)) {
06e4 IF (Bank0[0385].Bit(7)) {
06e9 LoadAndInitModel(#0013)
06ea EnterVehicle()
}
06ee LoadAndInitModel(#0003)
06ef EnterVehicle()
06f3 IF (Bank0[0385].Bit(6)) {
06fa ActiveEntity.SetMeshCoordsXZ(#0010, #0004)
0700 ActiveEntity.SetCoordsInMeshXZ(#0033, #0f8b)
0706 WRITE Bank0[0385].Bit(6) = #0000
}
}
}
0707 GOTO 083d
}
070f IF ((GetSpecial(0006) == #002e)) {
0716 ActiveEntity.SetMeshCoordsXZ(#000a, #0010)
071c ActiveEntity.SetCoordsInMeshXZ(#1707, #07e3)
0720 ActiveEntity.SetDirectionAndFacing(#00e8)
0721 GOTO 083d
}
0729 IF ((GetSpecial(0006) == #002f)) {
0730 ActiveEntity.SetMeshCoordsXZ(#000f, #0008)
0736 ActiveEntity.SetCoordsInMeshXZ(#182b, #161f)
073a ActiveEntity.SetDirectionAndFacing(#0080)
073b GOTO 083d
}
0743 IF ((GetSpecial(0006) == #0030)) {
074a ActiveEntity.SetMeshCoordsXZ(#0010, #0006)
0750 ActiveEntity.SetCoordsInMeshXZ(#13d9, #1420)
0754 ActiveEntity.SetDirectionAndFacing(#00e3)
0755 GOTO 083d
}
075d IF ((GetSpecial(0006) == #0031)) {
0768 Op319(((GetSpecial(0007) & #00fc) | #0002))
076e ActiveEntity.SetMeshCoordsXZ(#000a, #000e)
0774 ActiveEntity.SetCoordsInMeshXZ(#0a37, #0fc1)
0778 ActiveEntity.SetDirectionAndFacing(#0080)
0779 GOTO 083d
}
0781 IF ((GetSpecial(0006) == #0033)) {
0786 IF (Bank0[007f].Bit(4)) {
078b LoadAndInitModel(#0003)
078c EnterVehicle()
0790 LoadAndInitModel(#000a)
}
0791 GOTO 083d
}
0799 IF ((GetSpecial(0006) == #0035)) {
07a0 ActiveEntity.SetMeshCoordsXZ(GetSpecial(0000), GetSpecial(0001))
07a6 ActiveEntity.SetCoordsInMeshXZ(GetSpecial(0002), GetSpecial(0003))
07aa ActiveEntity.SetDirectionAndFacing(GetSpecial(0004))
07ab GOTO 083d
}
07b3 IF ((GetSpecial(0006) == #0036)) {
07b8 IF (Bank0[0352].Bit(2)) {
07bd LoadAndInitModel(#000d)
07be EnterVehicle()
}
07c4 ActiveEntity.SetMeshCoordsXZ(#0014, #0011)
07ca ActiveEntity.SetCoordsInMeshXZ(#0fe1, #1880)
07ce ActiveEntity.SetDirectionAndFacing(#00c0)
07cf GOTO 083d
}
07d7 IF ((GetSpecial(0006) == #0037)) {
07de ActiveEntity.SetMeshCoordsXZ(#000b, #0016)
07e4 ActiveEntity.SetCoordsInMeshXZ(#0b50, #0247)
07e8 ActiveEntity.SetDirectionAndFacing(#0000)
07e9 GOTO 083d
}
07f1 IF ((GetSpecial(0006) == #0039)) {
07f8 ActiveEntity.SetMeshCoordsXZ(#0013, #000a)
07fe ActiveEntity.SetCoordsInMeshXZ(#1d13, #0f0c)
0802 ActiveEntity.SetDirectionAndFacing(#0080)
0803 GOTO 083d
}
080b IF ((GetSpecial(0006) == #003a)) {
0812 ActiveEntity.SetMeshCoordsXZ(#0013, #000a)
0818 ActiveEntity.SetCoordsInMeshXZ(#19f2, #00f9)
081c ActiveEntity.SetDirectionAndFacing(#0019)
081d GOTO 083d
}
0825 IF ((GetSpecial(0006) == #003b)) {
082a IF (Bank0[007f].Bit(4)) {
082f LoadAndInitModel(#0003)
0830 EnterVehicle()
0836 ActiveEntity.SetMeshCoordsXZ(#0010, #0004)
083c ActiveEntity.SetCoordsInMeshXZ(#0033, #0f8b)
}
-
Just found this thread.
Also, I just found a project that I worked on a long time ago to parse the world map script. I basically started with this project: https://github.com/cebix/ff7tools (https://github.com/cebix/ff7tools)
Then I made some enhancements. The output parses the opcodes and outputs them in a readable format, very similar to what codeman shared, except in a more "pure" format.
The reason there are the extra bytes like 01 10 in between the data is because these are opcodes like PUSH. World map script operates with data on a STACK.
DLPB said:
wm0.ev (event file in world_us). 0x7e0
My script outputs this around that address:
offset opcode (params) interpretation
07d1 0100 CLEAR
07d2 011b 0006 PUSH GetSpecial(0006)
07d4 0110 0037 PUSH #0037
07d6 0070 PUSH POP2 == POP1
07d7 0201 07eb JUMPZ(POP1) 07eb
07d9 0100 CLEAR
07da 0110 000b PUSH #000b
07dc 0110 0016 PUSH #0016
07de 0308 ActiveEntity.SetMeshCoordsXZ(POP2, POP1)
07df 0100 CLEAR
07e0 0110 0b50 PUSH #0b50
07e2 0110 0247 PUSH #0247
07e4 0309 ActiveEntity.SetCoordsInMeshXZ(POP2, POP1)
07e5 0100 CLEAR
07e6 0110 0000 PUSH #0000
07e8 0304 ActiveEntity.SetDirectionAndFacing(POP1)
07e9 0200 083d JUMP 083d
This is a more "pure" translation of what codeman provided, which was:
07d7 IF ((GetSpecial(0006) == #0037)) {
07de ActiveEntity.SetMeshCoordsXZ(#000b, #0016)
07e4 ActiveEntity.SetCoordsInMeshXZ(#0b50, #0247)
07e8 ActiveEntity.SetDirectionAndFacing(#0000)
07e9 GOTO 083d
Not sure if you guys already figured that out, but just wanted to share. I only skimmed the conversation above, but will read it more carefully now, because I'm working on a personal project to figure out how to find the world map coordinates when a field scripts sends you to the world map.
-
codemann8, this is super funny, because I've also been working on a "graph" of how the field levels and world maps are connected together, then today I just read the first page of this thread, and I saw that you're working on a very similar thing.
I've already got a script that generates a graph very similar to yours. I have one node for each field ID (1-64 are wm nodes, the rest are field nodes), and I have different kinds of links, one for gateways, one for mapjump codes. I draw my graph using d3 force simulation, but my next goal is to anchor the wm nodes so that they don't go all over the place.
Anyway, I plan to share my work soon and will update this thread when that happens.
-
That's cool. Yeah, I also posted the summarized script as well near the end of my last post. Really I just wanted to document it generally so anyone could dive in.
But yes, my graph tho, due to its large size, I'm breaking it up into smaller manageable pieces to which it could be displayed on a webpage describing a certain area of the game, for instance, Mythril Mines would be one page. It will show the 4 field maps, all the enemies within the area, the shared attributes of them (so you can equip accordingly) and have a walkthru describing where to go, what to collect, etc.
I've already extracted most of the game base data, now it's just a matter of piecing all of it together in logical fashion, which i currently have the means of doing, just lots of time ahead...making this a rich resource to reference during runs of ANY kind.
You'd probably find it interesting to have a read thru this thread, given your interests. We'd definitely appreciate any findings not yet documented of any type. The one true thing that stands out as an unknown to me is related to DLPBs last post, his upcoming guide to understanding the mechanics of the multithreaded scripts will help me come up with a smart way to determine which scripts are unused/ not reachable, so the graph of maps can be accurate with no false positives.
-
Very cool, our goals are similar but different, and we could probably benefit by helping each other.
The overall goal behind Kujata is to translate the original assets from FF7 format to web-friendly formats like JSON.
So, if you wanted to display field models and animations in your website, you could use the output of those translations. (Not sure if that's practical for the kind of guide you're making, but just something to think about.)
-
The idea that makes the most sense to me, would be to build a "dependency map/graph". Each "node" in the graph represents a milestone like reaching a location in the game, or setting a Game moment variable. (For example, suppose there's an "if block" in a field script that only executes if some bank variable is set, but no scripts in the game set it. Then that part is unteachable.)
That sounds like a very fun project, and I might even pursue it.
Maybe the three of us could all discuss ideas on Discord or something.
-
The only issue I see with a graph like that, and I'm dealing with this same conundrum, is when optional quests come into the picture, and also when you get to the world map and there's a level of openness to it.
In a sense, we're basically describing a 4th dimension to this graph idea, because depending on your GM, the graph will look different. For instance, the inability to revisit certain parts later. This kinda complicates things a bit but not impossible. Perhaps our map consists of nodes and links, both of which have 2 values associated with them, the GM when this is first accessible, and the GM when it is no longer accessible. Nodes of course would be field maps AND WM areas. But also, some of these nodes would have to be split for cases where there are multiple but separate walkmeshes, accessible from different doorways/lines and gateways.
-
You're exactly right, there are many scenarios that make this tricky: GameMoment, walkmesh reachability, etc. Other examples might be "whether you have a Gold Chocobo", "whether you have a key item in your inventory", "whether Vincent is in your party", "how many Fort Condor battles you've fought". Anyway, yeah, you might think of this as "extra dimensions", or maybe it could be implemented as "conditions" tagged to a link?
And yeah, there's a lot of trickiness involving how to connect field maps together. For example, initially, my graphs were connecting Kalm and Nibelheim together, because when your party reaches the inn in Kalm and when Cloud tells everyone about the back story, the game (field script) performs mapjumps between the Kalm and Nibelheim field flevels (flashbacks). Another problem is (I think you guys may have already pointed it out) some of the coordinates are "relative coordinates", like 33=Yuffie encounter, 45=Leaving Highwind, etc.
-
Hey codemann8 and DLPB, check this out. It's basically codemann8's world map image + nodes for every worldmap ID and field ID + links between them:
https://picklejar76.github.io/kujata-webapp/scene-graph.html (https://picklejar76.github.io/kujata-webapp/scene-graph.html)
You can zoom and pan with your mouse. (Didn't test on mobile device, not sure how well that page works on a phone.)
Here's a screenshot in case it doesn't work in your browser:
(https://raw.githubusercontent.com/picklejar76/kujata-webapp/master/docs/screenshot.png)
The data is not clean, so you'll see a lot of "bugs" in the graph, but it's definitely "interesting". :D
Like codemann8 alluded to, it probably doesn't make sense to try to put all the nodes on a single graph like this, it would probably be better to split it into smaller sections. But here it is anyway. /shrug
-
That's very cool, here's what I was working on before I put the project on a shelf for now. Mind you, this is a tool I made for myself to manually build graphs, not meant to be public, however, without the password you'll not be able to make changes, but you can view the map groups I have built already. Also, hovering over the nodes will give you the image of the map. It's very much unfinished but a start. Click the groups in the upper right listbox.
http://ff7.codemann8.com/mapgroups.php (http://ff7.codemann8.com/mapgroups.php)
-
Oooooh, very nice job, man!
How do you decide the boundaries of a group? Is it automatic, or are you adding the nodes manually, via the tool itself? Looks like the latter? And I'm guessing that each group is basically a section of a walkthrough/ guide? Like, if we compare to the Prima Strategy Guide book, you'd have like 1 group per chapter?
-
Basically, I have a table that I store all known possible map jumps, even including the unreachable scripts as I have no good logical way to disregard them. Then, I create a new group and manually add any map to it, it will show the map I added as a green node (meaning it's now within the group) and it will also auto add red nodes, which are the known jumps that are possible from that map, then with a series of left and right clicks, I can make any of those red nodes green (adding to the group, and then adding more red nodes off that), and also remove nodes that are definitely not possible ways out of the group (at that moment of the game). This process is also the same for specifying whether it's bidirectional or one way.
And yes, the grouping is manually controlled, meant for segmenting it into portions that a walkthrough may have. It's possible, if I keep the groups small enough, that multiple walkthrough authors can utilize this site to show their walkthrough on, they might combine some of these groups into one paragraph, so two node groups would merge and the site would basically combine the enemy bestiary stats and what-not accordingly.
-
As DLPB said, really cool 8)
-
Hey codemann8, your tool shows an image of the actual background. Are you generating the image directly from the tiles in the data, or are you getting it from some other outside source? (Those images would be really useful for a project I'm working on.)
-
The field map images are stored within the field map files themselves. Same as how Makou Reactor extracts the background image. The caveat is that any field models that exist, that appear to be part of the background, won't be there, and there's not any good way to display those without a TON of work.
I think I might have just used MR to mass extract all the field map images tbh.
-
I actually have a way to display field models in a webpage. The Kujata project translates the field graphics models and animations to glTF, and you can use a library like THREE.js to display the models and animations.
Whoa, I didn't see Makou Reactor's Mass Extract feature, until you just pointed out that it exists. Thanks a bunch! I'm going to try that right now!
-
If you do manage to discover where the WM formation data is stored in the PSX file, then try also to find the Yuffie Encounters. They've always alluded me, and I've had to resort to TFerguson's FAQS for that info.
Anyway, field map IDs 1 ~ 64 are all shortcut-jumps to various places on the World Map. Using a placeholder ID for the World Maps as a Field Map means that the devs could easily assign a Field Map ID as the jump, without having to input the x,y,&z coordinates for the WM each time.
01 - WM0 - Midgar Southern Entrance
02 - WM1 - Kalm Entrance
03 - WM2 - Chocobo Ranch Entrance
04 - WM3 - Mythril Mine Entrance Swamp Side
05 - WM4 - Mythril Mine Entrance Junon Side
06 - WM5 - Fort Condor Entrance
07 - WM6 - Junon Entrance
08 - WM7 - Temple of The Ancients Entrance
09 - WM8 - Sleeping Man's Cave Entrance
10 - WM9 - Blacksmith's House Entrance
11 - WM10 - Mideel Entrance
12 - WM11 - Mideel Materia Cave Entrance
13 - WM12 - Costa Del Sol Entrance
14 - WM13 - Mt. Corel Entrance
15 - WM14 - North Corel Entrance
16 - WM15 - Corel Prison Entrance
17 - WM16 - Gongaga Entrance
18 - WM17 - Cosmo Canyon Entrance
19 - WM18 - Nibelheim Southern Entrance
20 - WM19 - Rocket Town Entrance
21 - WM20 - Lucrecia's Waterfall Entrance
22 - WM21 - North Corel Materia Cave Entrance
23 - WM22 - Wutai Entrance
24 - WM23 - Wutai Materia Cave Entrance
25 - WM24 - Bone Village Entrance
26 - WM25 - Corral Valley Entrance Snow Side
27 - WM26 - Icicle Inn Entrance Southern Side
28 - WM27 - Chocobo Sage's House Entrance
29 - WM28 - Round Island Materia Cave Entrance
30 - WM29 - Underwater (Last Coordinates)
31 - WM30 - Underwater Gelnika Entrance
32 - WM31 - Last World Map Coordinates
33 - WM32 - Last World Map Coordinates
34 - WM33 - Last World Map Coordinates
35 - WM34 - Last World Map Coordinates
36 - WM35 - Junon Ship Leaving Junon (Main Event)
37 - WM36 - Junon Ship Arriving at Costa Del Sol
38 - WM37 - Junon Ship Leaving Junon
39 - WM38 - Junon Ship Leaving Costa Del Sol
40 - WM39 - Wutai Shores
41 - WM40 - Highwind Outside Junon (After Escape)
42 - WM41 - Underwater By Junon's Reactor (After Mission)
43 - WM42 - Nibelheim Northern Entrance
44 - WM43 - Mt. Nibel Entrance Nibelheim Side
45 - WM44 - Last World Map Coordinates
46 - WM45 - Mt. Nibel Entrance Rocket Town Side
47 - WM46 - Icicle Inn Entrance Northern Side
48 - WM47 - Great Glacier Entrance
49 - WM48 - Rocket Town North of Entrance
50 - WM49 - Last World Map Coordinates
51 - WM50 - Diamond Weapon Surfaces (Event)
52 - WM51 - Last World Map Coordinates
53 - WM52 - Last World Map Coordinates
54 - WM53 - Highwind Outside Junon
55 - WM54 - Ancient Forest Entrance
56 - WM55 - Underwater (Last Coordinates)
57 - WM56 - Corral Valley Entrance Ravine Side
58 - WM57 - Forgotten Capitol Entrance
59 - WM58 - Highwind Over North Crater
60 - WM59 - Icicle Area Snowstorm Northern Entrance
61 - WM60 - Icicle Area Snowstorm Eastern Entrance
62 - WM61 - Icicle Area Snowstorm Southern Entrance
63 - WM62 - Icicle Area Snowstorm Western Entrance
64 - WM63 - Icicle Area Snowstorm Cavern Entrance
Awesome. This will be added to the location data in ff7tk.
-
@Sithlord if you've had a chance to explore my Enemy Database & Calculator (http://forums.qhimm.com/index.php?topic=18624.0), you'll find screenshots in the "Pics" version of each world map jump point, which could help as a visual aid
-
Kujata also has world map metadata for wm0, wm1, etc., in these files:
coords: https://github.com/picklejar76/kujata-data/blob/master/metadata/field-id-to-world-map-coords.json (https://github.com/picklejar76/kujata-data/blob/master/metadata/field-id-to-world-map-coords.json)
menu names: https://github.com/picklejar76/kujata-data/blob/master/metadata/wm-field-menu-names.json (https://github.com/picklejar76/kujata-data/blob/master/metadata/wm-field-menu-names.json)
links from fields to world map locations: https://github.com/picklejar76/kujata-data/blob/master/metadata/scene-graph.json (https://github.com/picklejar76/kujata-data/blob/master/metadata/scene-graph.json)
By the way, to get a little technical, there's a slight difference between the world map coordinates for entering and exiting a place. For example, when you exit a town from a field script to the world map, you typically appear at a very specific coordinate, slightly away from the town, and the main menu says your location is something like "Outside Kalm". However, to re-enter the town, you don't go to that exact same coordinate ("slightly away from the town"), you go to the location of the town itself. (And I assume you don't have to go to an exact coordinate, but more like a small area, perhaps triggered by a mesh triangle or a set of trigger lines.)
And of course there are the "special" scenarios where, instead of going to a specific coordinate, you go to something like "last field map location" or "relative location" (e.g. getting Yuffie, exiting Highwind, etc.)
-
I believe what i found earlier in the thread is that there are coordinate walkmeshes, that when entered, a script triggers and determines the field map to enter. There is an unknown parameter with a value 1 thru 4 that triggers within a walkmesh, and depending on that parameter it will go to a different area. This part I haven't investigated as to what it is exactly. For instance, the same walkmesh can bring you to the Zolom hung in a tree and also into Mythril Mines, but both have a different parameter tied to it. This you can see earlier in the thread.