Joined: 3/3/2010
Posts: 87
He does path to the tablet on the field on his own, but if you don't have the tablet from the castle grounds already, he suggests looking there and follows you there. Either way, you do have to lead him back to the town once you have all three tablets.
Player (36)
Joined: 9/11/2004
Posts: 2623
Putting this here because it's a bit counterintuitive. It is faster to go up the west wing of F1 to the Silence Terrace, get the Moon Blade, and then go back down to F2 to grab the statue before continuing up to F4. It's 1111 frames faster, or about 18.5 seconds. But there is less experience on the route, because you spend most of your time in enemy free rooms. I also tested going up the East wing and back down to F2 to grab the statue before continuing to the Silence Terrace to grab the Moon Blade, that route is actually 2139 frames slower (primarily because of the longer F1 route.)
Build a man a fire, warm him for a day, Set a man on fire, warm him for the rest of his life.
Player (36)
Joined: 9/11/2004
Posts: 2623
Beginning at 01F6B1 there seems to be hitbox data. The enemy type is stored at an offset of 0x1B from the beginning of the enemy data structure. Bosses and NPCs do not use this table. This seems to be regular enemies only. I have not confirmed what each of these four values mean yet. But I think it might be hurtbox delta x, hurtbox delta y, hitbox delta x, hitbox delta y?
.01:F6B1                 Hitbox $11, $A, $22, $14 ; 0 - Cril
.01:F6B9                 Hitbox $14, $A, $28, $14 ; 1 - Dro
.01:F6C1                 Hitbox $11, $C, $22, $18 ; 2 - Chiphon
.01:F6C9                 Hitbox $14, $A, $28, $14 ; 3 - Tick
.01:F6D1                 Hitbox $10, $A, $20, $14 ; 4 - Bune
.01:F6D9                 Hitbox $18, $B, $30, $16 ; 5 - Savage
.01:F6E1                 Hitbox $10, $A, $20, $14 ; 6 - Gregory
.01:F6E9                 Hitbox $13, $A, $26, $14 ; 7 - Revie
.01:F6F1                 Hitbox $10, $A, $20, $14 ; 8 - Roputo
.01:F6F9                 Hitbox $10, $A, $20, $14 ; 9 - Crud
.01:F701                 Hitbox $12, $A, $24, $14 ; 10 - Hauru
.01:F709                 Hitbox $12, 9, $24, $12 ; 11 - Rock
.01:F711                 Hitbox $11, $A, $22, $10 ; 12 - Hunter Wolf
.01:F719                 Hitbox $14, $A, $28, $14 ; 13 - Magie
.01:F721                 Hitbox $14, $A, $28, $14 ; 14 - Bizarl
.01:F729                 Hitbox $14, $A, $28, $14 ; 15 - Nondietto
.01:F731                 Hitbox $14, $D, $28, $1A ; 16 - Zudabvou
.01:F739                 Hitbox $12, $A, $24, $14 ; 17 - Gustavu
.01:F741                 Hitbox $12, $D, $24, $1A ; 18 - Elumerutergie
.01:F749                 Hitbox $14, $A, $28, $14 ; 19 - Mikky
.01:F751                 Hitbox $14, $A, $28, $14 ; 20 - Angelassu
.01:F759                 Hitbox $14, $A, $28, $14 ; 21 - Revock
.01:F761                 Hitbox $14, $D, $28, $14 ; 22 - Kiezer
.01:F769                 Hitbox $10, $A, $20, $14 ; 23 - Helliones
.01:F771                 Hitbox $D, $A, $1A, $10 ; 24 - Oruton
.01:F779                 Hitbox $14, $A, $28, $14 ; 25 - Sagiterl
.01:F781                 Hitbox $10, $A, $20, $14 ; 26 - Nuter
.01:F789                 Hitbox $14, $A, $28, $14 ; 27 - D-Cline
.01:F791                 Hitbox $14, $A, $28, $14 ; 28 - Killerknight Dame
.01:F799                 Hitbox $13, $C, $26, $14 ; 29 - Edoger
.01:F7A1                 Hitbox $12, $A, $24, $14 ; 30 - Sadowiee
.01:F7A9                 Hitbox $10, $A, $20, $14 ; 31 - Flyokuto
.01:F7B1                 Hitbox $12, $A, $24, $14 ; 32 - Brol
.01:F7B9                 Hitbox $14, $A, $28, $14 ; 33 - Foolisharukan
.01:F7C1                 Hitbox $16, $D, $2C, $1A ; 34 - Bishamon
.01:F7C9                 Hitbox $14, $A, $28, $14 ; 35 - Exellian
.01:F7D1                 Hitbox $18, $C, $30, $18 ; 36 - Thimale's Guardians
.01:F7D9                 Hitbox $28, $24, $50, $2C ; 37 - Evil Spirit Shell
.01:F7E1                 Hitbox $18, $18, $30, $18 ; 38 - Beast Zerah
Names are taken from the x68000 version, as the names never appear in the SNES game. The last three appear during boss battles; however, seem to use this table. I haven't figured out yet how the game decides whether to use this or the (yet to be found) boss table. (To think it took me 7 years to get this shit moving... Jesus I'm slow.) RNG state seems to be located in memory at 3B4 and 3B5. There's what looks to be a uniform distribution subroutine from 0 to Acc at 9800, which outputs to BF. The next state of the RNG from current is simply:
acc = $3B5 + 1;
$3B4 = acc;
$3B5 = 5 * acc;
$3B4 = $3B5 * 16 | $3B5 / 16; // nibble swap
Additionally, one is added to $3B5 every frame. The damage formula in code is (4*Atk - 2*Def)/10 (which is equivalent to the damage formula posted on page two). And there's an RNG component that is calculated by taking the result of that and generating a random number between 0 and one tenth of the result and adding it. So if you do between 0 and 9 damage, you just do that damage. But if you do between 10 and 19 damage, you have a 1/2 chance to do 11 to 20 instead. For 20 to 29 damage, you have a 1/3 chance of doing 21 to 30, and another 1/3 chance of doing 22 to 31. And so on.
Build a man a fire, warm him for a day, Set a man on fire, warm him for the rest of his life.
Player (36)
Joined: 9/11/2004
Posts: 2623
More info dump in no particular order. 0x80B3 is a function I'm calling DoStoredProcInBank1. There's also an entry point for Banks 2, 3, and 4 nearby (however, 3 and 4 are unused because there isn't a jump table for them.) At 0x18000 and 0x28000 there are jump tables for what I'm terming "stored procedures" for lack of better terminology. Essentially, this function allows the programmer to simply call a function from a different bank. Which, I'm not sure why this exists because JSL is a thing. There are 26 stored procedures in bank 1, and another 31 in bank 2. Bank 4 does have a (256 entry) table of addresses at 0x48000, but it seems to be pointing to data of some sort, rather than code. Essentially, the calling convention is: LDA $#7 ; or other valid offset JSL DoStoredProcInBank1 I don't think I've found instances where the table uses a data offset rather than a hard coded one. Which would give it an actual use case. But this was rather confusing to reverse, so I figured I'd document it. I don't know if this is a common thing in SNES games. Along a similar, but even more confusing vein, 0x80E9 contains a function that I'm calling JumpTableCalculate. It essentially modifies the return address on the stack using the argument passed in A, and performs a jump to it. The Jump table is stored immediately after the JSL to JumpTableCalculate. The code in the 0 block seems mostly concerned with input/output, rather than game logic. The function responsible for pulling room sprite data into memory is located at 0x18B84. Room sprite data being any enemies, npcs, bosses, doors, and maybe two other things that I haven't been able to determine. More on this later. Actually it seems to pull in a lot of information about the current room. Most of which I don't know. This function pulls data from 0x5B2DF. Each entry in this array is 0x38 bytes in size and it's simply memcpy'd to 0x7E0050. The offset to the sprite data within the array is 0x2B, and is a 3-byte long address. This winds up in 0x7B, which is why I had previously thought that's the RoomID (the RoomID turned out to be located at 0x7E048E). So, for instance, Atland (RoomID = 0) has it's sprite data located at 0x59552. Each entry is 9 bytes and has this structure:
00000000 RoomSpriteData  struc ; (sizeof=0x9)    ; XREF: .05:AtlandSpritesr
00000000                                         ; .05:ChapelSpritesr ...
00000000 Type:           .BYTE ?
00000001 ID:             .BYTE ?
00000002 XCoord:         .WORD ?
00000004 YCoord:         .WORD ?
00000006 Unknown1:       .BYTE ?
00000007 InitialStatus:  .BYTE ?
00000008 Unknown2:       .BYTE ?
00000009 RoomSpriteData  ends
InitialStatus is a bitfield, which interacts with several places. I haven't bothered to figure out exactly what it does, because it's always set the same for the same enemy. I don't know what the unknowns do. They set 0x54A and 0x55D respectively. The good news is we have positional information here. And a very stable, programmatic way of obtaining it for any room. Just need some tile data and I can make the maps I've wanted for so long. Current state of the art on the data in RAM for sprites (and Nasir)
wram:0500 StatusBitfield: .BYTE 0 ; (uninited)    ; DATA XREF: sub_18082:loc_180B2r
wram:0500                                         ; sub_18082+87r ...
wram:0501 StatusBitfield2?:.BYTE 0 ; (uninited)   ; DATA XREF: sub_18BD5+19w
wram:0501                                         ; sub_19125r ...
wram:0502 XCoord:         .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_183D8+25r
wram:0502                                         ; sub_183D8+50r ...
wram:0504 YCoord:         .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_183D8+7Cr
wram:0504                                         ; sub_183D8+A7r ...
wram:0506 byte_7E0506:    .BYTE 0 ; (uninited)    ; DATA XREF: sub_19BCE+102w
wram:0506                                         ; sub_19BCE+11Fw ...
wram:0507 byte_7E0507:    .BYTE 0 ; (uninited)    ; DATA XREF: SamsonXPosCheck+3Fw
wram:0507                                         ; sub_280EA+22w ...
wram:0508 byte_7E0508:    .BYTE 0 ; (uninited)    ; DATA XREF: sub_19BCE+105w
wram:0508                                         ; sub_19BCE+122w ...
wram:0509 byte_7E0509:    .BYTE 0 ; (uninited)    ; DATA XREF: SamsonXPosCheck+45w
wram:0509                                         ; sub_280EA+2Ew ...
wram:050A Unknown1:       .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_18D5E+Fr
wram:050A                                         ; sub_19B34+48r ...
wram:050C word_7E050C:    .BYTE 0,0 ; (uninited)  ; DATA XREF: DoDamage+4Dw
wram:050C                                         ; ProcessHit+F3w ...
wram:050E word_7E050E:    .BYTE 0,0 ; (uninited)  ; DATA XREF: DoDamage+53w
wram:050E                                         ; ProcessHit+FDw ...
wram:0510 AnimationFrames:.BYTE 0,0 ; (uninited)  ; DATA XREF: sub_18BD5+1Fw
wram:0510                                         ; DoDamage:loc_19260w ...
wram:0512 BumpAndHurtFrames:.BYTE 0 ; (uninited)  ; DATA XREF: sub_1A334+Cw
wram:0512                                         ; ProcessHit+221w ...
wram:0513 word_7E0513:    .BYTE 0,0 ; (uninited)  ; DATA XREF: ProcessHit+73w
wram:0513                                         ; sub_1A334+12w ...
wram:0515 word_7E0515:    .BYTE 0,0 ; (uninited)  ; DATA XREF: DoDamage+5Cw
wram:0515                                         ; sub_2BCE0+39w ...
wram:0517 unk_7E0517:     ; 0 .BYTE uninited & unexplored ; DATA XREF: DoDamage+62w
wram:0517                                         ; sub_28199+3Cw
wram:0518 SpriteType:     .BYTE 0 ; (uninited)    ; DATA XREF: sub_18184r
wram:0518                                         ; HandleBossDeathr ...
wram:0518                                         ; 0 = ?, 1 = NPC, 2 = Enemy, 3 = Boss, 4 = ?, 5 = Door
wram:0519 byte_7E0519:    .BYTE 0 ; (uninited)    ; DATA XREF: sub_183D8+4r
wram:0519                                         ; sub_18BD5+2Aw ...
wram:0519                                         ; Range 0-3 (Direction?)
wram:051A BinarySomething?:.BYTE 0 ; (uninited)   ; DATA XREF: sub_280EA+5r
wram:051A                                         ; sub_2D9BB+23w
wram:051B SpriteID:       .BYTE 0 ; (uninited)    ; DATA XREF: sub_18184:loc_181BBr
wram:051B                                         ; HandleBossDeath+7r ...
wram:051B                                         ; Enum depends on 0x0518 type.
wram:051C HitBoxID:       .BYTE 0 ; (uninited)    ; DATA XREF: IsCollidingWithInteractable:loc_1A414r
wram:051C                                         ; IsCollidingWithInteractable:loc_1A41Er ...
wram:051D Unknown2:       ; 0 .BYTE uninited & unexplored ; DATA XREF: sub_1A821+2Cw
wram:051D                                         ; sub_1A89B+24w ...
wram:051E unk_7E051E:     ; 0 .BYTE uninited & unexplored ; DATA XREF: sub_1A821:loc_1A86Fw
wram:051E                                         ; sub_1A7B5:loc_1A90Bw ...
wram:051F Tangible?:      .BYTE 0 ; (uninited)    ; DATA XREF: ProcessHit:loc_1A15Dr
wram:051F                                         ; ProcessHit+25w ...
wram:0520 HitPoints:      .BYTE 0,0 ; (uninited)  ; DATA XREF: DoDamage+3r
wram:0520                                         ; DoDamage+Aw ...
wram:0522 MagicPoints:    .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_19125+42r
wram:0522                                         ; sub_19749+4r ...
wram:0524 AttackStat:     .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_194F7+12w
wram:0524                                         ; sub_194F7+27r ...
wram:0526 DefenseStat:    .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_1952F+12w
wram:0526                                         ; sub_1952F+23r ...
wram:0528 GoldAmount:     .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_1947Ar
wram:0528                                         ; sub_1947A+7w ...
wram:052A ExperienceAmount:.BYTE 0,0 ; (uninited) ; DATA XREF: sub_194B2r
wram:052A                                         ; sub_194B2+7w ...
wram:052C LevelOrEnemyState:.BYTE 0,0 ; (uninited) ; DATA XREF: sub_194B2:loc_194CDr
wram:052C                                         ; sub_194B2+33w ...
wram:052E word_7E052E:    .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_19592:loc_19600r
wram:052E                                         ; sub_19592+7Aw ...
wram:0530 word_7E0530:    .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_194F7+6r
wram:0530                                         ; sub_1A89B+45r ...
wram:0532 word_7E0532:    .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_18934+25r
wram:0532                                         ; sub_1952F+1Er ...
wram:0534 word_7E0534:    .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_2B2F6:loc_2B85Aw
wram:0534                                         ; sub_2B8BA+8r ...
wram:0536 word_7E0536:    .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_19008r
wram:0536                                         ; sub_19008+8r ...
wram:0538                 ; 0 .BYTE uninited & unexplored
wram:0539 unk_7E0539:     ; 0 .BYTE uninited & unexplored ; DATA XREF: sub_2C797+1D3w
wram:053A                 ; 0 .BYTE uninited & unexplored
wram:053B unk_7E053B:     ; 0 .BYTE uninited & unexplored ; DATA XREF: sub_2C797+1D9w
wram:053C                 ; 0 .BYTE uninited & unexplored
wram:053D                 ; 0 .BYTE uninited & unexplored
wram:053E word_7E053E:    .BYTE 0,0 ; (uninited)  ; DATA XREF: sub_18BD5+22w
wram:053E                                         ; sub_19170+5r ...
There's a lot of unknowns here, but the good news is I have multiple code references for every entry. So figuring out any specific entry should be doable. These should be more or less self-explanatory, "Tangible?" is used by Battler to determine whether or not he can hit or be hit. It probably has more uses than just that. HitboxID is used by Natela and Samson and... no other enemy at all. Because these two enemies have animations where they move and walk around, they have differently sized hitboxes depending on their actions. You can see this in action in the hitbox code at 0x1A3DF, Samson's switch case is at 0x1A414, Natela's at 0x1A41E, for the rest of the cases they simply load an offset (except Battler, who does that aforementioned check to see if he even has a hitbox.) The boss hitboxes are stored here:
.01:FCEF                 Hitbox <28> ; Samson Swing Frame #1
.01:FCF7                 Hitbox <32> ; Samson Swing Frame #2
.01:FCFF                 Hitbox <24> ; Samson Idle
.01:FD07                 Hitbox <24> ; Samson Jump
.01:FD0F                 Hitbox <18> ; Natela Walk Frame #1
.01:FD17                 Hitbox <18> ; Natela Walk Frame #2
.01:FD1F                 Hitbox <24> ; Natela Idle
.01:FD27                 Hitbox <24> ; Natela Fire Breath
.01:FD2F                 Hitbox <18> ; Natela Rush #1
.01:FD37                 Hitbox <18> ; Natela Rush #2
.01:FD3F                 Hitbox <18> ; Natela Rush #3
.01:FD47                 Hitbox <40> ; Eardon
.01:FD4F                 Hitbox <1C> ; Duma
.01:FD57                 Hitbox <18> ; Duma Hands
.01:FD5F                 Hitbox <20> ; Thimale
.01:FD67                 Hitbox <14> ; Battler
.01:FD6F                 Hitbox <1A> ; Battler Eggs
.01:FD77                 Hitbox <20> ; Evil Spirit (Green and Red)
.01:FD7F                 Hitbox <14> ; Thor (Human)
.01:FD87                 Hitbox <30> ; Thor (Bird)
.01:FD8F                 Hitbox <10> ; Thor (Red Shield Balls)
.01:FD97                 Hitbox <10> ; Ella
Doors and NPCs all have the exact same hitboxes. These are static constants in code inside of the hitbox generating routine. At 0x29574 there is a gargantuan subroutine, that I believe MIGHT be the Sprite update routine (that is to say, enemy and NPC AI). However, navigating it so far has been difficult (because it's a switch on 6 cases for the SpriteType, which lead to a switch on the SpriteID of 16, 39, and 90 options for bosses, enemies, and npcs respectively...). I'll give it more of a go later on. I just discovered it today. At 0x292B2 there seems to be some sinusoidal looking data that's referenced by a chunk of code related to Thor Bird. I was thinking it might have something to do with movement. At memory location 0x7E0393 there seems to be a counter which might be related to Samson's idle timer. Which should help with the boss fight if I can figure out where it gets set. Nasir's stats by level in data are stored at offset 0x1E068. But I lost the code offset due to sloppiness. I'll find it again eventually. Enemy stats, on the other hand, are stored at 0x591AB. At this address there's a list of addresses to the actual structure data. The first enemy is at 0x591F9. Boss stats follow immediately after at 0x59442, with the first boss at 0x59462. The structure's layout is:
00000000 EnemyStatsByType struc ; (sizeof=0xF)   ; XREF: .05:91F9r
00000000                                         ; .05:stru_59462r
00000000 field_0:        .BYTE ?
00000001 HP:             .WORD ?                 ; base 10
00000003 MP:             .WORD ?                 ; base 10
00000005 Attack:         .WORD ?                 ; base 10
00000007 Defense:        .WORD ?                 ; base 10
00000009 Gold:           .WORD ?                 ; base 10
0000000B Experience:     .WORD ?                 ; base 10
0000000D State:          .WORD ?
0000000F EnemyStatsByType ends
I don't know what field_0 is, but it gets placed into 0x7E055A (The BinarySomething? field.)
Build a man a fire, warm him for a day, Set a man on fire, warm him for the rest of his life.
Joined: 3/11/2008
Posts: 583
Location: USA
OmnipotentEntity wrote:
More info dump in no particular order. 0x80B3 is a function I'm calling DoStoredProcInBank1. There's also an entry point for Banks 2, 3, and 4 nearby (however, 3 and 4 are unused because there isn't a jump table for them.) At 0x18000 and 0x28000 there are jump tables for what I'm terming "stored procedures" for lack of better terminology. Essentially, this function allows the programmer to simply call a function from a different bank. Which, I'm not sure why this exists because JSL is a thing.
Especially when there weren't short jumps/JSRs on the 68k whence came Lagoon. But, this sort of indirect call is called a trampoline. (by analogy: jump, [bounce off trampoline,] jump somewhere else)
Challenger
He/Him
Skilled player (1637)
Joined: 2/23/2016
Posts: 1035
I watched the current TAS and found this game interesting (I never played this game before). So, I tested a bit (through the TAS), and noted that you can move a bit while waiting some frames for each attack: http://dehacked.2y.net/microstorage.php/info/1239648865/lagoon-timesaver.smv EDIT: Snes9x 1.43 v18 used.
My homepage --Currently not much motived for TASing as before...-- But I'm still working.
Player (36)
Joined: 9/11/2004
Posts: 2623
Build a man a fire, warm him for a day, Set a man on fire, warm him for the rest of his life.