Rivals of Aether

Rivals of Aether

Not enough ratings
[LEGACY] [Workshop Creators] Implementing support for the Abyss Rune Buddy.
By Supersonic
Abyss Rune Buddy is no longer required to add Abyss Runes to your characters. Thank you for supporting the abyss rune buddy!

A guide for character creators on adding support for the Abyss Rune Buddy to their characters.
   
Award
Favorite
Favorited
Unfavorite
Introduction
Greetings! If you've made it to this guide, you're probably a workshop creator, or just curious about what this is. Maybe you've never heard of the Abyss Rune Buddy, or you're just curious and want to delve into how it works! I can't recommend enough for you not to look into my code, not because I don't want it copied (believe me, I really don't care about that) but because it's a mess as this was my first project, but I'm also not going to try and hide it either.

Why might you want to implement runes?
  • You're making a balanced character or trying to get it tourney legal in a workshop bracket, but you want to de-stress and let loose making silly stuff, but don't want to make an entirely new character.
  • You have ideas that don't fit in the character's base kit that would be fun, but you don't want to force it.
  • You want to make your character SUPER broken.
  • You want to make your already broken character EVEN MORE BROKEN.
Any reason you have, it's definitely valid.

There are a few steps, some optional, to implementing support for the buddy. Full implementation will include:
  • 15 runes, preferably of differing types.
  • Reset cases for attribute changes. (Makes the random mode work properly.)
  • All of the draw code, including the optional code in post_draw.gml. (Random mode display. I don't think it's urgent.)

This guide should get you started and give you valuable pointers on good practices when implementing runes.
Step 1: Required Code.
This is the first and easiest step: pasting the required code into their respective files, namely draw_hud.gml and init.gml. These will initialize the required variables and allow the Abyss Rune Buddy to actually draw its GUI to the screen.

First, let's paste the draw_hud.gml code. This code should be at the very bottom of the file:
//abyss gui code
ab_hud_x = temp_x;
ab_hud_y = temp_y;
//this is for the outdated warning message
if ("depNotice" not in self) depNotice = 0;
if ("abyssEnabled" in self && abyssEnabled && (menuActive || timerActive)) abyssDraw();
#define abyssDraw
/// abyssDraw()
/// draws text and images the player recieved from the abyss buddy.
if ("abyss_drawArray" in self && ds_list_valid(abyss_drawArray)) {
if (ds_list_size(abyss_drawArray) > 0) {
for (var _i = 0; _i < ds_list_size(abyss_drawArray);_i++) {
var _text = abyss_drawArray[| _i];
if draw_get_halign() != _text[6] {
draw_set_halign(_text[6]);
}
switch (_text[0]) {
case 0:
draw_debug_text(floor(_text[1]),floor(_text[2]),string(_text[3]));
break;
case 1:
draw_sprite_ext(_text[3],_text[5],_text[1],_text[2],1,1,0,_text[4],1);
break;
case 2:
draw_text_plus(floor(_text[1]),floor(_text[2]),string(_text[3]),floor(_text[5]),floor(_text[4]));
break;
case 3:
if draw_get_font() != 1 draw_set_font(1);
draw_text_ext_color(floor(_text[1]),floor(_text[2]),string(_text[3]),16,floor(_text[5]),_text[4],_text[4],_text[4],_text[4], 1);
break;
default:
break;
}
}
}
//finished drawing, so clear the table for the next frame.
ds_list_clear(abyss_drawArray);
}
//return draw_calls;
#define draw_text_plus
/// draw_text_plus(x, y, text, font, color = c_white)
/// draws outlined text in any in-game font.
if draw_get_font() != argument[3] {
draw_set_font(argument[3]);
}
draw_text_color(argument[0]+2,argument[1],argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]-2,argument[1],argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0],argument[1]-2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0],argument[1]+2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]+2,argument[1]-2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]-2,argument[1]-2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]+2,argument[1]+2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]-2,argument[1]+2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0],argument[1],argument[2],argument_count > 4 ? argument[4] : c_white,argument_count > 4 ? argument[4] : c_white,argument_count > 4 ? argument[4] : c_white,argument_count > 4 ? argument[4] : c_white,1);

With that out of the way, that's all you need in draw_hud.gml for the buddy to work. Now, in init.gml you need to define some variables. Go ahead and copy and paste this anywhere inside of init:
//Rune Support
abyssEnabled = false;
enum runes {A = 1,B = 2,C = 3,D = 4,E = 5,F = 6,G = 7,H = 8,I = 9,J = 10,K = 11,L = 12,M = 13,N = 14,O = 15}
runeA = false; runeB = false; runeC = false; runeD = false; runeE = false; runeF = false; runeG = false; runeH = false; runeI = false; runeJ = false; runeK = false; runeL = false; runeM = false; runeN = false; runeO = false; runesUpdated = false;
ab_hud_x = 0;
ab_hud_y = 0;

//abyssMods[1 to 15] = [type, description];
//types are: -1 - disabled
// 0 - object mod: Modifies a static object left behind after an attack.
// 1 - ranged mod: Modifies a projectile attack.
// 2 - hit mod: Modifies a direct physical interaction with an opponent.
// 3 - ability boost: Powers up a character attribute or action.
abyssMods = array_create(16,[-1,"Not Implemented."]);

With that out of the way, that's step 1 done! Step 2 will require some more work in init.gml below this point, so keep looking at that file.

Useful note for debugging: After refreshing, if the GUI does not show up, you will have rune A selected. Tap down, then tap left, and tap attack to exit the menu. You'll then be able to pause the game.
Step 2: Defining your runes.
This is the first part of programming runes, and it's very important. This is where you define your rune, giving it a type and a description. If it isn't defined, you are not able to select the rune, and thus cannot test it.
Let's take the image to the right as an example of what we're doing here. Rune F is an unimplemented, unselectable rune, which is why its letter is grey.
Rune C, the highlighted rune, is an Ability Boost rune, which makes the letter pink, and its description is "Midair jumps go higher."

Now that we've broken down what affects what (simple, I know.), we can easily jump into the code behind it, which looks like this:
abyssMods[@ runes.C] = [3, "Midair jumps go higher."];
This code goes below the init.gml code we set in the last step. This is how you define the rune's description and color. The cost is automatically determined by the letter (runes.C).
[3, "Midair jumps go higher."]
The 3 in this part is the type, and the string is obviously the description.
There are 4 types (not counting the unimplemented type):
  • Type 0 - Object Mod: Modifies a static object left behind after an attack.
  • Type 1 - Modifies a projectile attack.
  • Type 2 - Hit Mod: Modifies a direct physical interaction with an opponent.
  • Type 3 - Ability Boost: Powers up a character attribute or action.
Now, while these don't actually matter, I personally believe it's a good idea to make sure the types are accurate. I may implement a feature like what's in the base game, that shows these descriptions and highlights which runes are of that type, which may make it matter more in the future.

Description Tip:
For consistency with the base game runes (and in general), when rune descriptions refer to the move name, refer to specific moves in allcaps, for example: FAIR for Forward Air or DASH ATTACK for, well... Dash Attack.
Step 3: Programming your runes.
This is the hardest part. Although, I really wouldn't say it's that hard. Let's start off with an important concept. When programming, there are typically only two types of runes.
There are:
  • Permanent (kind of) Attribute Changes.
  • LITERALLY ANYTHING ELSE.
Now you might say that's cheating, but the fact of the matter is, they only have one primary difference (or two differences depending on how you look at it.) Here's an example of a permanent attribute change, which is the Bunny Hood rune from Hime Daisho (with added necessary context.):
//update.gml
if runesUpdated {
if runeC { //trigger the attribute change
dash_anim_speed = .3;
initial_dash_speed = 8.5;
dash_speed = 8.5;
jump_speed = 13;
djump_speed = 13;
jump_sound = sound_get("boiing");
djump_sound = sound_get("boiiing");
} else { //reset the attribute change
dash_anim_speed = .2;
initial_dash_speed = 7;
dash_speed = 7;
jump_speed = 11;
djump_speed = 10.5;
jump_sound = asset_get("sfx_jumpground");
djump_sound = asset_get("sfx_jumpair");
}
}
What makes this a correctly programmed permanent attribute change is the if runesUpdated and the else statement following if runeC, which resets the attribute change.
runesUpdated is only run for two frames in a row at most, which makes it ideal for one time changes like this. There are exceptions to doing this, which are rare but you may run into them if something dynamically changes your attributes, in which case you can likely set the variables that your character falls back on here instead, if that applies to your code.

Other runes typically don't require a runesUpdated call, and usually not even an else statement; for example, this Hime rune that resets her Up Special cooldown when she airdodges.
//update.gml
if state == PS_AIR_DODGE && runeF {
move_cooldown[AT_USPECIAL] = 0;
}
Completion.
After programming your runes, that's it! There's an optional code block in the next section of the guide that I do recommend you add, but it's only really used for one thing so it's not necessary.

Once you've finished adding runes to your character, go ahead and shoot me a comment in the comments section of my compatibility collection! I'd be happy to add your character to the list of supported characters.
https://steamcommunity.com/sharedfiles/filedetails/?id=1933835918
Optional: Extra draw code.
The following code is an optional chunk of code you can place at the bottom of post_draw.gml.
This code only enables one feature as of now, which I would feel bad making altering another file something mandatory. For clarity, the feature it enables is making random runes appear over your % whenever runes are added or changed, as shown to the right.

if "abysspostdrawenabled" not in self abysspostdrawenabled = true; // tell buddy you have post draw code
if "abyssPostDrawing" in self && abyssPostDrawing abyssPostDraw(); // actually do post draw code
#define abyssPostDraw
/// abyssPostDraw()
/// draws text and images the player recieved from the abyss buddy.
if ("abyss_postDrawArray" in self && ds_list_valid(abyss_postDrawArray)) {
if (ds_list_size(abyss_postDrawArray) > 0) {
for (var _i = 0; _i < ds_list_size(abyss_postDrawArray);_i++) {
var _text = abyss_postDrawArray[| _i];
if draw_get_halign() != _text[6] {
draw_set_halign(_text[6]);
}
switch (_text[0]) {
case 0:
draw_debug_text(floor(_text[1]),floor(_text[2]),(_text[3]));
break;
case 1:
draw_sprite_ext(_text[3],_text[5],_text[1],_text[2],1,1,0,_text[4],1);
break;
case 2:
draw_text_plus(floor(_text[1]),floor(_text[2]),string(_text[3]),floor(_text[5]),floor(_text[4]));
break;
case 3:
if draw_get_font() != 1 draw_set_font(1);
draw_text_ext_color(floor(_text[1]),floor(_text[2]),string(_text[3]),16,floor(_text[5]),_text[4],_text[4],_text[4],_text[4], 1);
break;
default:
break;
}
}
}
//finished drawing, so clear the table for the next frame.
ds_list_clear(abyss_postDrawArray);
}
#define draw_text_plus
/// draw_text_plus(x, y, text, font, color = c_white)
/// draws outlined text in any in-game font.

if draw_get_font() != argument[3] {
draw_set_font(argument[3]);
}
draw_text_color(argument[0]+2,argument[1],argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]-2,argument[1],argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0],argument[1]-2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0],argument[1]+2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]+2,argument[1]-2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]-2,argument[1]-2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]+2,argument[1]+2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0]-2,argument[1]+2,argument[2],c_black,c_black,c_black,c_black,1);
draw_text_color(argument[0],argument[1],argument[2],argument_count > 4 ? argument[4] : c_white,argument_count > 4 ? argument[4] : c_white,argument_count > 4 ? argument[4] : c_white,argument_count > 4 ? argument[4] : c_white,1);

I do recommend that you add it, but you will not lose anything other than this display if you do not add this code.
3 Comments
Curse Jan 17, 2022 @ 7:49am 
who would spend ours typing this
Curse Jan 17, 2022 @ 7:49am 
of course its complicated
webking321 Jan 12, 2020 @ 11:32pm 
Looks complicated