So, we all know the story of Yrel. If you don’t, avoid this post because spoilers are ahead.
Also, this isn’t meant to be written like a story. This is meant to be written similarly to a stream of ideas.
For starters, we do not know if Yrel is dead in the prime timeline. She could have been killed by orcs. She could have died in the explosion.
… But what if she didn’t die? What if the Legion captured her and other nearby draenei? What if they were being spiritually broken, malformed into the Eredar?
Imagine, if you will, the final raid tier of Legion. The cinematic comes out. Gul’dan is narrating the cutscene, saying how these champions of the light shall fall like so many before them. He will end it by mentioning that they will be broken and fall like so many before them. I realize this part is extremely cliche, but I haven’t seen anything regarding Legion’s story, so I don’t know what it is building towards.
The screen fades to black — All ambient sounds stop.
Gul’dan: “And if they make it here…”
Then Yrel’s voice, slightly modified to sound more similar to a Man’ari, speaks, “Then I shall thin their ranks.”
The patch name appears right then. You wouldn’t see Yrel in the cinematic. You’re left wondering if that was Yrel or not.
She would be the second to last boss in the raid. As you approach her room, you are approached by a group of demons led by an Eredar. The Eredar is Samaara, Yrel’s sister. (A possible alternative would be to give Samaara an Eredar name as she was not considered strong enough to keep her name. It isn’t until you beat Yrel that you discover that this Eredar was Samaara in this alternative.)
You enter the room with Yrel standing alone in center of the room.
That is her WOD appearance. In this raid, though, she’s have a much different appearance. The off-whites would be black. The bronze armor lining would be replaced with a fel green. The pink (and the pink crystals) would be replaced with a red similar to Gul’dan’s eyes. Her skin pigmentation would be slightly more purple, but very, very slightly so. But her hair, her horns, and everything else about her would be the same.
Why, though? Why would Yrel be an antagonist? Why would Samaara also be with her?
Because it tells you more about the Legion than a thousand words ever could. You saw how strong willed and determined Yrel was in the face of impossible odds in WOD. She knew that they were facing near impossible odds, but she still fought. She was a true heroine.
… Yet the Legion was able to break her will. The Legion shattered her resolve. It helps put into perspective the power that the Legion holds compared to the Iron Horde. You saw what she was willing to bear. You saw the sacrifices she made in the name of her people. And yet the Legion could break her.
Why is Samaara there? Because it is the antithesis of the WOD story.
Eredar Yrel isn’t too weak (physically) to protect her beloved sister. Eredar Yrel’s convictions were too weak to keep her from being corrupted. Instead, Samaara is too weak to be of any value to Eredar Yrel, so Yrel is willing to send her to her demise. So instead of a story about physical weakness leading to the death of her sister, it becomes a story about weakness in her convictions eventually leading to the death of her sister.
Want Yrel to still be a protagonist?
Fast-forward to Yrel’s defeat. Her red cotton candy mace falls to the ground and shatters. She is ready to be executed. She is as defiant as ever, as you’d expect of Yrel. Velen approaches and asks you to continue on. He will deal with Yrel.
Towards the end of the Gul’dan fight, things look very bleak. It seems as if evil will triumph. A blazing light shoots down from above, piercing through all of the demons on the battlefield, bringing them to their collective knees. In come Velen and Yrel, yelling for the champions of the light to hold on. They join in the battle to help defeat Gul’dan once and for all. I base this entire redemption arc on the fact that Avrus was redeemed, although I am not sure if an Eredar has the same rules of redemption as a satyr.
Closing cinematic features Yrel being very distraught. She thanks the champions for allowing her to see the world without being blinded by corruption. Her regret is palpable, but she is clearly very confused. Then she remembers Samaara. She sprints out of the cutscene. The rest of the cutscene then plays out however it will. The player is offered a quest by Velen to go and check on Yrel.
The player will find Yrel at the spot that Samaara died. The scene would be a cruel juxtaposition of the similar WOD event. After a brief time, Yrel swears herself to the light. She swears that she will become strong enough to save this world from suffering the same fate as hers; To save the people of Azeroth from suffering the same fate as her people; To save everyone from the torment that she and Samaara suffered.
Eventually (perhaps in the pre-patch for the expansion after Legion), Yrel becomes a regular NPC in the Exodar. Perhaps she could assume a more primary role in the war against the Burning Legion.
But wouldn’t this taint Yrel’s legacy?
To me? No. WOD showed you the kind of champion that Yrel could have been had historical events been different. She would have been a strong, unbreakable champion of virtue.
This path shows you that, even in her darkest moments, she still held onto a shred of light. Despite being near fully consumed by the demonic powers given to her by the Burning Legion, the love for her sister still burned brighter than that. She would be a hero whose tale wasn’t simply about glory and destiny. Her tale would be one of falling into the clutches of evil. It would be one of redemption and future glory. It would be a tale of a character who understands corruption, anger, and the power of the Burning Legion like few others.
It would give the players many “what if” questions to ask.
1.) The UI. The UI is definitely not the prettiest thing, but as this project was entirely a prototype to get ready for the “real” project, I was willing to just make something functional, albeit not pretty.
2.) The inventory system is only 10 slots, but every item can have multiple features:
ItemID – The ID that the item attempts to emplace.
Unique – If the item is unique, more than one stack cannot exist.
Stackable – If the item is stackable, more than one item can be placed into one stack.
StacksTo – If the item is stackable, this is the cap for its stacking capabilities.
It also handles all combination cases, such as…
A non-unique stackable item going over the StacksTo cap will overflow into the next stack.
A unique non-stackable item will check the entire inventory before emplacing to ensure the ItemID doesn’t come up later.
A unique stackable item will check the entire inventory before emplacing to check for the ItemID and will handle overflow.
3.) Respawning. Respawning works for players, but death does nothing to NPCs. They are unstoppable!
To start with, AMO is short for “Arena Masters Online”. It was originally intended to be a multiplayer PVP arena game with traditional MMORPG style combat. While this project was a Blueprint/Code hybrid project, this project is probably 99% Blueprint. I will indicate the systems that are not. I will try to screenshot as many systems as I can, but if you want to check out the project in all of its crash-prone prototype glory, click here. There have only been a couple changes in the past 6 months, and those were all updates to the spell table and updating the project and solution to 4.12.5 compatibility. If you download the project, keep in mind that the project does not contain all of the assets that appear in these clips. Some of the assets are purchased from the shop.
First, a really quick introduction: A three way fight, followed by an explanation of the AI.
It isn’t the most visually stunning fight, but allow me to explain what is happening. I am playing in the editor itself. The two enemies are enemies of both myself and each other. Being that everyone starts at 5% health, their first reaction is to find a hiding spot to heal up in. I target one of the AI and follow him. As you can see, he runs to a spot that is out of line of sight of everyone and begins casting Tranquility, which creates an enormous collision sphere. Tranquility then applies the Tranquility HOT to the 5 lowest health targets every tick. In this case, he is the only one, but what if there are, say…. 84 AI running around in this arena, but they’re only divided into two teams?
As you can see, the frame rate is horrible, and they also get stuck on each other a lot, but notice how I’m not casting, but I’m getting healed, as are quite a few others. This is because one AI started casting Tranquility, and it applied to the five lowest health units within the AOE. You can also see by the top left that the AI is announcing what spell it is casting. How many spells in total are there?
The AI will only cast two: PC_DruTranquility and TEST_Frostbolt. The AI’s behavior is very erratic, I will admit that. That is because the AI wasn’t setup to actually fight in the prototype. They had very, very primitive logic.
That is likely illegible, so I will explain. The top of the tree is the root followed by a selector named “RealRoot” and then a selector. You can ignore the “RealRoot” selector. The unnamed selector is the important one. It will attempt to go into the first sequence node. If “IsEnemyVisible” is set, it will check its own status. If it is defensive (< 35% health), it will attempt to run away and find an enemy hiding spot by using an EQS query. The EQS is very basic.
Yes, it is called TestEQS. I apologize for the name, but like I said, this is a prototyping project, and this was my first venture into EQS. But this EQS does as follows:
Create a pathing grid of 1500 units with 100 space between with a 2048 height projection.
Discard all unreachable nodes. We don’t need the system to be doing calculations to locations that it cannot reach.
It then attempts to do a basic visibility line trace to enemies from individual nodes. It does not score on this, it simply discards nodes that are out of line of sight of an enemy.
At this stage, the only pathing nodes we have left to choose from are ones out of line of sight of the enemy.
We now measure the distance to this node from the querier, but we prefer lesser. So, we want the closest node to the AI. We score this on an x1 scale.
We now measure the distance from this node to the enemy unit, but we prefer greater. We want the furthest node from our enemy. We score this on an x1 scale.
We then take into account the facing direction our enemy. We want to prefer spots behind the enemy or on the peripherals if they are close enough. We score it based on this factor.
This means the AI’s hiding spot will be an out of line-of-sight node that is somewhere in the middle of being close to the AI and far from his enemy. This generally generates spots that are very close to walls or other surfaces that obstruct LOS.
Hence, the very vertical, oddly shaped map I was testing it in. As you can see in the first video, the AI stopped running down the steps to heal because he had already obstructed LOS there. That spot met all of the criteria.
If they went on the offensive, the AI was much simpler (perhaps to a fault)
It would poll all actors of type ArenaCharacter within 2000 units of the AI. It would then check for LOS existing. If LOS does not exist between the enemy and the AI, the enemy is discarded. Then, once it has all enemies in LOS, it prefers enemies that are further away.
This was designed specifically for ranged caster AI that only used 2 of the original 4 spells (the other two being TEST_FlameJet and TEST_Firefield). TEST_Bloom came a lot later, but I will explain that then.
The reason the AI was built to prefer far away targets is because the caster is more likely to be able to escape a far away target than an up close target. If it could find no targets in line of sight…
Above is the “search for enemy” EQS. That’s all it does. It finds the furthest location on the pathing grid (size 1000, 1024 height) out of line of sight and goes towards it. If everything is in line of sight as far as it can tell, it will prefer the furthest spot that is in line of sight.
The logic for this basic search was that if everything is in line of sight, the furthest point is the most likely point to take you to an area with more cover and hiding spots. This is a pretty big assumption, but keep in mind, most of the areas that currently existed within the game were small, tight corridors where this assumption worked.
It would regularly get the AI caught in a loop of spots and never get out.
Those are the rest of the traits for each ability. Almost every trait on the other two slides is functional.
Let’s touch on some of the more important ones.
RawSpellID – This was a storage mechanism so that I could replicate the lookup ID in the table without needing to replicate the entire spell. I could send “PC_DruReplenish” to all clients, and then all clients knew to look up PC_DruReplenish for all effects.
Spell Logic – This was the actor that was spawned by the spell.
Casting Type – Cast, Channeled, Charged. Cast spells went off upon reaching the target cast time. Channeled spells went off multiple times throughout the cast. Charged spells could be released early with reduced effectiveness.
Disable Type – None, Silence, Disarm, Either, Both. None means the spell can always be cast. Silence means that the spell can be cast while disarmed but not while silenced. Disarm means the spell can be cast while silenced but not while disarmed. Either means that the spell cannot be cast while either disarmed or silenced. Both means that the spell cannot be cast while both disarmed AND silenced.
Aiming Type – Frame Targeted, Skill Shot, Ground Targeted, Auto Targeted, Toggled. Frame targeted requires a target of some kind. It has some smart targeting features. If you are casting a friendly-only spell while targeting an enemy, it will attempt to target the enemy’s target. If the enemy’s target is also an enemy, it will cast on you. It does this for many different circumstances. Skill Shots shoot directly ahead. Ground targeted are exactly what they sound like – Ground targeted. Auto targeted spells are generally self centered AOEs, such as tranquility. Toggled was extremely buggy and couldn’t be trusted to work in most circumstances.
Player Targeting Rules – Enemy, Friendly, Friendly (Other), Self, Either. Enemy could only be cast on enemies. Friendly could be targeted on yourself or any other friendly unit. Friendly (Other) could only be used on friendly targets besides yourself. Self could only target yourself. Either could target friend or foe. In the C++ edition, I added “Enemy or Self” to the list.
Damage Type – There were 18 damage types, although most of the types on the picture are either physical, shadow, healing, absorb, or nature. Most of the damage types, admittedly, do not have a difference in this version (outside of healing inverting the damage).
Magic Schools – This was only used for lockout purposes. Yes, interrupts existed and worked (PC_AssPommelStrike is an interrupt).
Base Cast While Moving – Can this spell be cast while moving?
CD Line Name – Most of the time, this one is “None”. But this one is sometimes set to something else. Let’s say “PC_BerRecklessness”. When you cast PC_BerRecklessness, it triggers a CD Line called “BerRageLine”. This means that ALL BerRageLine abilities are on CD for the duration. This is for situations where two or more abilities all share a CD.
CDs Also Triggered – Most of the time, this array is empty. When it isn’t, it puts all abilities listed on CD for a custom time. So, if we decided PC_BerRecklessness and PC_BerRampage shouldn’t be in the same line, but they shouldn’t be usable simultaneously, we may decide to handle it through this. We may put “PC_BerRampage” on CD for 8 seconds so that it can’t be used within 8 seconds of PC_BerRecklessness.
SelfEffects and TargetEffects do not work.
Casting Particles – I split the particles into 3 categories: Hands, body, feet. They attach to the bones at the respective positions as well.
Casting Particles Offsets – I added vector offsets to the particles for hands, body, and feet if you wish to adjust where the particle is.
Targeting Scale doesn’t work.
Damage scaling doesn’t work as levels don’t exist.
Resource cost scaling doesn’t work as levels don’t exist.
Base level doesn’t matter as you cannot acquire abilities.
Cast animation worked until I accidentally overwrote the animation sequence with a blank animation sequence during a hard drive transfer.
LookupID is the lookup ID for the spell’s damage range on the SQLite table.
IsBuilder – Some classes have abilities that spend mana and some have abilities that build mana.
IsVariableSpender – This is for abilities that do not spend a fixed amount.
SpendingIntervals – This is the amount of mana that each incremental level increases by.
MinimumSpend – This is the minimum amount that can be spent by the spell.
MaximumSpend – This is the maximum amount that can be spent by the spell.
ModifierIDs – These are effects that the game will check the caster or target for. To stick with the Berserker examples, perhaps an ability can only be cast while Recklessness is up. This is where that would be handled. Or perhaps while Recklessness is up, this specific ability costs 50% less. This is where it would be handled. The big thing is that these aren’t always necessary. On the other hand, it will return true if ONE necessary feature is found, but not if ALL necessary features are found. It is better to use AlternativeResourceID for this purpose.
Alternative Resource Spent ID – Let’s say that every time you cast PC_CleSplashHeal, it gives the cleric a stack of Divine Favor. Divine Favor stacks to 10. For some spells, Divine Favor may be required. This is where the ID of that required resource would be listed. This is separate from ModifierIDs because ModifierIDs was a very binary solution with, as mentioned in the end of the paragraph, a difficulty dealing with multiple requirements.
MinAltResource – This is the minimum amount that can be spent by the spell.
MaximumAltResource (off the end of the image) – This is the maximum amount that can be spent by the spell.
I’d like to take a few moments to talk about “ModifierIDs”, though.
That is what the modifier ID struct looks like. In it, you see 5 options:
BuffID – Indicates the ID of the buff it is looking for.
IsOptional – As mentioned earlier, if it is not optional, only one non-optional buff is required.
IsSelf – Checks self or checks target
IsPositiveBuffID – If it is a positive or negative buff ID
AbsenceRequired – If it is required to NOT be on the target.
To use an example from World of Warcraft, Paladin Blessing of Protection would have the BuffID of Forebearance (whatever that is), it would NOT be optional, it would not simply be self, it would be a positive buff ID, and absence would be required.
Here’s a short video going through some of the blueprints required to make this happen.
If you want to see them all, the source is available up at the very beginning. Hopefully you can find some use or inspiration from them. Yes, the drag-and-drop works in the inventory and spellbook. The inventory mostly works.
Here’s a short video showing how many blueprints there are total:
I chose to start working on the C++ version of the project whenever UE4.11 preview 4 was pushed into the main branch and I accidentally updated, corrupting the project for a long time (it would crash when I made any changes to the Blueprints). I would have loved to continue working on the project, but by the time this project was functional again, the new C++ project was well on its way.