As is known, the release of AO will bring the ability to obtain a second magic and change your first magic, among other things.
Knowing this, and that the release is far away into the future, many will try to theory craft a strong magic combo, the peak form of which is making a comprehensive tier list. So, given that my school is going fully online (again) which means I have an increase in free time, I decided to join the early birds and make a tier list right now.
But alas, I’m too lazy to actually think about all the possible combinations and their strengths and weaknesses, so I made my computer do all that, and I will reap the benefits.
Since I didn’t do anything manually, I went a step further and made one for when we get our 3 total elemental magics as well.
Triple Magic Tier List
Baste your eyes upon this behemoth, all 1140 possible magic combinations, sorted by “strength”. which in this case means the sum of strengths of the magic present in the combo, derived from their stats, and the strengths of interactions between these magics, as well as utility, like hazards created and status effect clears.
I decide to have 7 tiers, and split the list of magic in this manner:
10% / 15% / 15% / 20% / 15% / 15% / 10% per tier, respectively
So here are the results:
One tier per image:
Double Magic Tier List (BONUS)
With minimal modifications, I made the same program spit out results for 2 magics, its a bit less huge then the last image so here it is:
Some Caveats
While I think the tier list is decently accurate, it still fails to address some things that could be crucial to a combos success.
Mainly, interactions with other top tier combos, and it’s matchups against other combos in general. Frankly I couldn’t be bothered to do that, so I skipped it.
This is the list of magics by themselves, sorted by the written “statscore”:
Shadow 13.5
Earth 13
Wind 12.5
Water 12.5
Magma 12
Iron 12
Explosion 11
Ash 10.5
Sand 10.5
Crystal 10
Snow 9
Acid 9
Glass 9
Wood 9
Lightning 8.5
Ice 8
Plasma 8
Fire 8
Light 7.5
Poison 7
These scores took in consideration the magics damage, speed, size, DoT, hazards it creates and clash rates, but doesn’t include destruction (because I don’t think it matters that much and would probably create an even greater disbalance) and interactions (clears, status procs, +/-damage on enemies with a certain effect) because those only only come in play when there’s multiple magics present, which is when I include calculations for those.
At the end of the day, If you disagree that’s fine. I have a comprehensive tier list, you don’t, so I guess you gotta make your own to prove me wrong.
Source Code for the program used to generate the triple magic tier list
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
struct magic
{
string name;
double statscore = 0;
string status = "none";
vector<pair<string, string>> procs;
vector<string> clears;
vector<pair<string, double>> interactions;
};
struct combo
{
double score;
int id;
vector<string> magics;
vector<string> clears;
vector<string> statuses;
};
//for the sort
bool CompareCombos(combo c1, combo c2)
{
return c1.score > c2.score;
}
//check if another magic in the combo already clears a status
bool AlreadyCleared(vector<string> CurrentClears, string NewClear)
{
for(int i = 0; i < CurrentClears.size(); i++)
{
if(NewClear == CurrentClears[i])
return 1;
}
return 0;
}
vector<magic> Magics;
vector<combo> Combos;
int main(void)
{
ios::sync_with_stdio(false);
//things used for reading from input file
string sInput;
double nInput;
ifstream in("magicinput.txt");
//skip the first 15 lines from the file, which have notes on the format
for(int i = 0; i < 15; i++)
getline(in, sInput);
/*
The format is:
**Name**
damage (number)
speed (number)
others (size, hazards, status threshold, dot, clash) (number, again)
has status (0/1)
status name
# of procs
procs pair<required status, proced status>
# of clears
clears (name of cleared status)
# of interactions
interactions pair<required status, interaction score>
*/
//interaction strength is +/-0.5 points per +/-5% up to +/-40%, from where it is half of that
for(int i = 0; i < 20; i++)
{
magic mInput;
//get magics name from file
in >> sInput;
mInput.name = sInput;
//get magics damage from file and add to statscore
in >> nInput;
mInput.statscore += (nInput - 0.75)/0.025;
//get magics speed from file and add to statscore
in >> nInput;
if(nInput < 1)
mInput.statscore -= (1 - nInput) / 0.05 * 0.5;
else
{
mInput.statscore += max(0.0, min((nInput - 1.0), 0.4)) / 0.05 * 0.5;
mInput.statscore += max(0.0, nInput - 1.4) / 0.05 * 0.25;
}
//get additional statscore (for DoT, hazards, clash, size)
in >> nInput;
mInput.statscore += nInput;
//get the status that the magic gives on hit
in >> nInput;
if(nInput)
{
in >> sInput;
mInput.status = sInput;
}
//get statuses that magics procs on targets with some existing status on them
in >> nInput;
for(int j = 0; j < nInput; j++)
{
string proc_result;
in >> sInput;
in >> proc_result;
mInput.procs.push_back(make_pair(sInput, proc_result));
}
//get statuses the magic clears
in >> nInput;
for(int j = 0; j < nInput; j++)
{
in >> sInput;
mInput.clears.push_back(sInput);
}
//get statuses that +/- the magics damage, and the strength of the interaction
in >> nInput;
for(int j = 0; j < nInput; j++)
{
double interaction_score;
in >> sInput;
in >> interaction_score;
mInput.interactions.push_back(make_pair(sInput, interaction_score));
}
//add to magic list, duh
Magics.push_back(mInput);
}
//id used for html
int id = 1;
for(int i = 0; i < 20; i++)
{
for(int j = i+1; j < 20; j++)
{
for(int k = j+1; k < 20; k++)
{
combo cInput;
cInput.id = id++;
//add names of magics in the combo
cInput.magics.push_back(Magics[i].name);
cInput.magics.push_back(Magics[j].name);
cInput.magics.push_back(Magics[k].name);
//add each magics status to the total statuses the combo can proc
if(Magics[i].status != "none")
cInput.statuses.push_back(Magics[i].status);
if(Magics[j].status != "none")
cInput.statuses.push_back(Magics[j].status);
if(Magics[k].status != "none")
cInput.statuses.push_back(Magics[k].status);
//add statuses that the magics can proc on enemies which were hit by other magic in the combo
for(int x = 0; x < Magics[i].procs.size(); x++)
{
for(int y = 0; y < cInput.statuses.size(); y++)
{
if(Magics[i].procs[x].first == cInput.statuses[y])
cInput.statuses.push_back(Magics[i].procs[x].second);
}
}
for(int x = 0; x < Magics[j].procs.size(); x++)
{
for(int y = 0; y < cInput.statuses.size(); y++)
{
if(Magics[j].procs[x].first == cInput.statuses[y])
cInput.statuses.push_back(Magics[j].procs[x].second);
}
}
for(int x = 0; x < Magics[k].procs.size(); x++)
{
for(int y = 0; y < cInput.statuses.size(); y++)
{
if(Magics[k].procs[x].first == cInput.statuses[y])
cInput.statuses.push_back(Magics[k].procs[x].second);
}
}
//add each magics statscores to the combo score
cInput.score = (Magics[i].statscore + Magics[j].statscore + Magics[k].statscore) / 3;
//add statuses which the combo can clear, check for overlap
for(int x = 0; x < Magics[i].clears.size(); x++)
cInput.clears.push_back(Magics[i].clears[x]);
for(int x = 0; x < Magics[j].clears.size(); x++)
{
if(!AlreadyCleared(cInput.clears, Magics[j].clears[x]))
cInput.clears.push_back(Magics[j].clears[x]);
}
for(int x = 0; x < Magics[k].clears.size(); x++)
{
if(!AlreadyCleared(cInput.clears, Magics[k].clears[x]))
cInput.clears.push_back(Magics[k].clears[x]);
}
//score the clears
cInput.score += cInput.clears.size() * 0.5;
//decrease score slightly if the combo clears its own statuses
for(int x = 0; x < cInput.clears.size(); x++)
for(int y = 0; y < cInput.statuses.size(); y++)
cInput.score -= 0.25 * (cInput.clears[x] == cInput.statuses[y]);
//add interaction strength between each magic with the total statuses
for(int x = 0; x < Magics[i].interactions.size(); x++)
{
for(int y = 0; y < cInput.statuses.size(); y++)
{
if(Magics[i].interactions[x].first == cInput.statuses[y])
cInput.score += Magics[i].interactions[x].second;
}
}
for(int x = 0; x < Magics[j].interactions.size(); x++)
{
for(int y = 0; y < cInput.statuses.size(); y++)
{
if(Magics[j].interactions[x].first == cInput.statuses[y])
cInput.score += Magics[j].interactions[x].second;
}
}
for(int x = 0; x < Magics[k].interactions.size(); x++)
{
for(int y = 0; y < cInput.statuses.size(); y++)
{
if(Magics[k].interactions[x].first == cInput.statuses[y])
cInput.score += Magics[k].interactions[x].second;
}
}
//add to combo list
Combos.push_back(cInput);
}
}
}
//sort combos in ascending order
sort(Combos.begin(), Combos.end(), CompareCombos);
//print all combos andtheir scores, just to make sure it didi stuff
for(int i = 0; i < Combos.size(); i++)
{
for(int j = 0; j < 3; j++)
cout << Combos[i].magics[j] << " ";
cout << Combos[i].score << endl << endl;
}
//lists of combos per tier, could've been all in one list but I'm not changing it now
vector<string> Stier, Atier, Btier, Ctier, Dtier, Etier, Ftier;
string tier;
//how many combos per tier, respectively
// 114, 171, 171, 228, 171, 171, 114
int i = 1;
//the gruesome proccess of generating the html for the combos image
while(i <= 114)
{
//put the correct id for the combo
tier += "<div class=\"character\" id=\"";
tier += to_string(Combos[i-1].id);
tier += "\" style=\"background-image: url("/images/chart/chart/arcane-oddesy-triple-magic-combination-tier-list-841197/";
//Yandere-dev tier if/else wall to add the things that are from different batches of uploads
if(Combos[i-1].id > 1075)
tier += "zz1613954656";
else if(Combos[i-1].id > 1004)
tier += "zz1613954655";
else if(Combos[i-1].id > 931)
tier += "zz1613954654";
else if(Combos[i-1].id > 867)
tier += "zz1613954653";
else if(Combos[i-1].id > 854)
tier += "zz1613954652";
else if(Combos[i-1].id > 853)
tier += "zz1613954564";
else if(Combos[i-1].id > 783)
tier += "zz1613954563";
else if(Combos[i-1].id > 711)
tier += "zz1613954562";
else if(Combos[i-1].id > 640)
tier += "zz1613954561";
else if(Combos[i-1].id > 580)
tier += "zz1613954560";
else if(Combos[i-1].id > 556)
tier += "zz1613954500";
else if(Combos[i-1].id > 492)
tier += "zz1613954499";
else if(Combos[i-1].id > 429)
tier += "zz1613954498";
else if(Combos[i-1].id > 366)
tier += "zz1613954497";
else if(Combos[i-1].id > 324)
tier += "zz1613954496";
//magic names from the combo
tier += Combos[i-1].magics[0];
tier += "-and-";
tier += Combos[i-1].magics[1];
tier += "-and-";
tier += Combos[i-1].magics[2];
tier += "-magicpng.png");\"></div>";
//add to respective tier list
Stier.push_back(tier);
//increment counter and clear string used for generating the html
i++;
tier.clear();
}
//repeat for other tiers because ctrl+c/ctrl+v is easier that partitioning one big list later
while(i <= 114+171)
{
tier += "<div class=\"character\" id=\"";
tier += to_string(Combos[i-1].id);
tier += "\" style=\"background-image: url("/images/chart/chart/arcane-oddesy-triple-magic-combination-tier-list-841197/";
if(Combos[i-1].id > 1075)
tier += "zz1613954656";
else if(Combos[i-1].id > 1004)
tier += "zz1613954655";
else if(Combos[i-1].id > 931)
tier += "zz1613954654";
else if(Combos[i-1].id > 867)
tier += "zz1613954653";
else if(Combos[i-1].id > 854)
tier += "zz1613954652";
else if(Combos[i-1].id > 853)
tier += "zz1613954564";
else if(Combos[i-1].id > 783)
tier += "zz1613954563";
else if(Combos[i-1].id > 711)
tier += "zz1613954562";
else if(Combos[i-1].id > 640)
tier += "zz1613954561";
else if(Combos[i-1].id > 580)
tier += "zz1613954560";
else if(Combos[i-1].id > 556)
tier += "zz1613954500";
else if(Combos[i-1].id > 492)
tier += "zz1613954499";
else if(Combos[i-1].id > 429)
tier += "zz1613954498";
else if(Combos[i-1].id > 366)
tier += "zz1613954497";
else if(Combos[i-1].id > 324)
tier += "zz1613954496";
tier += Combos[i-1].magics[0];
tier += "-and-";
tier += Combos[i-1].magics[1];
tier += "-and-";
tier += Combos[i-1].magics[2];
tier += "-magicpng.png");\"></div>";
Atier.push_back(tier);
i++;
tier.clear();
}
//repeat for other tiers because ctrl+c/ctrl+v is easier that partitioning one big list later
while(i <= 114+171+171)
{
tier += "<div class=\"character\" id=\"";
tier += to_string(Combos[i-1].id);
tier += "\" style=\"background-image: url("/images/chart/chart/arcane-oddesy-triple-magic-combination-tier-list-841197/";
if(Combos[i-1].id > 1075)
tier += "zz1613954656";
else if(Combos[i-1].id > 1004)
tier += "zz1613954655";
else if(Combos[i-1].id > 931)
tier += "zz1613954654";
else if(Combos[i-1].id > 867)
tier += "zz1613954653";
else if(Combos[i-1].id > 854)
tier += "zz1613954652";
else if(Combos[i-1].id > 853)
tier += "zz1613954564";
else if(Combos[i-1].id > 783)
tier += "zz1613954563";
else if(Combos[i-1].id > 711)
tier += "zz1613954562";
else if(Combos[i-1].id > 640)
tier += "zz1613954561";
else if(Combos[i-1].id > 580)
tier += "zz1613954560";
else if(Combos[i-1].id > 556)
tier += "zz1613954500";
else if(Combos[i-1].id > 492)
tier += "zz1613954499";
else if(Combos[i-1].id > 429)
tier += "zz1613954498";
else if(Combos[i-1].id > 366)
tier += "zz1613954497";
else if(Combos[i-1].id > 324)
tier += "zz1613954496";
tier += Combos[i-1].magics[0];
tier += "-and-";
tier += Combos[i-1].magics[1];
tier += "-and-";
tier += Combos[i-1].magics[2];
tier += "-magicpng.png");\"></div>";
Btier.push_back(tier);
i++;
tier.clear();
}
//repeat for other tiers because ctrl+c/ctrl+v is easier that partitioning one big list later
while(i <= 114+171+171+228)
{
tier += "<div class=\"character\" id=\"";
tier += to_string(Combos[i-1].id);
tier += "\" style=\"background-image: url("/images/chart/chart/arcane-oddesy-triple-magic-combination-tier-list-841197/";
if(Combos[i-1].id > 1075)
tier += "zz1613954656";
else if(Combos[i-1].id > 1004)
tier += "zz1613954655";
else if(Combos[i-1].id > 931)
tier += "zz1613954654";
else if(Combos[i-1].id > 867)
tier += "zz1613954653";
else if(Combos[i-1].id > 854)
tier += "zz1613954652";
else if(Combos[i-1].id > 853)
tier += "zz1613954564";
else if(Combos[i-1].id > 783)
tier += "zz1613954563";
else if(Combos[i-1].id > 711)
tier += "zz1613954562";
else if(Combos[i-1].id > 640)
tier += "zz1613954561";
else if(Combos[i-1].id > 580)
tier += "zz1613954560";
else if(Combos[i-1].id > 556)
tier += "zz1613954500";
else if(Combos[i-1].id > 492)
tier += "zz1613954499";
else if(Combos[i-1].id > 429)
tier += "zz1613954498";
else if(Combos[i-1].id > 366)
tier += "zz1613954497";
else if(Combos[i-1].id > 324)
tier += "zz1613954496";
tier += Combos[i-1].magics[0];
tier += "-and-";
tier += Combos[i-1].magics[1];
tier += "-and-";
tier += Combos[i-1].magics[2];
tier += "-magicpng.png");\"></div>";
Ctier.push_back(tier);
i++;
tier.clear();
}
//repeat for other tiers because ctrl+c/ctrl+v is easier that partitioning one big list later
while(i <= 114+171+171+228+171)
{
tier += "<div class=\"character\" id=\"";
tier += to_string(Combos[i-1].id);
tier += "\" style=\"background-image: url("/images/chart/chart/arcane-oddesy-triple-magic-combination-tier-list-841197/";
if(Combos[i-1].id > 1075)
tier += "zz1613954656";
else if(Combos[i-1].id > 1004)
tier += "zz1613954655";
else if(Combos[i-1].id > 931)
tier += "zz1613954654";
else if(Combos[i-1].id > 867)
tier += "zz1613954653";
else if(Combos[i-1].id > 854)
tier += "zz1613954652";
else if(Combos[i-1].id > 853)
tier += "zz1613954564";
else if(Combos[i-1].id > 783)
tier += "zz1613954563";
else if(Combos[i-1].id > 711)
tier += "zz1613954562";
else if(Combos[i-1].id > 640)
tier += "zz1613954561";
else if(Combos[i-1].id > 580)
tier += "zz1613954560";
else if(Combos[i-1].id > 556)
tier += "zz1613954500";
else if(Combos[i-1].id > 492)
tier += "zz1613954499";
else if(Combos[i-1].id > 429)
tier += "zz1613954498";
else if(Combos[i-1].id > 366)
tier += "zz1613954497";
else if(Combos[i-1].id > 324)
tier += "zz1613954496";
tier += Combos[i-1].magics[0];
tier += "-and-";
tier += Combos[i-1].magics[1];
tier += "-and-";
tier += Combos[i-1].magics[2];
tier += "-magicpng.png");\"></div>";
Dtier.push_back(tier);
i++;
tier.clear();
}
//repeat for other tiers because ctrl+c/ctrl+v is easier that partitioning one big list later
while(i <= 114+171+171+228+171+171)
{
tier += "<div class=\"character\" id=\"";
tier += to_string(Combos[i-1].id);
tier += "\" style=\"background-image: url("/images/chart/chart/arcane-oddesy-triple-magic-combination-tier-list-841197/";
if(Combos[i-1].id > 1075)
tier += "zz1613954656";
else if(Combos[i-1].id > 1004)
tier += "zz1613954655";
else if(Combos[i-1].id > 931)
tier += "zz1613954654";
else if(Combos[i-1].id > 867)
tier += "zz1613954653";
else if(Combos[i-1].id > 854)
tier += "zz1613954652";
else if(Combos[i-1].id > 853)
tier += "zz1613954564";
else if(Combos[i-1].id > 783)
tier += "zz1613954563";
else if(Combos[i-1].id > 711)
tier += "zz1613954562";
else if(Combos[i-1].id > 640)
tier += "zz1613954561";
else if(Combos[i-1].id > 580)
tier += "zz1613954560";
else if(Combos[i-1].id > 556)
tier += "zz1613954500";
else if(Combos[i-1].id > 492)
tier += "zz1613954499";
else if(Combos[i-1].id > 429)
tier += "zz1613954498";
else if(Combos[i-1].id > 366)
tier += "zz1613954497";
else if(Combos[i-1].id > 324)
tier += "zz1613954496";
tier += Combos[i-1].magics[0];
tier += "-and-";
tier += Combos[i-1].magics[1];
tier += "-and-";
tier += Combos[i-1].magics[2];
tier += "-magicpng.png");\"></div>";
Etier.push_back(tier);
i++;
tier.clear();
}
//repeat for other tiers because ctrl+c/ctrl+v is easier that partitioning one big list later
while(i <= 114+171+171+228+171+171+114)
{
tier += "<div class=\"character\" id=\"";
tier += to_string(Combos[i-1].id);
tier += "\" style=\"background-image: url("/images/chart/chart/arcane-oddesy-triple-magic-combination-tier-list-841197/";
if(Combos[i-1].id > 1075)
tier += "zz1613954656";
else if(Combos[i-1].id > 1004)
tier += "zz1613954655";
else if(Combos[i-1].id > 931)
tier += "zz1613954654";
else if(Combos[i-1].id > 867)
tier += "zz1613954653";
else if(Combos[i-1].id > 854)
tier += "zz1613954652";
else if(Combos[i-1].id > 853)
tier += "zz1613954564";
else if(Combos[i-1].id > 783)
tier += "zz1613954563";
else if(Combos[i-1].id > 711)
tier += "zz1613954562";
else if(Combos[i-1].id > 640)
tier += "zz1613954561";
else if(Combos[i-1].id > 580)
tier += "zz1613954560";
else if(Combos[i-1].id > 556)
tier += "zz1613954500";
else if(Combos[i-1].id > 492)
tier += "zz1613954499";
else if(Combos[i-1].id > 429)
tier += "zz1613954498";
else if(Combos[i-1].id > 366)
tier += "zz1613954497";
else if(Combos[i-1].id > 324)
tier += "zz1613954496";
tier += Combos[i-1].magics[0];
tier += "-and-";
tier += Combos[i-1].magics[1];
tier += "-and-";
tier += Combos[i-1].magics[2];
tier += "-magicpng.png");\"></div>";
Ftier.push_back(tier);
i++;
tier.clear();
}
//time to write to the output file
ofstream out("3magicoutput.txt");
//write each tier separately with a nice indicator for easier inspect elementing
out << "S tier:" << endl << endl;
out << "<div class=\"tier sort\">";
for(int i = 0; i < Stier.size(); i++)
out << Stier[i] << endl;
out << "</div>" << endl << endl;
out << "A tier:" << endl << endl;
out << "<div class=\"tier sort\">";
for(int i = 0; i < Atier.size(); i++)
out << Atier[i] << endl;
out << "</div>" << endl << endl;
out << "B tier:" << endl << endl;
out << "<div class=\"tier sort\">";
for(int i = 0; i < Btier.size(); i++)
out << Btier[i] << endl;
out << "</div>" << endl << endl;
out << "C tier:" << endl << endl;
out << "<div class=\"tier sort\">";
for(int i = 0; i < Ctier.size(); i++)
out << Ctier[i] << endl;
out << "</div>" << endl << endl;
out << "D tier:" << endl << endl;
out << "<div class=\"tier sort\">";
for(int i = 0; i < Dtier.size(); i++)
out << Dtier[i] << endl;
out << "</div>" << endl << endl;
out << "E tier:" << endl << endl;
out << "<div class=\"tier sort\">";
for(int i = 0; i < Etier.size(); i++)
out << Etier[i] << endl;
out << "</div>" << endl << endl;
out << "F tier:" << endl << endl;
out << "<div class=\"tier sort\">";
for(int i = 0; i < Ftier.size(); i++)
out << Ftier[i] << endl;
out << "</div>" << endl << endl;
//profit
return 0;
}
- Should compile with any C++ compiler
- By default, reads from a file called “magicinput.txt”, where magic info is stored in the format that is shown in a comment block at the beginning of the main() function, keep in mind it skips the first 15 lines which by default contain those same notes on the format
- By default, writes to a file called “3magicoutput.txt”, where it creates html for each tier, that can be inspect elemented into the TierMaker tier lists to visualize the results, keep in mind that you should replace the whole “tier sort” type element with the html block for the appropriate tier
- Feel free to yoink it, reuse it, edit it and whatever else you want
Credits
@NoBanana for the tier list templates and ideas that further improved the ranking.
@Meta for the Magic’s document, and I also used his magic strength scoring system with slight modifications to determine the strength of just the stats of each magic.