[M][CE] HALLOWEEN HERO ●

Find and discuss trials made by other members and showcase your own trials.

Moderators: EN - Forum Moderators, EN - Trial Reviewers

User avatar
TimeAxis
Posts: 391
Joined: Fri Jan 01, 2021 8:27 pm
Spoken languages: English
Contact:

Re: [M][CE] HALLOWEEN HERO

Post by TimeAxis »

Grounder wrote: Mon Jan 09, 2023 6:22 am
TimeAxis wrote: Mon Jan 09, 2023 4:52 am Good news. I've completed the walkthrough. It can be found here or in the OP.
Spoiler : :
Nothing on Beatrice? Keeping it a secret, or a simple brain fart?
Spoiler : :
It was an intentional decision. If people want to share information between each other, they're free to. The walkthrough is for clearing the game.
Image
Question Arcs (Threads Coming Eventually)
ImageImageImageImage
Gaiden Episodes
Champion of Turnabouts ★
HALLOWEEN HERO
Other
Phoenix Wright: Ace Attornauts
The Curious Case of the Phantom Limousine (Coming Eventually)
The Imposter's Turnabout (Coming Maybe)
User avatar
risefromtheashes
Posts: 371
Joined: Wed Apr 04, 2018 2:33 am
Spoken languages: English, Russian, Armenian, Spanish

Re: [M][CE] HALLOWEEN HERO

Post by risefromtheashes »

Just played this now. Thank you for the "Man with the Machine Gun" remix. I love FF8 so much.

Oh and the true ending made me CACKLE but was also kind of wholesome. Thanks! The battle system, while simple, was clever with a good number of the enemies and I enjoyed things. Just a lot of floors haha.
The Mindcastle System
(Don't know what a system is? Play 6-4, and take a look here.)
----------
OUR CASES:
- Athena Cykes ~ Locks on the Heart (synopsis)
- May Your Memory Be a Blessing
- A Little Piece of Healing
- The Killer Turnabout (~70% complete!)
- I guess we made the Looking Back case comp ceremony with Super legenda, but that's not an actual case

----------
Check out our music & art thread here!
User avatar
TimeAxis
Posts: 391
Joined: Fri Jan 01, 2021 8:27 pm
Spoken languages: English
Contact:

Re: [M][CE] HALLOWEEN HERO ●

Post by TimeAxis »

The Making of HALLOWEEN HERO pt1

Since this is a pretty unique endeavor for AAO, I figured some people would be curious about how I accomplished certain parts of it. It would be a shame to let everything I learned while making it rot away in my own brain, so I decided to do a writeup sharing how I made this and explaining most of the design choices. I don't know if this writeup will be useful to anyone else, but I figured I'd write it anyway.

Spoiler : The Making of HALLOWEEN HERO :
Initial Design
When I started the project, there were three main things I established right away.
  1. It would be a Megami Tensei style dungeon-crawler, featuring SMT demons as enemies. (This eliminated the need for me to make a ton of custom enemy sprites, although I did have to arrange them into a format that worked for AAO.) You would use controls on the bottom screen to navigate.
  2. To tie the horror theme to the gameplay, there would be a part where a powerful enemy appears on the map outside of battle and chases you around. I decided early on that this would be an eyeball, which I modeled in blender.
  3. There would be multiple floors and they would be implemented in such a way that the maps (or at least some of them) could be procedurally generated.
As anyone who played it might be able to tell, number 3 got cut and never made it in entirely. It wasn't because it was impossible, but it would have taken a long time and I just didn't have enough time, so I went with manually designed levels, which were much easier to manage.

Once I had these things established, I created a main screen.
Image
This is when I decided on Candy as a currency/score metric for the game. As you can see, the HP was originally going to be signified by Hearts, but in the end, I went with a meter. The meter is just composed of a bunch of the same image of a thin white block lined up next to each other.
I also drew all the walls. Every wall is its own image, and the game logic controls when each wall should be visible (which I'll get into later). All together, not including doors and stairs and such, the walls look like this:
Image
I decided that seeing three tiles away was enough, as more than that would have gotten out of hand pretty quickly. If every wall is its own image, making one more tile back visible would have added at least 9 more images, and the screen would have gotten really crowded:
Image

The Map
The map was the first big puzzle I had to figure out. Map data isn't very complicated at the bare bones. All you really need is a grid of information for each tile, so the game knows whether it's a wall, or whether it's a walkable tile. But how in AAO could you store data on a grid of information? Theoretically, you could create a frame for every possible tile you could stand on, viewed from every possible direction, on every floor. But that would have been ridiculous and probably would have sent my frame count into the hextuple digit territory. I had to make it dynamic, as data that could be read in real time by the game. If you're familiar with programming, your first thought might be a 2D array (basically just an organized numbered list of variables). But AAO doesn't have arrays. So I was stuck, I thought, with somehow storing the info I needed in a single variable.

My first thought was to use binary. By storing the map data as a series of 1s and 0s, (1s being walkable tiles and 0s being walls), I could store a large amount of information in a simple number. For example, let's say I have a map that looks like this:

Code: Select all

11111
00001
01111
01000
01111
I could remove the linebreaks and store that as this number: 1111100001011110100001111. But here's the problem. AAO can't store numbers that long in its variables. I can't recall the exact limit, but I tested it and found that you could only have so many digits before AAO would start cutting them off or breaking. So that plan didn't work. Then I thought "What if I convert that binary number to decimal?" That same long string of 1s and 0s could be represented as the number 32554255 in decimal. I spent a while trying to create a system of converting numbers from binary to decimal and back in AAO, but then I realized the major flaw with this plan. The decimal number is useless to me if I actually want to get information out of it. Somehow I would need to convert that decimal number back to binary, which would mean I would need that long 25 digit (or 100 digit in the case of real maps in the game) number again. And there was another problem as well. 1s and 0s are all well and good when it comes to walls and floors, but what about other things, like an event tile where I want a cutscene to occur, or a stairway down to another floor, or a door? 1 and 0 weren't enough to convey that information. So I went back to the drawing board. Binary wasn't going to work.

The solution I came up with was crude, but it worked. I needed to store information on a 10x10 grid of tiles for each floor. That's 100 tiles at a time. So I just made 100 variables. map0, map1, map2 ... all the way to map99. Each variable had one of the following values:

Code: Select all

0: Wall
1: Floor
2: Door
3: Locked Door
4: Stairs Up
5: Stairs Down
A-Z: Various event tiles that do different things depending on which floor they're on.
But this came with its own problem. As a person with eyeballs, I can read the variable names and know which variable corresponds to which tile on the map. But how would the game know that? For example, let's say the player is at position 31, and they're facing east. The tile in front of them should be tile 32. How can I know to grab the value of map32 to check to see if it's a wall or a walkable tile? Here's where we start getting into some advanced AAO wizardry. When you use the "test an expression's value" action in AAO, in the advanced mode, there is a checkbox you can check under "Name of the variable to test". This lets you use variables to figure out the name of the variable you want to look at. If the player's position was stored in a variable called "p_pos", then I could use the following code to get the map variable containing the tile to the right of that player:

Code: Select all

'map' . (p_pos + 1)
The dot in that line causes you to "concatenate" (meaning that you're taking the number you get from p_pos + 1, and appending it to the end of the word 'map' so that the game reads it as 'map32'). This trick was a lifesaver and was used extensively throughout Halloween Hero, but it didn't work all the time. (More on that later.)

Even still, that on its own isn't enough. We've still got another problem. I want the tile in front of the player, but how do I know the tile in front of them is their current position + 1? That only works when they're facing east (and not when they're facing the edge of the map, such as in position 39. 39 + 1 would be 40, which would be a tile on the complete other side of the map). Look at this visual arrangement of map positions to see what I mean:

Code: Select all

[00][01][02][03][04][05][06][07][08][09]
[10][11][12][13][14][15][16][17][18][19]
[20][21][22][23][24][25][26][27][28][29]
[30][31][32][33][34][35][36][37][38][39]
[40][41][42][43][44][45][46][47][48][49]
[50][51][52][53][54][55][56][57][58][59]
[60][61][62][63][64][65][66][67][68][69]
[70][71][72][73][74][75][76][77][78][79]
[80][81][82][83][84][85][86][87][88][89]
[90][91][92][93][94][95][96][97][98][99]
So first I had to figure out which way the player was facing, which was easy enough to store in a variable, which I called "p_dir". It could either be N, E, S, or W. Let's say the player is at position 21. If the player is facing North, then the tile in front of them would be 11, which means you just need to subtract 10. Pretty simple. If they're facing East, you add 1, if they're facing west, you subtract 1, and if they're facing south, you add 10. Then I just need to take into account whether the player is facing the edge of the map or not. If they're facing west, and they're in the 0s column, then you don't want to check -1, because then you'd get a tile in the 9s column on the other side of the map. Likewise, if we're in the top row and facing north, we can't subtract 10, or we'd get a number less than 10 that's not even on the map. So taking all of this into account, I came up with the following formula:

Code: Select all

((p_dir = 'N' & (p_pos > 9)) & 'map' . (p_pos - 10)) | ((p_dir = 'E' & (p_pos % 10) < 9) & 'map' . (p_pos + 1)) | ((p_dir = 'S' & (p_pos < 90)) & 'map' . (p_pos + 10)) | ((p_dir = 'W' & (p_pos % 10) > 0) & 'map' . (p_pos - 1)) | alwayszero
This probably looks like absolute gibberish to most people, but it's not too hard to read once you understand one little AAO trick, as well as some programming symbols. This one is thanks to Ferdielance as well as Enthalpy's v6 Variable Guide.
This is basically a fancy way of making an "if" statement (for example, "if there are clouds in the sky, it's going to rain, or else it won't rain"), and unfortunately the only way in AAO to do so in one line without using a whole bunch of frames. Here's how it works:

Code: Select all

((clouds are in the sky) & 'Its gonna rain') | 'No rain'
The game hits this line, and it's trying to figure out whether it's going to rain or not. First off we have a "|" symbol separating multiple possible outcomes. This symbol means "OR". Either 'Its gonna rain' OR 'No rain'. But using the "&" (AND) symbol, we've added a condition to 'Its gonna rain' so that outcome will only happen if 'clouds are in the sky'. On the other hand, 'No rain' doesn't have any conditions attached to it. So if there are no clouds in the sky, this statement is going to choose the outcome that doesn't require there to be clouds in the sky, hence, 'No rain'.

Going back to the formula with this in mind, the game is trying to figure out the name of the variable we want it to read, remember? We want the tile in front of the player. So our first outcome is:
((p_dir = 'N' & (p_pos > 9)) & 'map' . (p_pos - 10))
In other words, if the player's direction is North AND, if their position is greater than 9, then return 'map' . (p_pos - 10). If those conditions aren't met, it moves onto the next possible outcome:
((p_dir = 'E' & (p_pos % 10) < 9) & 'map' . (p_pos + 1))
If the player's direction is East, AND if the remainder (that's the % symbol) of their position divided by 10 is less than 9 (which means they're not in a column ending in 9), then return 'map' . (p_pos + 1).
It then goes through all the other possible outcomes and their conditions, until it gets to the final position where if none of these conditions are true (which means we're checking a tile that's outside of the map, which we want to return 0 as a wall, so players can't walk off the map), it returns a variable called "alwayszero" which I set at the beginning of the game to always have a value of 0. We can't just put the value 0 there, because we're looking for a variable name to read.

So with that, we've got a map, and we can read its data and do stuff with it. But there is one wrinkle. Dynamically generating the name of a variable to read only works in a few circumstances. When you're in "Define new variables", "Test an expression's value", or "Evaluate conditions to redirect player." You need that checkbox that lets you use an expression to create the expression that's going to be read. You can't do this, say, in "Reveal/Hide an object in a place". That's going to be a problem, as you'll see in this next part.

Actually Displaying the Map
The core of the game's ability to display things comes from using "objects" in places. You know, like when you add a picture of a piece of evidence to a crime scene. Well, my entire top screen is basically one crime scene, and every single possible object that could be displayed on the screen is an object. Here's what they look like with most of them displaying at once:
Image
There are a total of 36 background objects (representing the walls and 3D parts of the screen), and a total of 763 foreground objects (representing the mini-map, all the possible numbers on the candy counter, as well as the HP bar). The vast majority of those objects are minimap images. Because for every single possible icon that can appear on the map (floors, stairs up, stairs down, doors, eyeballs, and black squares to cover spaces that haven't been seen yet), that's another 100 objects. There's no ability in AAO to move objects, so every single image needs to be there in every single possible position. No way around that, unfortunately.

But if I actually tried to use AAO as it was intended and used "Evaluate conditions to redirect player" frames to go to various frames and hide or show various objects on the screen every time I needed to (so every time the player moves), the game would have been slow as molasses. So the trick I use to try to show or hide objects in as few frames as possible is the one listed above.

First off, I added a background object and foreground object that's just a transparent image called BLANK. Then, for every single possible object that could be displayed, I wrote out the conditions under which they should be displayed in one frame, and the conditions they should be hidden in another frame. For example, here is the formula for drawing the first tile of the minimap, which is the first foreground object I added, so it has an ID of 1, and corresponds to the variable map0.

Code: Select all

((map0 = '1' | map0 = 'A' | map0 = 'B' | map0 = 'C' | map0 = 'D' | map0 = 'E' | map0 = 'F' | map0 = 'G' | map0 = 'H' | map0 = 'I' | map0 = 'J' | map0 = 'K' | map0 = 'L' | map0 = 'M' | map0 = 'N' | map0 = 'O' | map0 = 'P' | map0 = 'Q' | map0 = 'R' | map0 = 'S' | map0 = 'T' | map0 = 'U' | map0 = 'V' | map0 = 'W' | map0 = 'X' | map0 = 'Y' | map0 = 'Z') & 1) | 604
Basically what this code is saying is "If map0 has a value of either "1" or any of A through Z, then we can assume that the tile at position 0 is walkable, and should be displayed on the minimap, so return the ID "1" as the object to display. If not, then it's not walkable, so we don't want it to display, so we'll instead return "604" which is the ID of the "BLANK" image I added earlier. It will reveal the blank image, but the player won't see it, so it won't matter.

Unfortunately for me, "map0" is entered manually here. Because this is an expression that's determining which ID of object to reveal, and not an expression that determines which variable name to read, we can't use the trick from earlier. We have to enter the right variable name manually. So for tiles 2 and 3, I entered this:

Code: Select all

((map1 = '1' | map1 = 'A' | map1 = 'B' | map1 = 'C' | map1 = 'D' | map1 = 'E' | map1 = 'F' | map1 = 'G' | map1 = 'H' | map1 = 'I' | map1 = 'J' | map1 = 'K' | map1 = 'L' | map1 = 'M' | map1 = 'N' | map1 = 'O' | map1 = 'P' | map1 = 'Q' | map1 = 'R' | map1 = 'S' | map1 = 'T' | map1 = 'U' | map1 = 'V' | map1 = 'W' | map1 = 'X' | map1 = 'Y' | map1 = 'Z') & 2) | 604
((map2 = '1' | map2 = 'A' | map2 = 'B' | map2 = 'C' | map2 = 'D' | map2 = 'E' | map2 = 'F' | map2 = 'G' | map2 = 'H' | map2 = 'I' | map2 = 'J' | map2 = 'K' | map2 = 'L' | map2 = 'M' | map2 = 'N' | map2 = 'O' | map2 = 'P' | map2 = 'Q' | map2 = 'R' | map2 = 'S' | map2 = 'T' | map2 = 'U' | map2 = 'V' | map2 = 'W' | map2 = 'X' | map2 = 'Y' | map2 = 'Z') & 3) | 604
And I continued this all the way to tile 100.

Did I enter all of these things manually? Well, I very well might have done so if it weren't a few very severe inconveniences. Number 1, you can't just press tab to move to the next field, you need to use the mouse to click on the field or else your browser will think you're tabbing into other AAO fields from outside the action window. And number 2, it wasn't just the object ID field that needed to be filled out. For every single one, all of these fields had to be filled out:
Image
And in particular, the field that says 'foreground_objects' by default doesn't have single quotes around it, which will break the game. The alternative would be not using advanced mode and manually using the mouse to select the correct map every single time, which would have been even more of a nightmare. So I needed to go in and add those single quotes manually. I am not nearly enough of a masochist to do all of that by hand. So I did what any programmer would do, and found a way to automate it.

I downloaded autohotkey and wrote up scripts to automatically enter the fields for me. This ranged from anywhere between 15-minutes to an hour of my computer being unusable as it moved the mouse on its own to click on fields and type up their contents. But believe it or not, displaying the minimap was the easy part. Where it got really tricky was displaying the walls and 3D objects. You'd think the minimap would take longer, with there being over 700 minimap objects and only 35ish wall objects, right?

Well, here's the issue. Because I can't use the trick from earlier to get the exact map variable I need for any given tile, I can't just use the same formula from earlier to determine whether the tile in front of the player is a wall and should be displayed as a wall. Or rather, I could. but it would have taken way too many frames and been very slow. Instead, I needed to tackle it from a different direction. There are 100 possible map variables to check, but only 1 variable for the player's location. So here's an example of what I came up with:

Code: Select all

(((map0 = '0' | map0 = '2' | map0 = '3' ) & p_dir = 'N' & p_pos = 10) & 1) | (((map0 = '0' | map0 = '2' | map0 = '3' ) & p_dir = 'E' & p_pos = -1) & 1) | (((map0 = '0' | map0 = '2' | map0 = '3' ) & p_dir = 'S' & p_pos = -10) & 1) | (((map0 = '0' | map0 = '2' | map0 = '3' ) & p_dir = 'W' & p_pos = 1) & 1) | 19
This code is checking whether the tile directly in front of the player is a wall or not (if map0 is equal to either 0, 2, or 3, which all represent tiles in which a wall must be displayed, and if the player is standing in position 10 while facing North, and so on), in which case the first background image (id 1) representing a wall directly in front of you will be displayed. If not, then object 19, the id of the "BLANK" background image will be displayed instead.

The more astute of you may have noticed something horrifying, however. This formula says "map0" and specifies specific player positions as part of its conditions. The implications of this are exactly as you might imagine. For every single possible wall image that could be displayed, I wrote up a formula for each and every possible position that wall could be in (that being 100 possible positions), and then checked whether the player was in the correct position to see that wall. We're not talking about 35 formulas. We're talking about 3500.

There was literally no possible way I could ever hope to do this manually. Naturally, I automated it, but even the process of waiting for my computer to enter all of those formulas on its own was arduous and tedious. Each wall image would take hours to completely fill out, and after a certain point, my browser started running out of memory and crashing, causing me to have to start over. (Thankfully I saved every time I completed 100 of them, so it wasn't starting over completely from scratch) This happened at least 10 times, although I wasn't counting. I really wanted to squeeze all of those formulas into 1 frame, but in the end, it wasn't possible. It was too much for my browser. I had to split it into 2 frames, so technically you might notice the doors and some other objects in the game appear 1 frame later than the rest of the walls. A small sacrifice, but one that needed to be made.

Finally, there was the matter of hiding parts of the minimap that you haven't walked on yet. This was surprisingly easy, although there was one unfortunate limitation. If I wanted to do it the correct way, I would have had to store each tile the player had walked on, on each floor. But in AAO, that would have meant 100 more variables for every single floor. In the end, I was already pushing it with the number of variables as it was, so I decided against that. Instead, I would just reveal the full map depending on whether you've cleared a floor or not, and then reset the map if you left the floor. I mean, you can't expect everything to go perfectly when AAO isn't designed for something like this.

So what I did was have black square objects cover the minimap, and revealed all of them in 1 frame, then hid specific ones based on the player's position whenever they moved. No variables required.
I'm not sure whether people would be interested in hearing more, or if this kind of thing is even interesting to anyone. If there's anything specific people would like to hear about how I did, let me know and I can write about that.
Image
Question Arcs (Threads Coming Eventually)
ImageImageImageImage
Gaiden Episodes
Champion of Turnabouts ★
HALLOWEEN HERO
Other
Phoenix Wright: Ace Attornauts
The Curious Case of the Phantom Limousine (Coming Eventually)
The Imposter's Turnabout (Coming Maybe)
Trybien
Posts: 3052
Joined: Sat Oct 17, 2009 12:20 am
Gender: Male
Spoken languages: English

Re: [M][CE] HALLOWEEN HERO ●

Post by Trybien »

Really interesting stuff in there. You mentioned autohotkey stuff during the competition, and it makes sense why.
Spoiler : :
The map creation in general is quite brilliant, and I had a feeling the the map revealing worked like that.
Thanks for the share, enjoyed learning more about one of my favourite things on the site.
Image
Winner of the “Broken Commandments” Case Competition
User avatar
DeathByAutoscroll
Posts: 171
Joined: Sat Mar 12, 2022 7:00 pm
Gender: Female
Spoken languages: English
Location: Outside your home

Re: [M][CE] HALLOWEEN HERO ●

Post by DeathByAutoscroll »

Just finished playing this... truly there is a wizard among us. If you haven't already, go play and complete this!
Spoiler : Thoughts :
Without question this is an incredible feat, with TimeAxis making a full fledged RPG despite the AAO engine, and having it be fun, interesting, and introduce a cool mechanic. The resistance blocking separate from the attack is a really cool idea, though that does lead to exploitation later on in the game once you gain more candy than it costs to cast DIA, and can farm candy to 999 quite easily.

Thats pretty much the only bug I could find while playing, which is really impressive.

The eye really spooked me when it first appeared, and seeing it chase me on the minimap was fairly scary for what had been a pretty chill engaging RPG up to that point, and the terror from it never really faded as I moved down the floors.
Spoiler : Endings :
The Normal ending was quite satisfying to get, having to figure out the puzzle boss of the Doppleganger (who kicked my ass the first time), and seeing the kid escape. I orginally thought it was going to by like Monty Python and the Holy Grail... but then I went for the true ending.
Spoiler : True ending :
Good lord Beatrice is tough, with the constant elemental shuffles. She utterly owned me the first attempt, but the second attempt she got stuck on physical and casting DIA so I can't really give advice to anyone on how to beat them.

Seeing godot just appear in what was a really, really good moment, and the story afterwards helped to wrap everything up really nicely... and act as a reminder that this was all done in an AA engine.
I would love to see more DevLogs on how you pulled off certain effects within the case, seeing the minimap and level display breakdown was wild.

This might be kind of basic for a full breakdown, but how do random encounters work, alongside the elemental resistance battle mechanic?
Thrower of bricks.

Co-host of the Into the Takumi-verse case compeition.

Stuff I've made in 2 weeks:
The Impossible Turnabout
Erinaceinae Griminance

Cases I have collabed on:
Don't Resort to a Turnabout (W.I.P)
Trucy's Magical Catastrophe
That time I got reincarnated as a fictional Defence Lawyer in An Ace Attorney fangame and had to defend myself against incredibly unfair odds.

Stuff I've made by myself that is good:
...maybe in the future.
User avatar
TimeAxis
Posts: 391
Joined: Fri Jan 01, 2021 8:27 pm
Spoken languages: English
Contact:

Re: [M][CE] HALLOWEEN HERO ●

Post by TimeAxis »

Spoiler : :
Glad you enjoyed it, although I don't know if I'd call that exploit a bug. It's definitely a design flaw I suppose, but it's one that I balanced the game around since I couldn't really think of a way to avoid it. Having spells cost more as the game went on would have felt bad, and having them not scale with your stats would have made them useless later on. Having upgraded versions would have been a bit beyond the scope of what I was willing to do for this project. By the time you pass the point where you basically have infinite healing, that's when I started throwing gimmicks into the battles. In the end, some enemies like the Goats were just designed to be EXP fodder to prepare you for Beato (who you're expected to have maxed stats for). The gameplay in real fights mostly revolved around finding time to heal between attacks.
Well, since you asked for it, here's another devlog.
The Making of HALLOWEEN HERO pt2
Spoiler : The Making of HALLOWEEN HERO :
Character Naming
Naming your character was also something I decided on early on, but it was trickier than you'd think. I didn't want to just let the player type in a name, because that would be really messy and wouldn't fit the feel I was going for. I wanted to be able to control the max length of their name so that it wouldn't mess with text boxes too much. In the end I went with a graphical UI.
Image
The naming "place", as you might expect, consisted of objects for all the letters A to Z, which were hidden or revealed as needed as the player clicked on the up and down arrows. Each letter in the name was stored in a separate variable, then those variables were parsed into the name and stored in a new variable for the name itself. I had to take into account things like whether there were spaces in the name. If spaces were at the beginning or end, they would be ignored, but you could have a space in between words. I didn't want to include another 156 images to allow lowercase letters, so I decided to keep the name in all caps for that retro feel. That's also the reason I ended up capitalizing random words in the text, as well as other names, so that the player's name wouldn't look too out of place always in caps. One fun thing most people might not notice that was actually pretty involved as well, was a certain moment where the player character's name is shouted and the last letter trails off. What I did there was make a variable called name_yelling, and gave it a value based on this formula:

Code: Select all

((f:str_ends_with(playerName,'A')) & 'AAAAAAAAAAAAAAAAAAAAAAAAAAA ! ! !') | ((f:str_ends_with(playerName,'B')) & 'BBBBBBBBBBBBBBBBBBBBBBBBBBB ! ! !') | ((f:str_ends_with(playerName,'C')) & 'CCCCCCCCCCCCCCCCCCCCCCCCCCC ! ! !') | ((f:str_ends_with(playerName,'D')) & 'DDDDDDDDDDDDDDDDDDDDDDDDDDD ! ! !') | ((f:str_ends_with(playerName,'E')) & 'EEEEEEEEEEEEEEEEEEEEEEEEEEE ! ! !') | ((f:str_ends_with(playerName,'F')) & 'FFFFFFFFFFFFFFFFFFFFFFFFFFF ! ! !') | ((f:str_ends_with(playerName,'G')) & 'GGGGGGGGGGGGGGGGGGGGGGGGGGG ! ! !') | ((f:str_ends_with(playerName,'H')) & 'HHHHHHHHHHHHHHHHHHHHHHHHHHH ! ! !') | ((f:str_ends_with(playerName,'I')) & 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII ! ! !') | ((f:str_ends_with(playerName,'J')) & 'JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ ! ! !') | ((f:str_ends_with(playerName,'K')) & 'KKKKKKKKKKKKKKKKKKKKKKKKKKK ! ! !') | ((f:str_ends_with(playerName,'L')) & 'LLLLLLLLLLLLLLLLLLLLLLLLLLL ! ! !') | ((f:str_ends_with(playerName,'M')) & 'MMMMMMMMMMMMMMMMMMMMMMMMMMM ! ! !') | ((f:str_ends_with(playerName,'N')) & 'NNNNNNNNNNNNNNNNNNNNNNNNNNN ! ! !') | ((f:str_ends_with(playerName,'O')) & 'OOOOOOOOOOOOOOOOOOOOOOOOOOO ! ! !') | ((f:str_ends_with(playerName,'P')) & 'PPPPPPPPPPPPPPPPPPPPPPPPPPP ! ! !') | ((f:str_ends_with(playerName,'Q')) & 'QQQQQQQQQQQQQQQQQQQQQQQQQQQ ! ! !') | ((f:str_ends_with(playerName,'R')) & 'RRRRRRRRRRRRRRRRRRRRRRRRRRR ! ! !') | ((f:str_ends_with(playerName,'S')) & 'SSSSSSSSSSSSSSSSSSSSSSSSSSS ! ! !') | ((f:str_ends_with(playerName,'T')) & 'TTTTTTTTTTTTTTTTTTTTTTTTTTT ! ! !') | ((f:str_ends_with(playerName,'U')) & 'UUUUUUUUUUUUUUUUUUUUUUUUUUU ! ! !') | ((f:str_ends_with(playerName,'V')) & 'VVVVVVVVVVVVVVVVVVVVVVVVVVV ! ! !') | ((f:str_ends_with(playerName,'W')) & 'WWWWWWWWWWWWWWWWWWWWWWWWWWW ! ! !') | ((f:str_ends_with(playerName,'X')) & 'XXXXXXXXXXXXXXXXXXXXXXXXXXX ! ! !') | ((f:str_ends_with(playerName,'Y')) & 'YYYYYYYYYYYYYYYYYYYYYYYYYYY ! ! !') | ((f:str_ends_with(playerName,'Z')) & 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZ ! ! !') | ' ! ! !'
Ideally I could have added some formatting like line breaks to make it so that the whole string still displays rather than trailing off the screen, but I did some testing and found that if you display the value of a variable and it contains a string with formatting, that formatting breaks when you have text set to display instantly, and I can't control whether players use that setting or not, so I just kept it simple. I could have also made it a bit more complex and gone for detecting whether there was a vowel near the end of the name that I could have used instead of just repeating the last letter, but that would have raised the complexity a lot for just a single line, so I opted against it.

Stats & Math
Coming up with which stats to use was fairly simple. I went for the basics for pretty much any RPG. Like the naming screen, the stats screen consisted of objects for all the letters of the player's name, all the possible numbers to display their level, as well as blips to display their stats.
Image
The real hard part about the stats was the math, because of one very annoying limitation of AAO: All numbers are always integers. You can't multiply something by 0.5. The closest you can do is divide it by 2. But even then, the result will be converted to an integer, not by rounding, but by simply cutting off all the decimal places with the floor function. In other words, 5/2 should be 2.5, but instead of being rounded up to 3, it becomes 2. Even if it was 2.9, it would always become 2. One workaround I found to this was to multiply a number by 10 times the number of decimal places I wanted, then I could divide by that number afterward. For example, instead of 5 / 2, I could do 50 / 2 to get 25. This would be useful for not losing precision in the middle of complicated calculations. But ultimately, that 25 would still have to be divided by 10 and become 2 in the end.

It took quite a lot of testing and tinkering for me to come up with good formulas that produced the kinds of curves I wanted. In the end, the formula for determining the player's max HP based on their HP stat looks like this:

Code: Select all

((929 * (p_stat_HP * p_stat_HP))/100 + (12210 * p_stat_HP)/100 - 3140/100)/10
I used a lot of online graphing calculators to come up with those numbers, but the flooring really put a wrench in things and made them harder than they needed to be. In the end I'm pretty happy with what I ended up with, though. The goal was for it to produce a value of 10 at 1 HP stat, and a value of around 1000 (or 1199 to be exact) at 30 HP. I think that gave enough leeway to make upgrading your HP stat feel worthwhile. There was also a similar process to creating the EXP curve.

Ideally, I would have liked to mostly use single and double digit damage values, but I had to use values that high, because of AAO's lack of decimals and thus lack of precision. The damage formula looks like this:

Code: Select all

((attacker_atk * (1+(attacker_lvl/2)) * (1+(attacker_atk/2)) * (1+(attacker_atk/3)) + 40) / ( (defender_def/2) + defender_lvl + (defender_def + (1+(defender_def * 2))) )) + attacker_atk + f:random_int(0,(attacker_lvl + defender_lvl)/2)
Initially it would have been as simple as subtracting the enemy's DEF from your ATK, but that ended up not feeling good to play. It was way too easy to get too much attack and destroy everything, or too much defense and be invincible. I think I struck just the right balance with this. I ended up looking up a lot of different games' damage formulas for reference, but almost none of them could be used directly, because of AAO's lack of decimals. All of this would have been about 10x easier if you could just use real numbers in AAO instead of just integers.

Functions & Random Encounters
Random encounters were actually pretty simple to implement once I got battles and movement working. I'll do another post on battles at some point, because there's a lot to go into there, but to explain how random encounters work, I'll first need to explain functions, which are a technique I picked up while making Champion of Turnabouts, and have since started using in all of my cases that use complicated game logic.

Technically this isn't a "feature" of AAO, it's just a way of organizing frames that makes it more convenient to do certain things. What I like to do is take any series of frames that I'm going to want to use a lot, and put it inside an Investigation scene that the player will never visit. This serves two purposes. The first is that I can easily refer back to it whenever I want. And the second is that it allows you to have a ton of frames in your case without slowing down the editor by displaying them all at once. I call these organized groups of frames "Functions" and I give them a name that's formatted as (number):(Function name), with the number being the first frame of that function that I'm going to want to jump to in order to use it. Here's an example:
Image

In order for Random Encounters to work, every time the player moves, the "EncounterTick" function is called. In other words, the player is sent to frame 494. The frame they came from is usually stored in a variable called "return_frame" so that they can be sent back there after going through all the frames in the function. In this function, there is a variable called "encounterRate", which starts at 20, and then decreases by 1 every time you take a step. As you move, and the EncounterTick function is called, it generates a random number between 1 and "encounterRate", and if that number is 1, then it triggers a battle. The more you move, the higher the chance that number has of being 1. Then once a battle happens, it resets back to 20. I've found that 20 is a pretty reasonable rate that doesn't result in too many random encounters.

As for how it's determined which enemy you'll fight, once it's been determined that there should be a random encounter, there is a "Test an expression's value" frame that checks which floor you're on. Each floor has a frame that defines its encounter table, which will randomly spit out an enemy to be fought. These are often weighted to make certain enemies less common than others. I could go into exactly how battles work and how enemies and attacks and all that stuff work, but it would take a whole other writeup, so I'm not going to do that in this one. Hope that explained things well enough for now.
Image
Question Arcs (Threads Coming Eventually)
ImageImageImageImage
Gaiden Episodes
Champion of Turnabouts ★
HALLOWEEN HERO
Other
Phoenix Wright: Ace Attornauts
The Curious Case of the Phantom Limousine (Coming Eventually)
The Imposter's Turnabout (Coming Maybe)
User avatar
TimeAxis
Posts: 391
Joined: Fri Jan 01, 2021 8:27 pm
Spoken languages: English
Contact:

Re: [M][CE] HALLOWEEN HERO ●

Post by TimeAxis »

Rebellious double post because I had a bit more to say about this project. It's been out for a while, so this one will go into the story a bit. So it will contain spoilers, beware. There's also not really any interesting info about the editor stuff in this one. I can still answer any questions anyone has about how I did stuff, but I don't really feel like doing a whole writeup over any more of that stuff.

The Making of HALLOWEEN HERO pt2.0 + 1.0
Spoiler : The Making of HALLOWEEN HERO :
Horror Theme
I'm not a horror fan, so when the theme of the competition was horror, I was at a bit of a loss. Not for any special reason, I just have very little background with the genre, and the common tropes in horror don't tend to resonate with me as "scary" much. I thought a while about what kinds of things actually scared me, and figured it would be easier to just do something more lighthearted and Halloween themed. And part of why I haven't addressed the secret ending up until now, not including it in the walkthrough, is that I want to keep that aspect of the game in tact. For people who played through the game to the normal ending, found it a fun silly thing and had a good time and felt satisfied, I'm totally okay with that and don't want to ruin it for those people. So if that's you, don't read further.

So after I thought about doing the lighthearted thing, I also started to think on things that actually scare me. And I came upon the one thing that probably horrifies me the most out of anything. That would be the idea of your senses not being reliable. We take senses for granted, and trust that what we're seeing and hearing is what's actually happening, but I've always had this kind of nightmare fear at the back of my mind of thinking you're doing one thing, but you're actually doing another. Imagine, like, you're going to the bathroom, and making all the motions of doing so, but you're not actually in the bathroom. Or you're eating something, but it's not what you think it is. I've read a few stories that have played with this idea, and figured it would make for a good second layer to this project, so it could be both real horror and more light-hearted horror at the same time.

There was also supposed to be a bit of gameplay horror in the sense of the eyeballs chasing you, but in practice, I'm not super happy with how that turned out, as being able to save and load, or continue from where you left off means that getting caught by one of them just means your time gets wasted. It's also one of the reasons I don't really like Horror games. That idea was inspired by Etrian Odyssey's F.O.Es, though.

It may be sort of an elephant in the room, but this case is technically an Umineko crossover. Although it wasn't designed with Umineko fans in mind at all. I've characterized the witch Beatrice in my crossover works as mostly just a shit disturber who likes to tip a few dominoes and watch the chaos unfold, with no real relation to the original work. There's also a bit of a meta question as to whether she even existed in this story to begin with. I'd like to keep that ambiguous, but that meta question is thematically important to the stories I include her in. Some ace attorney crossover stuff also comes in at the end. Canonically, this case would take place concurrently around the middle of AA3. But none of that is really necessary to think about much.

One worry I had about this case is the whiplash between the fun, light-hearted halloween stuff and the darker implications of the secret ending. If you really think about it, this is technically a story about a mass-shooting, which... is probably not something to dwell on too much. My goal wasn't for it to be edgy, so I hope the execution of it held it together in a sort of "macabre humor" way. Part of the concept was inspired by the "Grail-Kun" skit from Carnival Phantasm, so I hope I captured a bit of that energy, rather than making it too depressing.

Actually, when I originally came up with this idea, I had planned on potentially having a Part 2 where there's an actual trial for what happens in this case, but I felt like overexplaining the details of the case would be a lot less compelling than leaving certain things ambiguous, and playing a bit more into the horror of the unknown.

For that same reason, it's probably best that I cut this write-up short and let people have their own interpretations. Overall this was a fun project and I'm glad I made it. Hope people had a good time with it.

Oh, also, a bit of a bonus, but the default name “Haro” comes from “Halloween”. Specifically, I was imagining how this game would be abbreviated if it were an old RPG in Japan (like “DraQue” for example) and settled on “HaroHero”. There is a mascot character in Gundam called Haro, who I wasn’t really intending to reference, but I do love Gundam, so if you want to think of the name as a Gundam reference, I don’t mind. There's no real significance to the default name though, like it's not "canon" or anything, I just wanted there to be a default, and didn't want it to be gendered.
Last edited by TimeAxis on Sun Apr 09, 2023 7:28 pm, edited 2 times in total.
Image
Question Arcs (Threads Coming Eventually)
ImageImageImageImage
Gaiden Episodes
Champion of Turnabouts ★
HALLOWEEN HERO
Other
Phoenix Wright: Ace Attornauts
The Curious Case of the Phantom Limousine (Coming Eventually)
The Imposter's Turnabout (Coming Maybe)
User avatar
The Fury Wraith
Posts: 444
Joined: Sun Oct 10, 2010 2:41 pm
Gender: Male
Spoken languages: English, Dutch
Location: Between a rock and a hard place

Re: [M][CE] HALLOWEEN HERO ●

Post by The Fury Wraith »

I'm quite enjoying the director's commentary here. (More authors should do this, imo) I'm less interested in the intricacies of the programming, but the thoughts that went into design and story do quite interest me!

Also, spoilers for the game below:
Spoiler : :
Since TimeAxis just mentioned the Seihai-kun skit from Carnaval Phantasm, the following skit just popped into my head as a result:

Haro: ''Seihai-kun!! Seihai-kun!!''
Seihai-kun: ''What's wrong, Haro-kun?''
Haro: ''I got into a car accident and now I have to spend my Halloween in the hospital! Now I won't be able to celebrate Halloween outside with the rest of the kids!!!''
Seihai-kun: ''You're so hopeless, Haro-kun...''
Drops knife
🔪 HALLOWEEN CELEBRATION KIT!! 🔪
Haro: ''...''
Seihai-kun: ''You'll be able to celebrate Halloween if you just kill everyone in the hospital and get out.''
Case made by me: Turnabout Retribution
User avatar
Ferdielance
Posts: 778
Joined: Sun Mar 09, 2008 12:46 am
Gender: Male
Spoken languages: English

Re: [M][CE] HALLOWEEN HERO ●

Post by Ferdielance »

I could remove the linebreaks and store that as this number: 1111100001011110100001111. But here's the problem. AAO can't store numbers that long in its variables. I can't recall the exact limit, but I tested it and found that you could only have so many digits before AAO would start cutting them off or breaking. So that plan didn't work. Then I thought "What if I convert that binary number to decimal?" That same long string of 1s and 0s could be represented as the number 32554255 in decimal. I spent a while trying to create a system of converting numbers from binary to decimal and back in AAO, but then I realized the major flaw with this plan. The decimal number is useless to me if I actually want to get information out of it. Somehow I would need to convert that decimal number back to binary, which would mean I would need that long 25 digit (or 100 digit in the case of real maps in the game) number again. And there was another problem as well. 1s and 0s are all well and good when it comes to walls and floors, but what about other things, like an event tile where I want a cutscene to occur, or a stairway down to another floor, or a door? 1 and 0 weren't enough to convey that information. So I went back to the drawing board. Binary wasn't going to work.
Did you ever see Endless Nine? It is nine frames long, and photobucket has wrecked it. I should probably fix at some point.

http://www.aaonline.fr/player.php?trial_id=60063

Like you, I used some automatic code generation. However... I did represent each row as a single number, in decimal. For example, variable r1 was: 99999909999999. This allowed for things like doors, NPCs, etc. Modulo arithmetic made it straightforward to split rows and check adjacent cells.

Anyway, I'm going to count you as the LONG-AWAITED winner of the Endless Nine challenge!
"A slow sort of country!" said the Queen. "Now, here, you see, it takes all the running you can do, to keep in the same place. If you want to get somewhere else, you must run at least twice as fast as that!"
User avatar
TimeAxis
Posts: 391
Joined: Fri Jan 01, 2021 8:27 pm
Spoken languages: English
Contact:

Re: [M][CE] HALLOWEEN HERO ●

Post by TimeAxis »

I never saw that before, but it’s really cool. Nice to see I wasn’t the first to attempt something like this. Your contributions to the variable guide really helped out though.

The main reason I didn’t go that route with the numbers is because with floors, walls, doors, locked doors, stairs up, and stairs down, that would only leave 4 slots for events/NPCs. By not having it be a number, I had room for a lot more. Theroretically I could have up to 26 different events per floor, plus more if I used special characters. I think the most I used on any floor was 6 or 7 though.

EDIT: I looked back on that challenge. I don't think I passed it in the strictest sense, cause my case is way more than 9 frames long. I guess I theoretically could make one of that length, if I cut out most of the meat in this one, but I'm not especially thrilled by the prospect of doing something like this again. But hey, if I'm counted as the winner anyway, I'll accept the honor.

I also see that you did yours in a way more sane way, by editing trial data directly instead of using the editor.
Image
Question Arcs (Threads Coming Eventually)
ImageImageImageImage
Gaiden Episodes
Champion of Turnabouts ★
HALLOWEEN HERO
Other
Phoenix Wright: Ace Attornauts
The Curious Case of the Phantom Limousine (Coming Eventually)
The Imposter's Turnabout (Coming Maybe)
User avatar
TimeAxis
Posts: 391
Joined: Fri Jan 01, 2021 8:27 pm
Spoken languages: English
Contact:

Re: [M][CE] HALLOWEEN HERO ●

Post by TimeAxis »

I have (I hope) successfully migrated this trial's images away from imgur. I have not extensively tested this, but the loading bar was green and the intro seemed to load properly. I ran through the game once and everything seemed to work correctly. If anything is broken, let me know. Unfortunately, as it's technically a new trial, your existing saves will no longer work.

It actually didn't take that long using Ferdielance's tool and then a quick Find & Replace to change the links. I was also able to download all the imgur files easily by going to Page Info in firefox, selecting the media tab, and selecting all imgur images and saving them all at once. It would have been a nightmare to change all the links by hand so I'm glad that worked. Note for anyone else who wants to try this method, it'll only work if you use a hosting method that doesn't change the filenames, and has a consistent URL for all the files.
Last edited by TimeAxis on Thu May 18, 2023 3:03 am, edited 1 time in total.
Image
Question Arcs (Threads Coming Eventually)
ImageImageImageImage
Gaiden Episodes
Champion of Turnabouts ★
HALLOWEEN HERO
Other
Phoenix Wright: Ace Attornauts
The Curious Case of the Phantom Limousine (Coming Eventually)
The Imposter's Turnabout (Coming Maybe)
Trybien
Posts: 3052
Joined: Sat Oct 17, 2009 12:20 am
Gender: Male
Spoken languages: English

Re: [M][CE] HALLOWEEN HERO ●

Post by Trybien »

Incredible turn around. I'm glad the export/import trick worked!
Image
Winner of the “Broken Commandments” Case Competition
User avatar
CodingAnt
Posts: 74
Joined: Thu Jul 16, 2020 6:02 pm
Gender: Female
Spoken languages: Just English, sadly.
Location: Somewhere over the rainbow

Re: [M][CE] HALLOWEEN HERO ●

Post by CodingAnt »

And I thought Champion of Turnabouts was magic. This one lets you cast six whole types of it!

As a fan of JRPGs, this one hit the spot. Even down to its bare essentials, the joy of leveling up and seeing big number get bigger cannot be matched. And of course, the impressiveness of this being done in the AAO engine cannot be overstated. Even just being able to name your character was mind-blowing to me.
ImageImage
User avatar
TimeAxis
Posts: 391
Joined: Fri Jan 01, 2021 8:27 pm
Spoken languages: English
Contact:

Re: [M][CE] HALLOWEEN HERO ●

Post by TimeAxis »

CodingAnt wrote: Wed Sep 20, 2023 8:19 pm And I thought Champion of Turnabouts was magic. This one lets you cast six whole types of it!

As a fan of JRPGs, this one hit the spot. Even down to its bare essentials, the joy of leveling up and seeing big number get bigger cannot be matched. And of course, the impressiveness of this being done in the AAO engine cannot be overstated. Even just being able to name your character was mind-blowing to me.
Glad you enjoyed it.
Image
Question Arcs (Threads Coming Eventually)
ImageImageImageImage
Gaiden Episodes
Champion of Turnabouts ★
HALLOWEEN HERO
Other
Phoenix Wright: Ace Attornauts
The Curious Case of the Phantom Limousine (Coming Eventually)
The Imposter's Turnabout (Coming Maybe)
Post Reply