Handbook rewrite part 1: Formatting and JSON-ification!
- Everything is now defined in a JSON file - Formatting is now done dynamically and content wraps properly no matter what font is used - Added inline hyperlinks - Added a movable bookmark for quick access to a specific page - Added next section and previous section buttons to flip through entire sections at once - Added a contents button visible on every page, which navigates directly to the main contents
This commit is contained in:
@@ -0,0 +1,566 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.ClientProxy;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.client.gui.GuiButtonInvisible;
|
||||
import electroblob.wizardry.client.gui.handbook.GuiButtonTurnPage.Type;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.audio.SoundHandler;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.multiplayer.ClientAdvancementManager;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.event.entity.player.AdvancementEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.ReflectionHelper;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* GUI class for the wizard's handbook. Like any GUI class, this is instantiated each time the book is opened. As of
|
||||
* Wizardry 4.2, the handbook text is defined as a JSON file rather than a plain text file, and is loaded only on
|
||||
* resource pack reload, rather than every time the book is opened. This means all the data structures (sections, images,
|
||||
* etc.) are built before the GUI instance exists at all. However, since some things depend on positioning, these have to
|
||||
* be initialised on GUI creation. (Previously, everything was done on GUI load)
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.0
|
||||
* @see Section
|
||||
* @see Contents
|
||||
* @see Image
|
||||
* @see CraftingRecipe
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
private static final ResourceLocation DEFAULT = new ResourceLocation(Wizardry.MODID, "texts/handbook_en_us.json");
|
||||
|
||||
static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
|
||||
|
||||
/** Global Gson instance for the handbook. */
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
// Formatting markup
|
||||
|
||||
static final char FORMAT_MARKER = '#';
|
||||
static final char HYPERLINK_MARKER = '@';
|
||||
|
||||
static final String IMAGE_TAG = "image";
|
||||
static final String RECIPE_TAG = "recipe";
|
||||
|
||||
static final Map<String, String> FORMAT_TAGS = new HashMap<>();
|
||||
|
||||
// Dimension constants
|
||||
// Private constants are not relevant to book elements, package-protected ones are
|
||||
|
||||
/** The dimensions of the rendered GUI area. */
|
||||
static final int GUI_WIDTH = 288, GUI_HEIGHT = 180;
|
||||
/** The dimensions of the area of a single page in which text can be drawn. */
|
||||
static final int PAGE_WIDTH = 120, PAGE_HEIGHT = 140;
|
||||
/** The distance of the text from the top outside corner of each page. */
|
||||
static final int TEXT_INSET_X = 17, TEXT_INSET_Y = 16;
|
||||
/** The distance of the buttons from the bottom outside corners of the GUI. */
|
||||
private static final int BUTTON_INSET_X = 22, BUTTON_INSET_Y = 13;
|
||||
/** The distance between adjacent buttons. */
|
||||
private static final int BUTTON_SPACING = 20;
|
||||
/** The distance of the page numbers from the bottom of the GUI. */
|
||||
private static final int PAGE_NUMBER_INSET = 22;
|
||||
|
||||
// IDEA: Constant dimensions could be converted to JSON like the spell HUD ones
|
||||
|
||||
// Global variables
|
||||
|
||||
/**
|
||||
* The <b>double-page</b> currently being viewed. Each double-page spread counts as a single page, with the inside
|
||||
* of the front cover being page 0.
|
||||
*/
|
||||
private int currentPage = 0;
|
||||
/**
|
||||
* The number of <b>single</b> pages currently in the book. This is calculated on GUI load based on visible sections.
|
||||
*/
|
||||
private int pageCount = 1; // Starts at 1 because the first single-page is the inside of the cover
|
||||
/**
|
||||
* The <b>double-page</b> number where the bookmark is currently set, <b>relative to the section stored in
|
||||
* {@link GuiWizardHandbook#bookmarkSection}</b>. Static because it persists when the book is closed.
|
||||
*/
|
||||
private static int bookmarkPage = 1;
|
||||
/**
|
||||
* A reference to the section in which the bookmark is currently set. Static because it persists when the book is
|
||||
* closed. Storing a section means the bookmark doesn't change location when new sections are unlocked.
|
||||
*/
|
||||
private static Section bookmarkSection;
|
||||
|
||||
// Buttons
|
||||
private GuiButton bookmark, next, previous, nextSection, previousSection, menu;
|
||||
|
||||
// Handbook content
|
||||
|
||||
// As a general rule, I prefer to make static final fields lowercase if they're collections that change, because even
|
||||
// though the collection itself is constant, the stuff in it is not, so being lowercase highlights this difference.
|
||||
|
||||
/**
|
||||
* A map which stores all loaded section objects, including subsections. This gets wiped on resource pack reload and
|
||||
* repopulated with mappings as specified by the handbook JSON file for the current language. The keys in the map
|
||||
* correspond to the keys in the sections object in that file, and are sorted in that order.
|
||||
*/
|
||||
static final Map<String, Section> sections = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* A list which stores all loaded section objects, including subsections. This is an unmodifiable list view of the
|
||||
* values in {@link GuiWizardHandbook#sections}, sorted in the same (page number) order. This exists only to allow
|
||||
* sections to be accessed by ordinal index for the various navigation buttons, hence why it is private.
|
||||
*/
|
||||
private static List<Section> sectionList;
|
||||
|
||||
/**
|
||||
* A map which stores all loaded contents objects. This gets wiped on resource pack reload and repopulated with
|
||||
* mappings as specified by the handbook JSON file for the current language. The keys in the map correspond to the
|
||||
* id strings for the contents objects in that file. This map is not sorted.
|
||||
*/
|
||||
static final Map<String, Contents> contentsList = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A map which stores all loaded hex colour values. This gets wiped on resource pack reload and repopulated with
|
||||
* mappings as specified by the handbook JSON file for the current language. The keys in the map correspond to the
|
||||
* keys in the colours object in that file. This map is not sorted.
|
||||
*/
|
||||
static final Map<String, Integer> colours = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A map which stores all loaded image objects. This gets wiped on resource pack reload and repopulated with
|
||||
* mappings as specified by the handbook JSON file for the current language. The keys in the map correspond to the
|
||||
* keys in the images object in that file. This map is not sorted.
|
||||
*/
|
||||
static final Map<String, Image> images = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A map which stores all loaded crafting recipe objects. This gets wiped on resource pack reload and repopulated
|
||||
* with mappings as specified by the handbook JSON file for the current language. The keys in the map correspond to
|
||||
* the keys in the recipes object in that file. This map is not sorted.
|
||||
*/
|
||||
static final Map<String, CraftingRecipe> recipes = new HashMap<>();
|
||||
|
||||
static String rulerText = "--------------------"; // Assigned here as a fallback
|
||||
|
||||
private static final List<Pair<ItemStack, NonNullList<NonNullList<ItemStack>>>> RECIPES = new ArrayList<>();
|
||||
|
||||
// Reflection
|
||||
|
||||
static final Field ADVANCEMENT_TO_PROGRESS;
|
||||
|
||||
static {
|
||||
// TODO: Obf field name
|
||||
ADVANCEMENT_TO_PROGRESS = ReflectionHelper.findField(ClientAdvancementManager.class, "advancementToProgress");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a format tag to the handbook. All occurrences of the given tag string preceded by a # will be replaced with
|
||||
* the result of the given value string on GUI load. The value string, therefore, can be anything that should be
|
||||
* input dynamically, as long as it does not change while the GUI is open. Examples include wizardry's version,
|
||||
* the various element colours and the keys assigned to wizardry's controls.
|
||||
* @param tag The tag string, as defined in the handbook JSON file, excluding the # character. Cannot include spaces.
|
||||
* @param value The string to replace occurrences of the given format tag with. Can include spaces but not the # character.
|
||||
*/
|
||||
public static void addFormatTag(String tag, String value){
|
||||
FORMAT_TAGS.put(tag, value);
|
||||
}
|
||||
|
||||
private static void initFormatTags(){
|
||||
|
||||
addFormatTag("next_spell_key", ClientProxy.NEXT_SPELL.getDisplayName());
|
||||
addFormatTag("previous_spell_key", ClientProxy.PREVIOUS_SPELL.getDisplayName());
|
||||
addFormatTag("mana_per_crystal_minus_30", "" + (Constants.MANA_PER_CRYSTAL - 30));
|
||||
addFormatTag("mana_per_crystal", "" + Constants.MANA_PER_CRYSTAL);
|
||||
addFormatTag("novice_max_charge", "" + Tier.BASIC.maxCharge);
|
||||
addFormatTag("apprentice_max_charge", "" + Tier.APPRENTICE.maxCharge);
|
||||
addFormatTag("advanced_max_charge", "" + Tier.ADVANCED.maxCharge);
|
||||
addFormatTag("master_max_charge", "" + Tier.MASTER.maxCharge);
|
||||
addFormatTag("version", Wizardry.VERSION);
|
||||
addFormatTag("mcversion", Minecraft.getMinecraft().getVersion());
|
||||
|
||||
addFormatTag("colour_novice", "\u00A77");
|
||||
addFormatTag("colour_apprentice", Tier.APPRENTICE.getFormattingCode());
|
||||
addFormatTag("colour_advanced", Tier.ADVANCED.getFormattingCode());
|
||||
addFormatTag("colour_master", Tier.MASTER.getFormattingCode());
|
||||
|
||||
addFormatTag("colour_fire", Element.FIRE.getFormattingCode());
|
||||
addFormatTag("colour_ice", Element.ICE.getFormattingCode());
|
||||
addFormatTag("colour_lightning", Element.LIGHTNING.getFormattingCode());
|
||||
addFormatTag("colour_necromancy", Element.NECROMANCY.getFormattingCode());
|
||||
addFormatTag("colour_earth", Element.EARTH.getFormattingCode());
|
||||
addFormatTag("colour_sorcery", Element.SORCERY.getFormattingCode());
|
||||
addFormatTag("colour_healing", Element.HEALING.getFormattingCode());
|
||||
|
||||
addFormatTag("colour_reset", "\u00A70");
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
/**
|
||||
* Converts the given single page index to a double-page index. Inverse of
|
||||
* {@link GuiWizardHandbook#doubleToSinglePage(int, boolean)}.
|
||||
*
|
||||
* @param singlePageIndex The single-page index, which is the same as the page numbers actually displayed.
|
||||
* @return The corresponding double-page index.
|
||||
*/
|
||||
static int singleToDoublePage(int singlePageIndex){
|
||||
// Yes, this is trivial, but if I ever change the numbering it'll be useful. It's also more descriptive.
|
||||
return singlePageIndex / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given double-page index to a single-page index. Inverse of
|
||||
* {@link GuiWizardHandbook#singleToDoublePage(int)}.
|
||||
*
|
||||
* @param doublePageIndex The double-page index, as stored in {@link GuiWizardHandbook#currentPage}.
|
||||
* @param rightHandPage True to return the page on the right (1 greater), false for the left-hand page.
|
||||
* @return The corresponding single-page index.
|
||||
*/
|
||||
static int doubleToSinglePage(int doublePageIndex, boolean rightHandPage){
|
||||
return rightHandPage ? doublePageIndex * 2 + 1 : doublePageIndex * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given page index refers to a right-hand page or a left-hand page.
|
||||
*
|
||||
* @param page The single-page index, which is the same as the page number actually displayed.
|
||||
* @return True if the given page index refers to a right-hand page, false if it is a left-hand page.
|
||||
*/
|
||||
static boolean isRightPage(int page){
|
||||
return page % 2 == 1;
|
||||
}
|
||||
|
||||
// Drawing
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks){
|
||||
|
||||
int left = this.width / 2 - GUI_WIDTH / 2;
|
||||
int top = this.height / 2 - GUI_HEIGHT / 2;
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
// Main background
|
||||
DrawingUtils.drawTexturedRect(left, top, 0, 0, GUI_WIDTH, GUI_HEIGHT, 512, 256);
|
||||
|
||||
// First page background
|
||||
if(currentPage == 0){
|
||||
DrawingUtils.drawTexturedRect(left, top, 368, 0, GUI_WIDTH / 2, GUI_HEIGHT, 512, 256);
|
||||
previous.visible = false;
|
||||
previousSection.visible = false; // Not worth testing if we're in the first section every frame
|
||||
menu.visible = false;
|
||||
}else{
|
||||
previous.visible = true;
|
||||
previousSection.visible = true;
|
||||
menu.visible = true;
|
||||
}
|
||||
|
||||
// Last page background
|
||||
if(currentPage == singleToDoublePage(pageCount)){
|
||||
DrawingUtils.drawTexturedFlippedRect(left + GUI_WIDTH / 2, top, 368, 0, GUI_WIDTH / 2, GUI_HEIGHT, 512, 256, true, false);
|
||||
next.visible = false;
|
||||
nextSection.visible = false;
|
||||
}else{
|
||||
next.visible = true;
|
||||
nextSection.visible = true;
|
||||
}
|
||||
|
||||
// Page numbers
|
||||
if(currentPage > 0){
|
||||
String pageNumber = "" + doubleToSinglePage(currentPage, false);
|
||||
this.fontRenderer.drawString(pageNumber, left + TEXT_INSET_X + PAGE_WIDTH / 2
|
||||
- fontRenderer.getStringWidth(pageNumber)/2, top + GUI_HEIGHT - PAGE_NUMBER_INSET, DrawingUtils.BLACK);
|
||||
}
|
||||
if(currentPage < singleToDoublePage(pageCount)){
|
||||
String pageNumber = "" + doubleToSinglePage(currentPage, true);
|
||||
this.fontRenderer.drawString(pageNumber, left + GUI_WIDTH - TEXT_INSET_X - PAGE_WIDTH / 2
|
||||
- fontRenderer.getStringWidth(pageNumber)/2, top + GUI_HEIGHT - PAGE_NUMBER_INSET, DrawingUtils.BLACK);
|
||||
}
|
||||
|
||||
// Main content
|
||||
images.values().forEach(i -> i.draw(fontRenderer, currentPage, left, top));
|
||||
contentsList.values().forEach(c -> c.draw(fontRenderer, currentPage, left, top));
|
||||
sections.values().forEach(s -> s.draw(fontRenderer, currentPage, left, top));
|
||||
recipes.values().forEach(r -> r.draw(fontRenderer, itemRender, currentPage, left, top));
|
||||
|
||||
// Buttons
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
|
||||
// Bookmark
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
if(currentPage == singleToDoublePage(bookmarkSection.startPage) + bookmarkPage){
|
||||
bookmark.visible = false;
|
||||
DrawingUtils.drawTexturedRect(left + 138, top, 299, 0, 11, 191, 512, 256);
|
||||
}else{
|
||||
bookmark.visible = true;
|
||||
bookmark.x = left + (currentPage > singleToDoublePage(bookmarkSection.startPage) + bookmarkPage ? 130 : 147);
|
||||
DrawingUtils.drawTexturedRect(bookmark.x, top,
|
||||
bookmark.isMouseOver() ? 310 : 288, 0, 11, 191, 512, 256);
|
||||
}
|
||||
|
||||
// Recipe tooltips
|
||||
recipes.values().forEach(r -> r.drawTooltips(this, fontRenderer, itemRender, currentPage, left, top, mouseX, mouseY));
|
||||
|
||||
}
|
||||
|
||||
// GUI Initialisation / Close
|
||||
|
||||
@Override
|
||||
public void onResize(Minecraft minecraft, int width, int height){
|
||||
initGui();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed(){
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui(){
|
||||
|
||||
super.initGui();
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
|
||||
initFormatTags();
|
||||
|
||||
final int left = this.width / 2 - GUI_WIDTH / 2;
|
||||
final int top = this.height / 2 - GUI_HEIGHT / 2;
|
||||
|
||||
recipes.values().forEach(CraftingRecipe::load);
|
||||
|
||||
int nextButtonId = 0;
|
||||
|
||||
this.buttonList.clear();
|
||||
|
||||
this.buttonList.add(next = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE));
|
||||
|
||||
this.buttonList.add(previous = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE));
|
||||
|
||||
this.buttonList.add(nextSection = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH - BUTTON_SPACING,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION));
|
||||
|
||||
this.buttonList.add(previousSection = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X + BUTTON_SPACING,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION));
|
||||
|
||||
this.buttonList.add(menu = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH/2 - 28,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS));
|
||||
|
||||
this.buttonList.add(bookmark = new GuiButtonInvisible(nextButtonId++, left + 130, top + 172, 11, 19) {
|
||||
@Override
|
||||
public void playPressSound(SoundHandler soundHandler){
|
||||
soundHandler.playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_PAGE_TURN, 1));
|
||||
}
|
||||
});
|
||||
|
||||
pageCount = 1;
|
||||
|
||||
// Clears instances of all images and recipes
|
||||
images.values().forEach(Image::clearInstances);
|
||||
recipes.values().forEach(CraftingRecipe::clearInstances);
|
||||
|
||||
// Formats all the unlocked sections in order
|
||||
for(Section section : sections.values()){
|
||||
if(section.isUnlocked()){
|
||||
pageCount = section.format(this.fontRenderer, pageCount, left, top);
|
||||
buttonList.addAll(section.getButtons());
|
||||
}
|
||||
}
|
||||
|
||||
contentsList.values().forEach(c -> buttonList.addAll(c.getButtons()));
|
||||
|
||||
this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1));
|
||||
}
|
||||
|
||||
// JSON Parsing / Data Construction
|
||||
|
||||
/**
|
||||
* Called from preInit in the main mod class (via the proxies) to initialise the handbook (parses the JSON file
|
||||
* and constructs the relevant data structures), and again on each resource reload (changing the language triggers
|
||||
* a resource reload).
|
||||
*/
|
||||
public static void loadHandbookFile(IResourceManager manager){
|
||||
|
||||
IResource handbookFile = getHandbookResource(manager);
|
||||
|
||||
if(handbookFile != null){
|
||||
|
||||
// Wipes all the maps before repopulating them
|
||||
images.clear();
|
||||
sections.clear();
|
||||
contentsList.clear();
|
||||
colours.clear();
|
||||
|
||||
bookmarkSection = null; // Also need to wipe the reference to the old bookmarked section
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(handbookFile.getInputStream()));
|
||||
|
||||
JsonElement je = gson.fromJson(reader, JsonElement.class);
|
||||
JsonObject json = je.getAsJsonObject();
|
||||
|
||||
rulerText = JsonUtils.getString(json, "ruler_text");
|
||||
|
||||
JsonUtils.getJsonObject(json, "colours").entrySet().forEach(e -> colours.put(e.getKey(),
|
||||
Color.decode(e.getValue().getAsString()).getRGB()));
|
||||
|
||||
// Repopulates the remaining maps
|
||||
Image.populate(images, json);
|
||||
CraftingRecipe.populate(recipes, json);
|
||||
Section.populate(sections, json);
|
||||
|
||||
sectionList = Collections.unmodifiableList(new ArrayList<>(sections.values()));
|
||||
|
||||
if(sections.isEmpty()){
|
||||
Wizardry.logger.warn("Handbook has no sections! Aborting loading...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempts to find a suitable place to put the bookmark to start with
|
||||
bookmarkSection = sections.get("introduction");
|
||||
if(bookmarkSection == null) bookmarkSection = sectionList.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the handbook JSON file for the current language and returns its IResource object. If a handbook file
|
||||
* cannot be found for the current language, a message is printed to the console and the method attempts to retrieve
|
||||
* the default file instead (English-US). If this file cannot be found, the resulting error is printed to the
|
||||
* console and the method returns null.
|
||||
*
|
||||
* @param manager The resource manager instance to use.
|
||||
* @return The handbook JSON file, as an IResource, or null if it was not found.
|
||||
*/
|
||||
private static IResource getHandbookResource(IResourceManager manager){
|
||||
|
||||
IResource handbookFile = null;
|
||||
|
||||
try{
|
||||
handbookFile = manager.getResource(new ResourceLocation(Wizardry.MODID, "texts/handbook_"
|
||||
+ Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage().getLanguageCode() + ".json"));
|
||||
}catch(IOException e){
|
||||
|
||||
Wizardry.logger.info("Wizard handbook JSON file missing for the current language (" + Minecraft.getMinecraft()
|
||||
.getLanguageManager().getCurrentLanguage() + "). Using default (English-US) instead.");
|
||||
|
||||
try{
|
||||
handbookFile = manager.getResource(DEFAULT);
|
||||
}catch(IOException x){
|
||||
Wizardry.logger.error("Couldn't find file: " + DEFAULT + ". The file may be missing; please try re-downloading and reinstalling Wizardry.", x);
|
||||
}
|
||||
}
|
||||
|
||||
return handbookFile;
|
||||
}
|
||||
|
||||
// Controls
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button){
|
||||
|
||||
if(button.enabled){
|
||||
|
||||
if(button == next){
|
||||
if(currentPage < singleToDoublePage(pageCount)) currentPage++;
|
||||
|
||||
}else if(button == previous){
|
||||
if(currentPage > 0) currentPage--;
|
||||
|
||||
}else if(button == nextSection || button == previousSection){
|
||||
|
||||
Section currentSection = null;
|
||||
|
||||
for(Section section : sections.values()){
|
||||
// We always want this button to do something, and taking the right-hand page means it always does
|
||||
if(section.containsPage(doubleToSinglePage(currentPage, true))){
|
||||
currentSection = section;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentSection != null){
|
||||
|
||||
int index = sectionList.indexOf(currentSection);
|
||||
|
||||
if(button == nextSection && index + 1 < sections.size()){
|
||||
currentPage = singleToDoublePage(sectionList.get(index + 1).startPage);
|
||||
}else if(index > 0){
|
||||
currentPage = singleToDoublePage(sectionList.get(index - 1).startPage);
|
||||
}
|
||||
}
|
||||
|
||||
}else if(button == menu){
|
||||
currentPage = singleToDoublePage(sections.get("main_contents").startPage);
|
||||
|
||||
}else if(button == bookmark && bookmarkSection != null){
|
||||
currentPage = singleToDoublePage(bookmarkSection.startPage) + bookmarkPage;
|
||||
|
||||
}else{
|
||||
if(button instanceof GuiButtonHyperlink.Internal){
|
||||
currentPage = singleToDoublePage(((GuiButtonHyperlink.Internal)button).target.startPage);
|
||||
}else if(button instanceof GuiButtonHyperlink.External){
|
||||
this.handleComponentClick(((GuiButtonHyperlink.External)button).link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException{
|
||||
if(mouseButton == 1){
|
||||
// Right-clicking of bookmark
|
||||
if(bookmark.mousePressed(this.mc, mouseX, mouseY)){
|
||||
|
||||
this.selectedButton = bookmark;
|
||||
|
||||
for(Section section : sections.values()){
|
||||
// The bookmark is assumed to bookmark the left-hand page
|
||||
if(section.containsPage(doubleToSinglePage(bookmarkPage, false))) bookmarkSection = section;
|
||||
}
|
||||
|
||||
bookmarkPage = currentPage - singleToDoublePage(bookmarkSection.startPage);
|
||||
}
|
||||
}else{
|
||||
super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
}
|
||||
|
||||
// Overridden to make it public
|
||||
@Override
|
||||
public void renderToolTip(ItemStack stack, int x, int y){
|
||||
super.renderToolTip(stack, x, y);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onAdvancementEvent(AdvancementEvent event){
|
||||
sections.values().forEach(s -> s.onAdvancement(event.getEntityPlayer(), event.getAdvancement()));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user