Jump to content


Photo

A Second Word on Events


  • Please log in to reply
20 replies to this topic

#1 CT075

CT075

    formerly shota supercomputer

  • Member
  • Gender:Male
  • Location:Somewhere
  • Favorite Fire Emblem Game:Radiant Dawn

Posted 01 January 2012 - 02:24 PM

This is just a clarification of some stuff that Arch didn’t really cover in his tutorial, in a very simplified way. Read it here. I highly recommend reading it if you want to learn events, Arch did a great job with it, it should be sufficient to get you through most semi-complex events you may need.

On the other hand, I still do feel that there are some (largely unnecessary but nice-to-know things regardless) things that Arch either skimmed over or missed completely. I’m going to say right now that none of the following things are necessary for events; the tutorial and the EA language doc included with the EA release should be enough, given enough creativity.

First of all, that FE7 code template provided with the EA release? Here’s a slightly more complete one.
Spoiler -


Now, this may not seem a whole lot different at first glance. “Sure, Cam, you changed some names around and added a little thing at the top, how is it more complete?” Well, the default included template is already mostly done for you. All I did was add a few things (well, actually only one) to make things easier on you.

At the top of the file, you’d notice a few lines I added that says
#define MAP_OFFS 0x_____
…
EventPointerTable(0x__,MAP_OFFS)
What does that do? Well, it’s supposed to repoint the map for the chapter for you without any kerjiggering in nightmare.

The next thing you may notice is, as I pointed out before, that I changed the label names. It’s largely an aesthetic change, but it illustrates an important point in programming. You can label anything whatever you want. If you want, you can create a macro and call it
#define TunaFish(unit,class,level) LOU1 unit class Level(level,ally,false)
if you wanted. Hell, in my template, you could change MiscEvents to “IHaveALovelyBunchOfCoconutsCollection” and EA wouldn’t complain (as long as you updated the pointer array to match).However, it’s important that you label your code properly so that people who are trying to read your code can have some clue of what it’s supposed to do. Imagine, for example, that you saw this (this is one of my old events):
what:
GOTO NowLoadingTurn
FADI 0x05
ASMC 0x7A8B9
LOMA 0x02 [13,12]
FADU 0x05
WarpIn(Eli,17,15)
LOU1 NewLyn
ENUN
TEX1 0x836
MUS1 0x0042
MORETEXT 0x837
REMA
TEX6 0x01 [07,05] 0x0838
_ASM0x42 0x83181
ENUT 0x69
ENDA
That is confusing and almost unreadable. The label name doesn’t tell you anything about what it does, and the number of raw hex or ASM codes doesn’t help anything.
Now, here’s the same code, but I fixed some things up:
BeginMapThree:
// Stall and generate the 'now loading' message.
GOTO NowLoadingTurn
FADI 0x05
// Clear units and load next map
ClearAll // ASMC 0x7A8B9
LOMA 0x02 [13,12]
FADU 0x05
// Load player units
WarpIn(Eli,17,15)
LOU1 NewLyn
ENUN
TEX1 0x836
MUS1 TogetherWeRide //0x0042
MORETEXT 0x837
REMA
ScrollText(0x838)
// Activate permanent event ID 0x69 and begin map three
ENUT 0x69
ENDA
Should be slightly less scary, but even that could be improved. At the moment, all I did was replace some ASM codes with corresponding macros. I also added in comments to make it more obvious what is supposed to do what. This makes it much easier for both myself (when trying to fix something if it doesn’t work) and others (if they’re trying to read my source for some reason or other).

Spoiler - Comments


Going back to pointers for a second (I know, I’m rambling, just try to follow along), remember this?
Chapter:
POIN TurnEvents 
POIN CharacterEvents 
POIN LocationEvents 
POIN MiscEvents
POIN BallistaData BallistaData
POIN Bad Bad Bad Bad
POIN Good Good Good Good
POIN OpeningEvent EndingEvent
This is important. This is the ‘header’ (for lack of a better term) of your chapter. This set of pointers tells your game where all the data in your chapter is. I suppose you could write it like this:
Chapter:
POIN TunaFish Foo Generic Not Rain Rain Units Units2 Units3 Units4 Other Other2 Other3 Other4 Scene 
POIN OtherScene
That should work perfectly well, assuming you get your labels all correct. But again, that’s difficult to read. If you were to look at that string, how would one know which label points to the turn based events? Rather, it’s separated the way it is so it’s much simpler for the reader to figure out which label (and therefore, which in-game pointer) is used for what. Let’s go over each one individually:

TurnEvents, CharacterEvents, LocationEvents and MiscEvents have all been covered fairly thoroughly in Arch’s event tutorial. As long as you make sure that they aren’t pointed to the same thing, it should cause no problems.

BallistaData is almost self-explanatory. There are two pointers there, and for a reason. Technically, the labels should read
POIN BallistaDataEliwood BallistaDataHector
But, since most of us only deal with one of the two lords’ modes (Lyn mode is counted as Eliwood Normal), they point to the same thing for convenience.

The same goes for the Good and Bad labels. If you disassemble a chapter, you’d see that the labels read
POIN EnemyUnitsENM EnemyUnitsEHM EnemyUnitsHNM EnemyUnitsHHM
POIN AllyUnitsENM AllyUnitsEHM AllyUnitsHNM AllyUnitsHHM
But again, they point to the same thing because precious few of us deal with HM vs. NM, or Eliwood vs. Hector modes.

OpeningEvent is the event that is loaded if the prep screen is active, and it has no purpose otherwise. A note, if the prep screen is active, it also must end with an ENDB command.

EndingEvent is loaded when condition IDs 0x02 or 0x03 are triggered (boss death and seize, respectively). All the other victory conditions (Rout, Escape, etc.) must be called manually (I don’t know exactly how but I know there’s a macro for it).

Another thing you may notice myself and some others doing in their source is the usage of a little code called ‘GOTO’, or its close cousin ‘JUMP’. What those do is throw the event over to another set of commands, which are written elsewhere. There is a vital difference between the two (besides that GOTO doesn’t exist in FE6). GOTO sends you to a set of events, and returns to the original thread once it reaches an ENDA. JUMP does not.
Foo:
SomeEvents
// This is the original event thread
GOTO MoreEvents
// Control returns here
MESSAGE This statement is reached
JUMP EvenMoreEvents
// Everything after this is never reached.
WARNING This statement is not
GameOver
ENDA
…
MoreEvents:
// events
ENDA
// Go back to Foo.
…
EvenMoreEvents:
// events
ENDA
// The event is now over.
I know that it seems pointless and unnecessary. “But Cam, why would you bother with GOTO if you can just write the extra events into the original thread?” For most occasions, there’s no reason not to. However, if you happen to call a certain string of codes several times, then suddenly it is a lot more space-efficient to use GOTO. For example:
Spoiler -

As you can see, I managed to save space in my ROM by making it so that, instead of having to use the same set of codes (“now loading”) three times, I only had to use it once and replaced them with a GOTO. The use of this command is largely conditional, there are only a handful of situations where it saves enough space to really matter. However, it’s a good habit to get into, if only because it makes things so much easier to read (oh I know what that does I don’t have to try and follow the string of codes again).
Spoiler - More on JUMP


Finally, since I know this is dragging on, I want to make a note about the usage of macros. Macros and definitions make your code much more humanly readable.
UNIT 0x03 0x02 0x00 0x9 [7,4] [4,6] [0x01,0x0,0x0,0x0] [0x0,0x0,0x0,0x0]
is a lot scarier (and harder to decipher) than
UNIT Lyn_t LynLord 0x00 Level(3,Ally,False) [7,4] [4,6] [IronSword] NoAI
This (scary-looking) string of ASMC codes:
ASMC 0x7D711; STAL 0x3C; ASMC 0x7D7B5; STAL 0x3C; ASMC 0x6CCB9; ASMC 0x7D771
can be compressed to a single
QuintessenceEffect
with the EAstdlib. It makes it so much easier to read your script and to find problems that it’s suddenly not a giant mystery/question mark every time something goes wrong.

In conclusion, I want to thank Nintenlord for making such an awesome tool (making events with a hex editor is… well, never again), Hextator/Zahlman/anyone else who helped me with my noobish regular programming skills (that carried over back to events and inspired me to write this), Arch for writing such a boss event tutorial that makes it so I don’t have to explain everything (and for being a boss), everyone else who has helped me with this hobby and you, the reader, for spending this much time dealing with me and my rambling.

Thank you, and goodnight.

Edited by Camtech, 05 January 2012 - 04:24 PM.


#2 Jubby

Jubby

    Who, me?

  • Member
  • Gender:Male
  • Favorite Fire Emblem Game:Blazing Sword

Posted 01 January 2012 - 02:33 PM

I like the MAP OFFS thing :P Your talk of ASM always confuses me. Lawl, I'll figure it out. So, in general, is it better to put explanations BEFORE the codes?

#3 CT075

CT075

    formerly shota supercomputer

  • Member
  • Gender:Male
  • Location:Somewhere
  • Favorite Fire Emblem Game:Radiant Dawn

Posted 01 January 2012 - 02:43 PM

...I don't think I actually talked about ASM directly, except for that one bit where I used it to illustrate how macros make things less scary.

And explanations going before or after is a matter of style. I think the current standard for programmers is to put it before, but you don't have to put explanations at all if that's what floats your boat. It makes it easier for everyone involved if you do so.

#4 Jubby

Jubby

    Who, me?

  • Member
  • Gender:Male
  • Favorite Fire Emblem Game:Blazing Sword

Posted 01 January 2012 - 02:48 PM

I just figured it's probably easier to have sort of a universal standard :P And I realized a bit after that it's ASM conditions which I didn't realize were ASM conditions (like quintessence effect, for example) because I've only ever used/seen/heard of the macros. I never realized they were ASM related at all Posted Image

#5 Primefusion

Primefusion

    Now Be Defiant

  • Member
  • Gender:Male
  • Favorite Fire Emblem Game:N/A

Posted 02 January 2012 - 05:35 PM

Nicely done, Cam. I'm sure people will find this helpful. Hopefully, people will start using comments more often when they post their scripts in the EA questions thread (It really does help, folks!).

Your explanations of GOTO and JUMP were very nice as well. I know I'll probably be using those on a regular basis now.


I just figured it's probably easier to have sort of a universal standard :P And I realized a bit after that it's ASM conditions which I didn't realize were ASM conditions (like quintessence effect, for example) because I've only ever used/seen/heard of the macros. I never realized they were ASM related at all Posted Image


Haha, you newbies have it easy nowadays. I still remember back in the day when macros didn't even exist yet (Btw, thank you for that Nintenlord. You're slowing the progress of Carpal Tunnel for many of us =P)

#6 Arch

Arch

    Just go with the flow lion.

  • Member
  • Gender:Male
  • Favorite Fire Emblem Game:Radiant Dawn

Posted 05 January 2012 - 10:05 AM

Haha, you newbies have it easy nowadays. I still remember back in the day when macros didn't even exist yet (Btw, thank you for that Nintenlord. You're slowing the progress of Carpal Tunnel for many of us =P)

Heck, everyone has it easy. The original EA didn't have a labeling system: every pointer to something within the file had to reference its line number in the document. It was absurd to keep track of everything (wrote Ch1 of FE4A pre-labels). But even that was better than raw hex editing. It's come a long way, a really long way. Everyone should send Nintenlord a card or something, maybe a small gift basket for making our lives infinitely easier.

It's nice to see a supplement to my event tutorial crop up. I'm revising it for a second edition, so I'll be sure to insert a link to this addon tutorial. Great work, Cam!

Edited by Arch, 05 January 2012 - 10:06 AM.


#7 Nintenlord

Nintenlord

    Disciple of Harmony

  • Member
  • Gender:Male
  • Location:Finland
  • Favorite Fire Emblem Game:Blazing Sword

Posted 05 January 2012 - 11:02 AM

#define MAP_OFFS 0x______

...

MESSAGE The map for this chapter is at offset 0x______
This would be better as:
#define MAP_OFFS 0x______

...

MESSAGE The map for this chapter is at offset MAP_OFFS

It's really topics like these that are my favorites, people sharing knowledge and teaching eachother to use my app while thanking how easy I've made the process :P: . As someone who made 6 custom chapters 100% in hex, I can safely say that anything is better than it.

Edited by Nintenlord, 05 January 2012 - 11:10 AM.


#8 CT075

CT075

    formerly shota supercomputer

  • Member
  • Gender:Male
  • Location:Somewhere
  • Favorite Fire Emblem Game:Radiant Dawn

Posted 05 January 2012 - 04:22 PM

[...]
This would be better as:
[...]

Changed.

It's really topics like these that are my favorites, people sharing knowledge and teaching eachother to use my app while thanking how easy I've made the process :P: . As someone who made 6 custom chapters 100% in hex, I can safely say that anything is better than it.

Anything is better than raw hex.

*gives Nintenlord a prize*

Edited by Camtech, 05 January 2012 - 04:23 PM.


#9 CT075

CT075

    formerly shota supercomputer

  • Member
  • Gender:Male
  • Location:Somewhere
  • Favorite Fire Emblem Game:Radiant Dawn

Posted 04 April 2012 - 06:12 PM

(Should this be called “a third word”?)

Part 2: Touching up on conditions


“cam what the fuck do you want now”

Personally, I’m only writing this because I’m bored. And because some people might actually get a use out of this. But I digress.

A lot of times, I see a ton of people who do the (entirely reasonable) IF-chain just because they need to check several things at once. Like Astralunasol once asked me to do -

// The following checks whether either of two characters are dead.
IFCD	0x00	ID	char1	// if (char1.isDead) {
IFCD 	0x00	ID + 1	char2	// if (char2.isDead) {
// if both are dead
ELSE	ID + 2	// } else {
ENIF	ID + 1
// if char1 is dead and char2 is not
ENIF	ID + 2	// }
ELSE	ID + 3	// } else {
IFCD	0x00	ID + 4	char2	// if (char2.isDead) {
// if char1 is alive and char2 is not
ELSE	ID + 5	// } else {
ENIF	ID + 4
// if both are alive
ENIF	ID

And so forth. I’d imagine it could get pretty complex if you have long things that are dependent on the conditions. He also asked me to do one for him that was an all-or-nothing dependent on nine characters (if any of them are dead, the whole thing is off). Now, I could do something like the above to simulate how ANDs are processed. Which is tedious because you get into a ton of nested if/elses since there’s no code called IFCND (if character not dead) or something.

However, since ASM has a similar problem, there’s a much simpler way. Instead of trying
IFCD	0x00	ID	char1
ELSE	ID + 1
ENIF	ID
IFCD	0x00	ID + 2	char2
// … and so on…
which takes like 18 different condition IDs, you could do it a much simpler way -

// the better, assembly code-like way.
IFCD	0x00	ID	char1
JUMP	next	// if char1 is dead, skip the rest of this
ENIF	ID
IFCD	0x00	ID + 1	char2
JUMP	next	// etc
ENIF	ID + 1
// … //
ENIF	ID + 8
// This code is never reached if any of the above conditions returned true
// proceed if all characters are alive

next:
// rest of chapter

I can imagine some people going “huh” already, so I’ll try to explain. Because there’s only one test (if dead), you’d otherwise need a ton of ELSEs which can get annoying, as you saw above. With my way, however, you can get by with none.

Still confused? I was for a while too. Think about it this way.

if condition is negative (if the conditions aren’t met, don’t do something)
skip this part
stuff that happens if it’s true
end up here anyway

if (!condition) {// if the qualifying condition returns false
skip to *end*
}
*stuff*
*end*

Say that I was testing if a certain character is alive. I’d check to see if he’s dead. If he is, then we skip the part labeled *stuff*. This has the same effect as an ELSE clause, since the *stuff* is only executed if he’s NOT dead – in other words, if he’s alive. If you still don’t get it, don’t be worried. It takes a sec.

and jubby very nicely puts it better than i could

Basically rather than using else's, just jump over the rest of your conditions if the first one is false. If it's not, you move down to the next condition, and if it's false, jump over the rest of it, if true, go down to the next one, etc. Right?


“But Cam, it’s not a black-and-white, I NEED my ELSE clause to do something!”

This is legitimate as well. Let me rewrite my original 2-character condition using the philosophy outlined above.

// The better-ified version.
IFCD	0x00	ID	char1	// Check if char1 is dead
// He is, so we move on to check for character 2
IFCD	0x00	ID + 1	char2	// If both are dead
*both dead codes*
JUMP	end	// And now we’re done, so skip EVERYTHING else
ENIF	ID + 1
*char1 dead codes*
JUMP	end	// we skip again
ENIF	ID
IFCD	0x00	ID + 2	char2	// You only get here if char1 is alive.
*char2 dead codes*
JUMP	end
ENIF	ID + 2
*both alive codes*	// Again, you only arrive here if none of the other JUMPs were executed.

end:
// …rest of script

Still don’t get it? Well… I don’t blame you. I suck at explaining this stuff. Just… stare at it for a long time and it might click, it might not.

Just take my word for it, if it does, you suddenly have twice as many condition IDs to work with (and half as many to keep track of).

Edited by Camtech, 04 April 2012 - 06:57 PM.


#10 Jubby

Jubby

    Who, me?

  • Member
  • Gender:Male
  • Favorite Fire Emblem Game:Blazing Sword

Posted 04 April 2012 - 06:19 PM

Basically rather than using else's, just jump over the rest of your conditions if the first one is false. If it's not, you move down to the next condition, and if it's false, jump over the rest of it, if true, go down to the next one, etc. Right?

#11 CT075

CT075

    formerly shota supercomputer

  • Member
  • Gender:Male
  • Location:Somewhere
  • Favorite Fire Emblem Game:Radiant Dawn

Posted 04 April 2012 - 06:21 PM

preeeetty much

i know i explained it super-poorly but that's the basic idea. when you get into nested if's and else's it can get kind of icky

#12 Jubby

Jubby

    Who, me?

  • Member
  • Gender:Male
  • Favorite Fire Emblem Game:Blazing Sword

Posted 04 April 2012 - 06:26 PM

Yeah I often confuse the shit outta myself with just normal IF's and ELSE's, so if they were stacked I'm pretty sure I'd fuck it up multiple times and need like, entire paragraph comments every line to explain it to myself XD

I like the JUMP way better, too. Never knew it existed till you redid my events that one time though :P

#13 zahlman

zahlman

    Member

  • Member
  • Gender:Male
  • Favorite Fire Emblem Game:Blazing Sword

Posted 08 April 2012 - 03:53 PM

GOTO sends you to a set of events, and returns to the original thread once it reaches an ENDA. JUMP does not.


fffffffuuuuuuuuuuuu, Nintenlord.

'GOTO' is a bad name for this. In real programming languages that actually have something called 'goto', a return like that is never implied. It's intended to behave like, well, a jump.

Please rename this to 'CALL' or something.

As you can see, this technique allows you to manually JUMP into the middle of an event flow, the same way GOTO does automatically.


Not really, since it will only work for one JUMP source. If there's more than one, then in the JUMPed-to code, you don't know where to JUMP back to.

Also, in your example you really ought to put an ENDA or something at the end of ContinueFoo, so that it doesn't look like it'll flow through to SomeEvents again.

I like the MAP OFFS thing :P Your talk of ASM always confuses me. Lawl, I'll figure it out. So, in general, is it better to put explanations BEFORE the codes?


If the explanation is for what a block of several lines of code does, put the explanation before the block.

If the explanation is of a single line, put it at the end of the line.

I never realized they were ASM related at all


In a sense, everything is ASM related here. It's just that some functionality has shortcuts, and other things require explicitly saying "go to this location in the ROM and run the code that's there".

This would be better as:
[code]
#define MAP_OFFS 0x______

...

MESSAGE The map for this chapter is at offset MAP_OFFS


This relies on MAP_OFFS being in allcaps and detects it like that, yeah? What if you want an all-caps word in a message, will it get flagged as - oh, no, wait, you're doing C-style preprocessing, aren't you.

Edited by zahlman, 08 April 2012 - 04:03 PM.


#14 CT075

CT075

    formerly shota supercomputer

  • Member
  • Gender:Male
  • Location:Somewhere
  • Favorite Fire Emblem Game:Radiant Dawn

Posted 08 April 2012 - 03:57 PM

i personally like "LINK" or something (to mirror bl = branch link) but i agree that GOTO is an unsuitable name

#15 Nintenlord

Nintenlord

    Disciple of Harmony

  • Member
  • Gender:Male
  • Location:Finland
  • Favorite Fire Emblem Game:Blazing Sword

Posted 09 April 2012 - 07:48 AM

I'd prefer BRANCH if I were to name it now, but CALL is a good second. I have no idea what I was thinking back in the day. I'll think about renaming it.

EDIT: I'll post this here, too:

IFCD 0x00 ID char1
IFCD 0x00 ID char2
IFCD 0x00 ID char3
IFCD 0x00 ID char4
IFCD 0x00 ID char5
IFCD 0x00 ID char6
IFCD 0x00 ID char7
IFCD 0x00 ID char8
IFCD 0x00 ID char9
*event*
ENIF ID

FE7 strings conditions together like this, like for example the ending scene of 19x where it checks for 19xx access. These should behave as if the conditions were and'd.

Edited by Nintenlord, 10 April 2012 - 09:36 AM.


#16 zahlman

zahlman

    Member

  • Member
  • Gender:Male
  • Favorite Fire Emblem Game:Blazing Sword

Posted 11 April 2012 - 02:56 AM

FE7 strings conditions together like this, like for example the ending scene of 19x where it checks for 19xx access. These should behave as if the conditions were and'd.


I thought there were only two conditions for 19xx?

#17 Nintenlord

Nintenlord

    Disciple of Harmony

  • Member
  • Gender:Male
  • Location:Finland
  • Favorite Fire Emblem Game:Blazing Sword

Posted 11 April 2012 - 04:02 AM

Erm, yes? If it works for two conditional codes then it should work for any N amount. The conditions in question go like this:

    IFHM 0x5
    IFAF 0x5 0x7D241
FADI 4
HIDEMAP
_0x87
BACG 0x5B
FADU 128
SHOWMAP
STAL 2
ASMC 0x15591
TEX6 0x7 [0,0] 0xB8E
_ASM0x42 0x83181
_0x89
    IFYN 0x6
MNCH 0x19
STAL 1
_0x1
    ELSE 0x9
    ENIF 0x5
FADI 4
HIDEMAP
BACG 0x5B
    ENIF 0x6
MNCH 0x1A
STAL 1
_0x1
    ENIF 0x9


#18 zahlman

zahlman

    Member

  • Member
  • Gender:Male
  • Favorite Fire Emblem Game:Blazing Sword

Posted 11 April 2012 - 08:01 PM

Oh, I see, you were just showing multiple conditions for demonstration purposes.

#19 CT075

CT075

    formerly shota supercomputer

  • Member
  • Gender:Male
  • Location:Somewhere
  • Favorite Fire Emblem Game:Radiant Dawn

Posted 12 April 2012 - 06:05 AM

I completely f'd up what JUMP can really do, I just can't be arsed to write up another explanation.

#20 Nintenlord

Nintenlord

    Disciple of Harmony

  • Member
  • Gender:Male
  • Location:Finland
  • Favorite Fire Emblem Game:Blazing Sword

Posted 22 April 2012 - 07:58 AM

Just so you guys know, next release will rename GOTO to CALL. EAStdlib will naturally have backwards compatibility define. Also, _GOTO_HELL is renamed as _CALL_HELL, which I hope remains reasonably funny.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users