Merge upstream

This commit is contained in:
Vincent Lee committed 2016-01-21 22:04:45 -06:00
commit 77cd24b58a
10 files changed
+228 -78

No files matched your search

@@ -18,9 +18,14 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import net.minecraft.block.*;
import net.minecraft.block.BlockCarpet;
import net.minecraft.block.BlockColored;
import net.minecraft.block.BlockStainedGlass;
import net.minecraft.block.BlockStainedGlassPane;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.IBlockState;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.EnumDyeColor;
@@ -67,7 +72,10 @@ public final class BotaniaAPI {
public static Map<String, KnowledgeType> knowledgeTypes = new HashMap<>();
public static Map<String, Brew> brewMap = new LinkedHashMap<>();
public static Map<String, Brew> brewMap = new LinkedHashMap<String, Brew>();
public static List<String> disposableBlocks = new ArrayList<String>();
public static List<String> semiDisposableBlocks = new ArrayList<String>();
public static List<RecipePetals> petalRecipes = new ArrayList<>();
public static List<RecipePureDaisy> pureDaisyRecipes = new ArrayList<>();
@@ -90,6 +98,7 @@ public final class BotaniaAPI {
public static Set<Item> looniumBlacklist = new LinkedHashSet<>();
public static Map<Block, PropertyEnum<EnumDyeColor>> paintableBlocks = new LinkedHashMap<>();
public static Set<String> magnetBlacklist = new LinkedHashSet<>();
public static Set<Class<? extends Entity>> gravityRodBlacklist = new LinkedHashSet<Class<? extends Entity>>();
public static ArmorMaterial manasteelArmorMaterial = EnumHelper.addArmorMaterial("MANASTEEL", "manasteel", 16, new int[] { 2, 6, 5, 2 }, 18);
@@ -241,6 +250,16 @@ public final class BotaniaAPI {
registerPaintableBlock(Blocks.stained_hardened_clay, BlockColored.COLOR);
registerPaintableBlock(Blocks.wool, BlockColored.COLOR);
registerPaintableBlock(Blocks.carpet, BlockCarpet.COLOR);
registerDisposableBlock("dirt"); // Vanilla
registerDisposableBlock("sand"); // Vanilla
registerDisposableBlock("gravel"); // Vanilla
registerDisposableBlock("cobblestone"); // Vanilla
registerDisposableBlock("netherrack"); // Vanilla
registerSemiDisposableBlock("stoneAndesite"); // Vanilla
registerSemiDisposableBlock("stoneBasalt"); // Vanilla
registerSemiDisposableBlock("stoneDiorite"); // Vanilla
registerSemiDisposableBlock("stoneGranite"); // Vanilla
}
/**
@@ -280,6 +299,21 @@ public final class BotaniaAPI {
return fallbackBrew;
}
/*
* Registers a Block as disposable using its Ore Dictionary Name.
*/
public static void registerDisposableBlock(String oreDictName) {
disposableBlocks.add(oreDictName);
}
/*
* Registers a Block as semi disposable using its Ore Dictionary Name.
* This means it will not be trashed when sneaking.
*/
public static void registerSemiDisposableBlock(String oreDictName) {
semiDisposableBlocks.add(oreDictName);
}
/**
* Registers a paintableBlock and returns it.
* You must also provide the PropertyEnum that this block uses to express its color
@@ -290,6 +324,22 @@ public final class BotaniaAPI {
return paintable;
}
/*
* Blacklists an Entity from being affected by the Rod of the Shaded Mesa.
* Pass in the class for the Entity, e.g. EntityCow.class
*/
public static void blacklistEntityFromGravityRod(Class entity) {
gravityRodBlacklist.add(entity);
}
/*
* Checks if the provided Entity is contained in the Blacklist.
* Pass in the class for the Entity, e.g. entity.getClass()
*/
public static boolean isEntityBlacklistedFromGravityRod(Class entity) {
return gravityRodBlacklist.contains(entity);
}
/**
* Blacklists an item from being pulled by the Ring of Magnetization.
* Short.MAX_VALUE can be used as the stack's damage for a wildcard.
@@ -1,4 +1,4 @@
@API(owner = "Botania", apiVersion = "75", provides = "BotaniaAPI")
@API(owner = "Botania", apiVersion = "76", provides = "BotaniaAPI")
package vazkii.botania.api;
import net.minecraftforge.fml.common.API;
@@ -12,10 +12,12 @@ package vazkii.botania.common.item;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import net.minecraft.block.Block;
import net.minecraft.block.BlockDirt;
@@ -41,7 +43,11 @@ import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
public class ItemGrassSeeds extends ItemMod implements IFloatingFlowerVariant {
private static Map<Integer, List<BlockSwapper>> blockSwappers = new HashMap();
/**
* Represents a map of dimension IDs to a set of all block swappers
* active in that dimension.
*/
private static Map<Integer, Set<BlockSwapper>> blockSwappers = new HashMap<Integer, Set<BlockSwapper>>();
private static final IslandType[] ISLAND_TYPES = {
IslandType.GRASS, IslandType.PODZOL, IslandType.MYCEL,
@@ -150,28 +156,52 @@ public class ItemGrassSeeds extends ItemMod implements IFloatingFlowerVariant {
@SubscribeEvent
public void onTickEnd(TickEvent.WorldTickEvent event) {
// Block swapper updates should only occur on the server
if(event.world.isRemote)
return;
if(event.phase == Phase.END) {
int dim = event.world.provider.getDimensionId();
if(blockSwappers.containsKey(dim)) {
List<BlockSwapper> swappers = blockSwappers.get(dim);
Set<BlockSwapper> swappers = blockSwappers.get(dim);
Iterator<BlockSwapper> iter = swappers.listIterator();
Iterator<BlockSwapper> iter = swappers.iterator();
while(iter.hasNext()) {
BlockSwapper next = iter.next();
if(!next.tick())
if(next == null || !next.tick())
iter.remove();
}
}
}
}
/**
* Adds a grass seed block swapper to the world at the provided positiona
* and with the provided meta (which designates the type of the grass
* being spread).
*
* Block swappers are only actually created on the server, so a client
* calling this method will recieve a marker block swapper which contains
* the provided information but is not ticked.
* @param world The world the swapper will be in.
* @param pos The position of the swapper.
* @param meta The meta value representing the type of block being swapped.
* @return The created block swapper.
*/
private static BlockSwapper addBlockSwapper(World world, BlockPos pos, int meta) {
BlockSwapper swapper = swapperFromMeta(world, pos, meta);
// Block swappers are only registered on the server
if(world.isRemote)
return swapper;
// If a set for the dimension doesn't exist, create it.
int dim = world.provider.getDimensionId();
if(!blockSwappers.containsKey(dim))
blockSwappers.put(dim, new ArrayList());
blockSwappers.put(dim, new HashSet<BlockSwapper>());
// Add the block swapper
blockSwappers.get(dim).add(swapper);
return swapper;
@@ -44,13 +44,16 @@ public class ItemManaTablet extends ItemMod implements IManaItem, ICreativeManaP
@Override
public void getSubItems(Item par1, CreativeTabs par2CreativeTabs, List par3List) {
par3List.add(new ItemStack(par1, 1, 10000));
// Empty tablet
par3List.add(new ItemStack(par1, 1));
ItemStack fullPower = new ItemStack(par1, 1, 1);
// Full tablet
ItemStack fullPower = new ItemStack(par1, 1);
setMana(fullPower, MAX_MANA);
par3List.add(fullPower);
ItemStack creative = new ItemStack(par1, 1, 0);
// Creative Tablet
ItemStack creative = new ItemStack(par1, 1);
setMana(creative, MAX_MANA);
setStackCreative(creative);
par3List.add(creative);
@@ -64,8 +67,12 @@ public class ItemManaTablet extends ItemMod implements IManaItem, ICreativeManaP
@Override
public int getDamage(ItemStack stack) {
float mana = getMana(stack);
return 1000 - (int) (mana / MAX_MANA * 1000);
// Compatibility shim, so tablets from previous versions of botania
// stack right in barrels and so forth
if(super.getDamage(stack) != 0)
super.setDamage(stack, 0);
return 0;
}
@Override
@@ -141,4 +148,18 @@ public class ItemManaTablet extends ItemMod implements IManaItem, ICreativeManaP
public float getManaFractionForDisplay(ItemStack stack) {
return (float) getMana(stack) / (float) getMaxMana(stack);
}
@Override
public boolean showDurabilityBar(ItemStack stack) {
// If the stack is not creative, show the durability bar.
return !isStackCreative(stack);
}
@Override
public double getDurabilityForDisplay(ItemStack stack) {
// I believe Forge has their durability values swapped, hence the (1.0 -).
// This will probably be fixed soon.
return 1.0 - getManaFractionForDisplay(stack);
}
}
@@ -129,14 +129,16 @@ public abstract class ItemBauble extends ItemMod implements IBauble, ICosmeticAt
@Override
public void onEquipped(ItemStack stack, EntityLivingBase player) {
if(!player.worldObj.isRemote)
player.worldObj.playSoundAtEntity(player, "botania:equipBauble", 0.1F, 1.3F);
if(player != null) {
if(!player.worldObj.isRemote)
player.worldObj.playSoundAtEntity(player, "botania:equipBauble", 0.1F, 1.3F);
if(player instanceof EntityPlayer)
((EntityPlayer) player).addStat(ModAchievements.baubleWear, 1);
if(player instanceof EntityPlayer)
((EntityPlayer) player).addStat(ModAchievements.baubleWear, 1);
onEquippedOrLoadedIntoWorld(stack, player);
setLastPlayerHashcode(stack, player.hashCode());
onEquippedOrLoadedIntoWorld(stack, player);
setLastPlayerHashcode(stack, player.hashCode());
}
}
public void onEquippedOrLoadedIntoWorld(ItemStack stack, EntityLivingBase player) {
@@ -17,10 +17,6 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
public class ItemElementiumPick extends ItemManasteelPick {
static final List<String> validBlocks = Arrays.asList(new String[] {
"dirt", "sand", "gravel", "cobblestone", "netherrack"
});
public ItemElementiumPick() {
super(BotaniaAPI.elementiumToolMaterial, LibItemNames.ELEMENTIUM_PICK);
MinecraftForge.EVENT_BUS.register(this);
@@ -30,24 +26,36 @@ public class ItemElementiumPick extends ItemManasteelPick {
public void onHarvestDrops(HarvestDropsEvent event) {
if(event.harvester != null) {
ItemStack stack = event.harvester.getCurrentEquippedItem();
if(stack != null && (stack.getItem() == this || stack.getItem() == ModItems.terraPick && ItemTerraPick.isTipped(stack)))
if(stack != null && (stack.getItem() == this || stack.getItem() == ModItems.terraPick && ItemTerraPick.isTipped(stack))) {
for(int i = 0; i < event.drops.size(); i++) {
ItemStack drop = event.drops.get(i);
if(drop != null) {
Block block = Block.getBlockFromItem(drop.getItem());
if(block != null && isDisposable(block))
event.drops.remove(i);
if(block != null){
if(isDisposable(block) || (isSemiDisposable(block) && !event.harvester.isSneaking()))
event.drops.remove(i);
}
}
}
}
}
}
public static boolean isDisposable(Block block) {
for(int id : OreDictionary.getOreIDs(new ItemStack(block)))
if(validBlocks.contains(OreDictionary.getOreName(id)))
for(int id : OreDictionary.getOreIDs(new ItemStack(block))) {
String name = OreDictionary.getOreName(id);
if(BotaniaAPI.disposableBlocks.contains(name))
return true;
}
return false;
}
public static boolean isSemiDisposable(Block block) {
for(int id : OreDictionary.getOreIDs(new ItemStack(block))) {
String name = OreDictionary.getOreName(id);
if(BotaniaAPI.semiDisposableBlocks.contains(name))
return true;
}
return false;
}
}
@@ -65,8 +65,12 @@ public class ItemTerraAxe extends ItemManasteelAxe implements ISequentialBreaker
* The amount of mana required to restore 1 point of damage.
*/
private static final int MANA_PER_DAMAGE = 100;
private static Map<Integer, List<BlockSwapper>> blockSwappers = new HashMap<Integer, List<BlockSwapper>>();
/**
* Represents a map of dimension IDs to a set of all block swappers
* active in that dimension.
*/
private static Map<Integer, Set<BlockSwapper>> blockSwappers = new HashMap<Integer, Set<BlockSwapper>>();
public ItemTerraAxe() {
super(BotaniaAPI.terrasteelToolMaterial, LibItemNames.TERRA_AXE);
@@ -107,30 +111,57 @@ public class ItemTerraAxe extends ItemManasteelAxe implements ISequentialBreaker
@SubscribeEvent
public void onTickEnd(TickEvent.WorldTickEvent event) {
// Block Swapping ticking should only occur on the server
if(event.world.isRemote)
return;
if(event.phase == Phase.END) {
int dim = event.world.provider.getDimensionId();
if(blockSwappers.containsKey(dim)) {
List<BlockSwapper> swappers = blockSwappers.get(dim);
Set<BlockSwapper> swappers = blockSwappers.get(dim);
// Iterate through all of our swappers, removing any
// which no longer need to tick.
Iterator<BlockSwapper> swapper = swappers.iterator();
while(swapper.hasNext()) {
BlockSwapper next = swapper.next();
if(!next.tick())
// If a null sneaks in or the swapper is done, remove it
if(next == null || !next.tick())
swapper.remove();
}
}
}
}
/**
* Adds a new block swapper to the provided world as the provided player.
* Block swappers are only added on the server, and a marker instance
* which is not actually ticked but contains the proper passed in
* information will be returned to the client.
*
* @param world The world to add the swapper to.
* @param player The player who is responsible for this swapper.
* @param stack The Terra Truncator which caused this block swapper.
* @param origCoords The original coordinates the swapper should start at.
* @param steps The range of the block swapper, in blocks.
* @param leaves If true, will treat leaves specially (see the BlockSwapper
* documentation).
* @return The created block swapper.
*/
private static BlockSwapper addBlockSwapper(World world, EntityPlayer player, ItemStack stack, BlockPos origCoords, int steps, boolean leaves) {
BlockSwapper swapper = new BlockSwapper(world, player, stack, origCoords, steps, leaves);
// Block swapper registration should only occur on the server
if(world.isRemote)
return swapper;
// If the mapping for this dimension doesn't exist, create it.
int dim = world.provider.getDimensionId();
if(!blockSwappers.containsKey(dim))
blockSwappers.put(dim, new ArrayList<BlockSwapper>());
blockSwappers.put(dim, new HashSet<BlockSwapper>());
// Add the swapper
blockSwappers.get(dim).add(swapper);
return swapper;
@@ -23,6 +23,7 @@ import net.minecraft.potion.PotionEffect;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import vazkii.botania.api.BotaniaAPI;
import vazkii.botania.api.item.IManaProficiencyArmor;
import vazkii.botania.api.mana.IManaUsingItem;
import vazkii.botania.api.mana.ManaItemHandler;
@@ -82,11 +83,12 @@ public class ItemGravityRod extends ItemMod implements IManaUsingItem {
int targetID = ItemNBTHelper.getInt(stack, TAG_TARGET, -1);
int ticksCooldown = ItemNBTHelper.getInt(stack, TAG_TICKS_COOLDOWN, 0);
double length = ItemNBTHelper.getDouble(stack, TAG_DIST, -1);
if(ticksCooldown == 0) {
Entity item = null;
if(targetID != -1 && player.worldObj.getEntityByID(targetID) != null) {
Entity taritem = player.worldObj.getEntityByID(targetID);
boolean found = false;
Vector3 target = Vector3.fromEntityCenter(player);
List<Entity> entities = new ArrayList<>();
@@ -125,38 +127,43 @@ public class ItemGravityRod extends ItemMod implements IManaUsingItem {
}
}
if(ManaItemHandler.requestManaExactForTool(stack, player, COST, true) && item != null) {
if(item instanceof EntityItem)
ObfuscationReflectionHelper.setPrivateValue(EntityItem.class, ((EntityItem) item), 5, LibObfuscation.PICKUP_DELAY);
if(item != null) {
if(BotaniaAPI.isEntityBlacklistedFromGravityRod(item.getClass()))
return stack;
if(ManaItemHandler.requestManaExactForTool(stack, player, COST, true)) {
if(item instanceof EntityItem)
ObfuscationReflectionHelper.setPrivateValue(EntityItem.class, ((EntityItem) item), 5, LibObfuscation.PICKUP_DELAY);
if(item instanceof EntityLivingBase) {
EntityLivingBase targetEntity = (EntityLivingBase)item;
targetEntity.fallDistance = 0.0F;
if(targetEntity.getActivePotionEffect(Potion.moveSlowdown) == null)
targetEntity.addPotionEffect(new PotionEffect(Potion.moveSlowdown.id, 2, 3, true, true));
if(item instanceof EntityLivingBase) {
EntityLivingBase targetEntity = (EntityLivingBase)item;
targetEntity.fallDistance = 0.0F;
if(targetEntity.getActivePotionEffect(Potion.moveSlowdown) == null)
targetEntity.addPotionEffect(new PotionEffect(Potion.moveSlowdown.id, 2, 3, true, true));
}
Vector3 target3 = Vector3.fromEntityCenter(player);
target3.add(new Vector3(player.getLookVec()).multiply(length));
target3.y += 0.5;
if(item instanceof EntityItem)
target3.y += 0.25;
for(int i = 0; i < 4; i++) {
float r = 0.5F + (float) Math.random() * 0.5F;
float b = 0.5F + (float) Math.random() * 0.5F;
float s = 0.2F + (float) Math.random() * 0.1F;
float m = 0.1F;
float xm = ((float) Math.random() - 0.5F) * m;
float ym = ((float) Math.random() - 0.5F) * m;
float zm = ((float) Math.random() - 0.5F) * m;
Botania.proxy.wispFX(world, item.posX + item.width / 2, item.posY + item.height / 2, item.posZ + item.width / 2, r, 0F, b, s, xm, ym, zm);
}
setEntityMotionFromVector(item, target3, 0.3333333F);
ItemNBTHelper.setInt(stack, TAG_TARGET, item.getEntityId());
ItemNBTHelper.setDouble(stack, TAG_DIST, length);
}
Vector3 target3 = Vector3.fromEntityCenter(player);
target3.add(new Vector3(player.getLookVec()).multiply(length));
target3.y += 0.5;
if(item instanceof EntityItem)
target3.y += 0.25;
for(int i = 0; i < 4; i++) {
float r = 0.5F + (float) Math.random() * 0.5F;
float b = 0.5F + (float) Math.random() * 0.5F;
float s = 0.2F + (float) Math.random() * 0.1F;
float m = 0.1F;
float xm = ((float) Math.random() - 0.5F) * m;
float ym = ((float) Math.random() - 0.5F) * m;
float zm = ((float) Math.random() - 0.5F) * m;
Botania.proxy.wispFX(world, item.posX + item.width / 2, item.posY + item.height / 2, item.posZ + item.width / 2, r, 0F, b, s, xm, ym, zm);
}
setEntityMotionFromVector(item, target3, 0.3333333F);
ItemNBTHelper.setInt(stack, TAG_TARGET, item.getEntityId());
ItemNBTHelper.setDouble(stack, TAG_DIST, length);
}
if(item != null)
@@ -188,6 +195,7 @@ public class ItemGravityRod extends ItemMod implements IManaUsingItem {
int targetID = ItemNBTHelper.getInt(stack, TAG_TARGET, -1);
ItemNBTHelper.getDouble(stack, TAG_DIST, -1);
Entity item = null;
if(targetID != -1 && player.worldObj.getEntityByID(targetID) != null) {
Entity taritem = player.worldObj.getEntityByID(targetID);
@@ -34,15 +34,15 @@ botaniamisc.toolRank=%s&7 級
botaniamisc.edition=%s 版本
# ^ Leave this empty for no edition display
botaniamisc.devEdition=作者的
botaniamisc.lexiconcover0=在植物魔法中
botaniamisc.lexiconcover1=你所需的指南
botaniamisc.lexiconcover2=來自暢銷書作者
botaniamisc.lexiconcover3=Lizbeth "Vazkii" Rika
botaniamisc.lexiconcover0=一本由 Lizbeth "Vazkii"
botaniamisc.lexiconcover1=Whitewood 所撰寫的
botaniamisc.lexiconcover2=官方指引手冊,詳述
botaniamisc.lexiconcover3=所有植物魔法所需知識
botaniamisc.lexiconcover4=由BevoLJ編輯
botaniamisc.lexiconcover5=由Saice編輯
botaniamisc.shiftinfo=按住&bSHIFT&7顯示更多
botaniamisc.knowledgeTypesSingular=內含%s個世界的知識
botaniamisc.knowledgeTypesPlural=內含%s個世界的知識
botaniamisc.knowledgeTypesSingular=包含%s個世界的知識
botaniamisc.knowledgeTypesPlural=包含%s個世界的知識
botaniamisc.bookmark=書籤 #%s
botaniamisc.clickToSee=點擊閱讀
botaniamisc.shiftToRemove=Shift點擊移除
@@ -146,7 +146,7 @@ botaniamisc.badArena=只要你努力,信標就不會讓你白白犧牲。你
botaniamisc.tutorialText=Ingame
botaniamisc.tutorialVideo=Video
botaniamisc.shiftToStopSpin=(SHIFT以停止動畫)
botaniamisc.noVideo=&4視頻教程很快就有,抱歉 :(
botaniamisc.noVideo=&4視頻教程很快就有 ,抱歉 :(
botaniamisc.adaptativeConfigChanges=Botania自調整系統為你調整了以下默認設定:
botaniamisc.altarRefill0=用空手右擊
botaniamisc.altarRefill1=返回最後一個合成
@@ -1951,7 +1951,7 @@ botania.page.welcome0=&o什麼是和平?是沒有鬥爭?是 年復一年的待
botania.page.welcome1=&o和平是不需勞作就能獲得之 物-又或是難以獲得之物?<br>讓我們來簡單地說說-只需要 一會兒-和平其實就是無需工 作;可以過著被花朵所包圍的 和平日子.
botania.page.welcome2=&o看著你的植物們如預期般的 完成各自的任務,就這樣感受 著時間的流逝,沒有嘈雜且汙 染四布的機器和電腦,也沒有 密佈的線纜和管道.<br>現在想像一下,我們所討論的 這一切都是可能實現的.
botania.page.welcome3=&o想像下,經過你的一番努力- 對植物的精心培養,對瑪那的 使用,對於這個世界和紅石的 知識運用-你就能達到那種夢 寐以求的境界.想像下你所需 求的一切都能通過植物來實 現自動化.植物,加上些瑪那, 還有你的智慧.
botania.page.welcome4=&o有了這本書,你就能夢想成真 了.<br>書名只是表面的.儘管稱它植 物瑪那辭典,但它所涉及的可 不僅僅都是花.其中也涵蓋了 各種瑪那和近似科技的裝置.
botania.page.welcome4=&o有了這本書,你就能夢想成真 了.<br>書名只是表面的.儘管稱它植 物魔法辭典,但它所涉及的可 不僅僅都是花.其中也涵蓋了 各種魔法和近似科技的裝置.
botania.page.welcome5=&o它裡面也蘊藏著精靈的秘密, 而精靈們卻早已帶著世界起 源的線索一同消失在了這個 世界上.對於每一個有著基礎 紅石知識並能將其投入實踐 的人而言,這本書為每一個人 們所知的問題給出了答案,有 了它你就再也不需要學習那 些不明所以的瑪那和機械知 識了.
botania.page.welcome6=&o書的內容也包括了你所要進 行的每一項任務的解釋說明, 只需要用上書中的東西,確保 在Minecraft的世界裡可行, 然後動用你的聰明才智,難題 就都不是問題了.<br>試試看,你會吃驚於自己在沒 有外界干擾的情況下竟然可 以做到這麼多.
+2 -2
View File
@@ -6,7 +6,7 @@
<ul>
<li id="q1-fake"><b>Can I use Botania in a modpack, let's play, or its code in my mod?</b> <a href="#q1">(permalink)</a><br>
Have a look through the <a href="http://localhost/xampp/mod/Botania/license.php">license</a> section of the website.</li><br>
Have a look through the <a href="http://botaniamod.net/license.php">license</a> section of the website.</li><br>
<li id="q2-fake"><b>Is Botania opensource?</b> <a href="#q2">(permalink)</a><br>
Yes, <a href="https://github.com/Vazkii/Botania">yes it is</a>.</li><br>
@@ -53,4 +53,4 @@ Yes, there is <a href="https://github.com/Vazkii/Botania/tree/master/src/main/ja
<li id="q15-fake"><b>What is the airspeed velocity of an unladen swallow?</b> <a href="#q16">(permalink)</a><br>
African or European?</li>
</ul>
</ul>