Flight Rising Wiki

Hi all!

So, I dipped my feet into the water, trying to create the pages for each of the Blooming Grove familiars... And then immediatelly created the items, and played around with creating the galleries, using the Training Fields as a rough template.

...And then I took a deep, deep dive. I want to create a general page for each of the venues. But, this could take some time, compiling all the drops from all the enemies, and then categorizing them, and then alphabetizing them, and then looking up their images.

So I did what any lazy dev does: I (sort of) automated. I made a quick javascript function, used some regular expressions, and now I can make each venue pretty quickly. I thought I would outline my methods here, should I suddenly disappear off the internet and someone decides to take up the mantle in my place, or in case another venue is released later when I don't have as much time to dedicate to creating new pages.

Step 1: Skeleton[]

create the 'outline' of the new page. This involves going to one of the "good" venues that exist (Training Fields or Blooming Grove at the time of this post), editing it in source mode, and pasting this into the new Page that's being added (note that the headers are in place, the Coliseum Venues template is in place under "See Also", and the neat element table under Encoutners exist, but all the values are blank. Also note that when I'm editing there are proper line breaks and spacing, but I didn't think I had to get it perfect for the purposes of explaining my process):

{{Incomplete}} [[File:COLISEUM VENUE NAME Day.png|300px|right]] [[File:COLISEUM VENUE NAME Night.png|300px|right]]'''COLISEUM VENUE NAME''' is a Flight Rising coliseum venue with level X monsters. ==Encounters== <center> {| class="wikitable" style="text-align:center" |- ![[File:Neutral symbol.png|30px|Neutral]] !! [[File:Arcane Rune.png|30px]] !! [[File:Earth Rune.png|30px]] !! [[File:Fire Rune.png|30px]] !! [[File:Ice Rune.png|30px]] !! [[File:Light Rune.png|30px]] !! [[File:Lightning Rune.png|30px]] !! [[File:Nature Rune.png|30px]] !! [[File:Plague Rune.png|30px]] !! [[File:Shadow Rune.png|30px]] !! [[File:Water Rune.png|30px]] !! [[File:Wind Rune.png|30px]] !! Total |- | - || - || - || - || - || - || - || - || - || - || - || - || -'''''<nowiki/>''''' |} </center> <gallery orientation="square" captionalign="center" position="center" spacing="medium" widths="150"> </gallery> ==Possible Loot== == See Also == {{Venues}}

Step 2: Familiars[]

Here you need a list of all the familiars encountered in the venue. For existing venues, that should be pretty easy. For a new Venue, I guess you have to create the pages, and unfortunately I don't have any tips for creating a dozen+ pages. I also recommend Notepad++.

So you'll create a new file, and paste in something like this (kept list shorter for easier reading):

Ancient Fungus
Blue Dragon Reef Snail
Blueband Duelist

Whether by hand or using a program's ability to alphabetize for you (in Notepad++, it's under Edit -> Line Operations -> Sort Lines Lexicographically Ascending), you have to sort the list alphabetically, and make sure each word is capitalized. This is important for something coming up in a sec.

Now, you may also want two copies of this list. I'll explain why in a second. But regardless, now you need to set this up for use in a proper gallery. So use find/replace in Notepad++, with "Regular expression" option bubbled in) to do this:

find: ^(.+)$
replace: $1.png|[[$1]] - 

This will create something that looks like this:

Ancient Fungus.png|[[Ancient Fungus]] - 
Blue Dragon Reef Snail.png|[[Blue Dragon Reef Snail]] - 
Blueband Duelist.png|[[Blueband Duelist]] - 

Great, now comes manual work (blech)- go to each familiar/enemy's page and add their element after that dash. You'll get something like this:

Ancient Fungus.png|[[Ancient Fungus]] - Fire
Blue Dragon Reef Snail.png|[[Blue Dragon Snail]] - Water
Blueband Duelist.png|[[Blueband Duelist]] - Water

Now all that's left is going back to the Source editor. See that <gallery orientation="square" captionalign="center" position="center" spacing="medium" widths="150"> </gallery> Right under the wikitable, under "Encounters"? If you place this between the <gallery> and </gallery> tags, you've filled out the Encounter gallery. Great!

Lastly, you can go back to your list that has familiar.png|[[familiar]] - element, and use another find/replace regex. You can do:

find: ^[^-]+- (.+)$
replace: $1

and you'll get:

Fire
Water
Water 

Now just alphabetize those and you can fill out the encounter element table. Whoo! Halfway there in my opinion.

Step 2: Loot[]

This blog post is a WIP. I will be updating with the next part of my method soon.

Here comes more manual work, which is my absolute favorite thing when working with the Wiki- Now you need to add the drop set or setboxes for each of the enemies encountered in this venue on one page. The script assumes they're all on one page. Which, for me, is easiest to do by going into edit mode (source version, again) for the first familiar on the page, copying everything inside the {{setbox}} or {{set}} under the Drops header, and pasting it into the venue page (again in source editor mode). Most "Drops" setboxes use "{{PAGENAME}} Drops" as the title, which will need to be updated to be the name of the actual enemy (e.g. "Ancient Fungus Drops"). Do this for all the familiars for the venue. When that's done, close out of source mode. You can even publish your changes if you're worried about accidentally losing your progress. What's important is you have a page that is showing the nice visual representation of items with their tooltips.

Now go back to your list of familiars in Notepad++ or wherever (remember when I said you might want two lists? Here's where you can use the second, or you can just undo any changes made to the file until you get back to the list of just names). Use the following find/replace (the commas and quotation marks are intentional):

find: ^(.+)$
replace: '$1 Drops',

You should end up with something like this:

'Ancient Fungus Drops',
'Blue Dragon Reef Snail Drops',
'Blueband Duelist Drops',

Delete the comma after the last entry in the list, add square brackets around the entire list, and put something like 'dropList = ' in front of the left bracket, so it should look like this:

dropList = ['Ancient Fungus Drops',
'Blue Dragon Reef Snail Drops',
'Blueband Duelist Drops']

Now we're ready for some magic. Open the developer tools in your browser (Ctrl + Shift + i in Google Chrome on a Windows machine) and navigate to the console. Copy paste the entire list of familiars you just created into the browser, and hit enter. Now take the following script and copy paste it into the console.

  getTooltips = (selectTexts) => {
  let obj = {
  Food: {
  Insects: [],
  Meat: [],
  Seafood: [],
  Plants: []
  },
  Materials: {
  Dragonmade: [],
  'Minerals & Ores': [],
  Organics: []
  },
  Apparel: [],
  Familiars: [],
  'Battle Items': {
  'Ability Stones': [],
  'Accessory Stones': [],
  'Augment Stones': [],
  'Energy Stones': [],
  Items: []
  },
  Specialty: [],
  Other: {
  Trinkets: [],
  Chests: [],
  'Dragon Eggs': []
  }
  };
  
  selectTexts.forEach(selectText => {
  const b = $(`b:contains(${selectText})`);
  
  if (!b.length) {
  console.error(`could not find table with name "${selectText}"`);
  } else {
  
  const table = b.parents('table');
  const foo = $(table.find('tr')[1]);
  const tooltips = foo.find('.item-tooltip-inner').toArray();
  tooltips.forEach(tooltip => {
  let $tooltip = $(tooltip);
  let name = $tooltip.find('.item-tooltip-name')[0].innerText;
  let type = $tooltip.find('.item-tooltip-type')[0].innerText;
  
  switch (type.toLowerCase()) {
  case 'insect': 
  obj.Food.Insects.push(name);
  break;
  case 'meat':
  obj.Food.Meat.push(name);
  break;
  case 'seafood':
  obj.Food.Seafood.push(name);
  break;
  case 'plant':
  obj.Food.Plants.push(name);
  break;
  case 'dragonmade':
  obj.Materials.Dragonmade.push(name);
  break;
  case 'minerals & ores':
  obj.Materials['Minerals & Ores'].push(name);
  break;
  case 'organic':
  obj.Materials.Organics.push(name);
  break;
  case 'apparel':
  obj.Apparel.push(name);
  break;
  case 'familiar':
  obj.Familiars.push(name);
  break;
  case 'ability stone':
  obj['Battle Items']['Ability Stones'].push(name);
  break;
  case 'accessory stone':
  obj['Battle Items']['Accessory Stones'].push(name);
  break;
  case 'augment stone':
  obj['Battle Items']['Augment Stones'].push(name);
  break;
  case 'energy stone':
  obj['Battle Items']['Energy Stones'].push(name);
  break;
  case 'battle item':
  obj['Battle Items'].Items.push(name);
  break;
  case 'specialty': 
  case 'specialty item':
  case 'specialty items':
  case 'forum vista':
  obj.Specialty.push(name);
  break;
  case 'trinket':
  obj.Other.Trinkets.push(name);
  break;
  case 'chest':
  obj.Other.Chests.push(name);
  break;
  case 'dragon egg':
  obj.Other['Dragon Eggs'].push(name);
  break;
  default:
  console.group(`cannot categorize item from drop "${selectText}"`);
  console.error('name: ', name);
  console.error(`unknown type: "${type}"`);
  console.error('tooltip: ', tooltip);
  console.groupEnd();
  break;
  }
  });
  }
  });
  
  const galleryString = '<gallery captionalign="center">\n';
  	const galleryEndString = '</gallery>\n';
  
  const createHeader = (header, withGallery = false ) => {
  let ret = `===${header}===\n`;
  
  if (withGallery) {
  ret += galleryString;
  }
  
  return ret;
  };
  
  const createSubHeader = (header) => {
  return `''${header}''\n` + galleryString;
  }
  
  const generateItemTemplate = (item, addOnText = '') => {''
  return `${item + addOnText}.png|[[${item}]]\n`;
  };
  
  const orderArr = (arr) => {
  let ret = [];
  
  if (!!arr && arr.length) {
  
  let namesArr = [];
  
  ret = arr.filter(el => {
  let ret = true;
  
  if (!namesArr.includes(el)) {
  namesArr.push(el);
  } else {
  ret = false;
  }
  
  return ret;
  }).sort();
  }
  
  return ret;
  };
  
  const generateGalleryTemplate = (arr, addOnText = '') => {''
  arr = orderArr(arr);
  
  let foo = (item) => generateItemTemplate(item, addOnText);
  
  const ret = arr.map(foo).join('');''
  return ret + galleryEndString;
  }
  
  const getFoodTemplate = (obj) => {
  let ret = ``;
  const fobj = obj.Food;
  
  if (fobj.Insects.length > 0 ||
  fobj.Meat.length > 0 ||
  fobj.Seafood.length > 0 ||
  fobj.Plants.length > 0) {
  ret = createHeader('Food');
  
  if (fobj.Insects.length > 0) {
  ret += createSubHeader('Insects');
  ret += generateGalleryTemplate(fobj.Insects);
  }
  if (fobj.Meat.length > 0) {
  ret += createSubHeader('Meat');
  ret += generateGalleryTemplate(fobj.Meat);
  }
  if (fobj.Seafood.length > 0) {
  ret += createSubHeader('Seafood');
  ret += generateGalleryTemplate(fobj.Seafood);
  }
  if (fobj.Plants.length > 0) {
  ret += createSubHeader('Plants');
  ret += generateGalleryTemplate(fobj.Plants);
  }
  }
  
  return ret;
  };
  
  const getMaterialsTemplate = (obj) => {
  let ret = ``;
  const mobj = obj.Materials;
  
  if (mobj.Dragonmade.length > 0 ||
  mobj['Minerals & Ores'].length > 0 ||
  mobj.Organics.length > 0) {
  ret = createHeader('Materials');
  
  if (mobj.Dragonmade.length > 0) {
  ret += createSubHeader('Dragonmade');
  ret += generateGalleryTemplate(mobj.Dragonmade);
  }
  if (mobj['Minerals & Ores'].length > 0) {
  ret += createSubHeader('Minerals & Ores');
  ret += generateGalleryTemplate(mobj['Minerals & Ores']);
  }
  if (mobj.Organics.length > 0) {
  ret += createSubHeader('Organics');
  ret += generateGalleryTemplate(mobj.Organics);
  }
  }
  
  return ret;
  };
  
  const getApparelTemplate = (obj) => {
  let ret = ``;
  const aobj = obj.Apparel;
  
  if (aobj.length > 0) {
  ret = createHeader('Apparel', true);
  ret += generateGalleryTemplate(aobj);
  }
  
  return ret;
  };
  
  const getFamiliarsTemplate = (obj) => {
  let ret = ``;
  const fobj = obj.Familiars;
  
  if (fobj.length > 0) {
  ret = createHeader('Familiars', true);
  ret += generateGalleryTemplate(fobj, ' Icon');
  }
  
  return ret;
  };
  
  const getBattleItemsTemplate = (obj) => {
  let ret = ``;
  const bobj = obj['Battle Items'];
  
  if (bobj['Ability Stones'].length > 0 ||
  bobj['Accessory Stones'].length > 0 ||
  bobj['Augment Stones'].length > 0 ||
  bobj['Energy Stones'].length > 0 ||
  bobj.Items.length > 0) {
  ret = createHeader('[[Battle Stones|Battle Items]]');
  
  if (bobj['Ability Stones'].length > 0) {
  ret += createSubHeader('Ability Stones');
  ret += generateGalleryTemplate(bobj['Ability Stones']);
  }
  if (bobj['Accessory Stones'].length > 0) {
  ret += createSubHeader('Accessory Stones');
  ret += generateGalleryTemplate(bobj['Accessory Stones']);
  }
  if (bobj['Augment Stones'].length > 0) {
  ret += createSubHeader('Augment Stones');
  ret += generateGalleryTemplate(bobj['Augment Stones']);
  }
  if (bobj['Energy Stones'].length > 0) {
  ret += createSubHeader('Energy Stones');
  ret += generateGalleryTemplate(bobj['Energy Stones']);
  }
  if (bobj.Items.length > 0) {
  ret += createSubHeader('Items');
  ret += generateGalleryTemplate(bobj.Items);
  }
  }
  
  return ret;
  };
  
  const getSpecialtyTemplate = (obj) => {
  let ret = ``;
  const sobj = obj.Specialty;
  
  if (sobj.length > 0) {
  ret = createHeader('Specialty', true);
  ret += generateGalleryTemplate(sobj);
  }
  
  return ret;
  };
  
  const getOtherTemplate = (obj) => {
  let ret = ``;
  const oobj = obj.Other;
  
  if (oobj.Trinkets.length > 0 ||
  oobj.Chests.length > 0 ||
  oobj['Dragon Eggs'].length > 0) {
  ret = createHeader('Materials');
  
  if (oobj.Trinkets.length > 0) {
  ret += createSubHeader('Trinkets');
  ret += generateGalleryTemplate(oobj.Trinkets);
  }
  if (oobj.Chests.length > 0) {
  ret += createSubHeader('Chests');
  ret += generateGalleryTemplate(oobj.Chests);
  }
  if (oobj['Dragon Eggs'].length > 0) {
  ret += createSubHeader('Dragon Eggs');
  ret += generateGalleryTemplate(oobj['Dragon Eggs']);
  }
  }
  
  return ret;
  };
  
  const printObj = (obj) => {
  let ret = `==Possible Loot==\n`;
  
  ret += getFoodTemplate(obj);
  ret += getMaterialsTemplate(obj);
  ret += getApparelTemplate(obj);
  ret += getFamiliarsTemplate(obj);
  ret += getBattleItemsTemplate(obj);
  ret += getSpecialtyTemplate(obj);
  ret += getOtherTemplate(obj);
  
  console.log(ret);
  };
  
  printObj(obj);
  };
 


Now, assuming you didn't take the time to read through all that script (and you might not be familiar with javascript, and I may not have figured out how to display it in a nice, pretty way yet)- I'll summarize: This basically tries to find sets on the page and automatically extract the item names, sorting them, ordering them, and then printing out the complete source template for the "Possible Loot" section of the venue. But we have to run it first. In the console run the following:

getTooltips(dropList);

note that "dropList" has to match whatever value you used in the declaration of your array, the first thing you pasted into the console.

Now, there's the possibility that, after running this, one or more errors have occurred. If an item's page is using the wrong template for info information, or using an item category that is not recognized (e.g. the script is looking for "Organic", not "Organics"), the script can't correctly place it. The error message should specifically point out which set is causing trouble, as well as which item name. These have to be fixed if you want the printed out template to include all the available loot in the Coliseum. So now go through each one and try and figure out why it's wrong.

Once there are no more errors, you can run getTooltips(dropList) again, copy the output, and you're almost there. Paste it in the appropriate place on the page, deleting the sets as you do so.

Now, it's time for the last step- the template generator assumes the image for each item is {{PAGENAME}}.png or, in the case of familiars {{PAGENAME}} Icon.png and capitalization matters. So if an item's image is mismatched (e.g. the Aqua Birdskull Headdress item uses the image Aqua birdskull headdress.png, where the "b" and "h" are not appropriately capitalized to match the item), you'll have to manually update.

But, once those errors are fixed, the venue should be pretty much done! Add a note to the introductory paragraph about how many familiars, anything of note, and then remove the Incomplete Template and you're done!