That's one heck of a commit you've got there...
I may have got a bit behind with version control. A lot behind, in fact. Maybe I'll go back and split this sometime - then again, I probably won't. But hey, at least it's here!
This commit is contained in:
@@ -7,13 +7,12 @@ import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.advancements.AdvancementProgress;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
@@ -23,7 +22,7 @@ import java.util.*;
|
||||
* everything within the section itself, including JSON parsing, unlock triggers and drawing the actual rawText.
|
||||
* Sections may now also be nested and have other elements within them, such as images and a table of contents, a
|
||||
* behaviour which is also handled within this class.
|
||||
* <p>
|
||||
* <p></p>
|
||||
* The formatting of the book is now done 'dynamically' - that is, the exact positions and page numbers of
|
||||
* sections, images and so on are determined on GUI load and depend on which of the previous sections have been
|
||||
* unlocked, amongst other factors. This means that all of the unlocked sections must be formatted in order on GUI
|
||||
@@ -32,15 +31,15 @@ import java.util.*;
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
// Because these are now generated on resource pack reload (not on handbook open, as before), this class must now
|
||||
// be static
|
||||
// Because these are now generated on resource pack reload (not on handbook open, as before), this class can no longer
|
||||
// be a non-static inner class
|
||||
class Section {
|
||||
|
||||
// Final fields are mandatory (none here though), the rest are optional
|
||||
String title;
|
||||
private String[] rawText;
|
||||
private Contents contents;
|
||||
private Advancement[] triggers;
|
||||
private ResourceLocation[] triggers;
|
||||
private Map<String, Section> subsections;
|
||||
private boolean centreX, centreY;
|
||||
|
||||
@@ -54,6 +53,9 @@ class Section {
|
||||
*/
|
||||
private final List<List<String>> pages;
|
||||
|
||||
private boolean unlocked = false;
|
||||
private boolean isNew = false;
|
||||
|
||||
private Section(){
|
||||
this.buttons = new ArrayList<>();
|
||||
this.pages = new ArrayList<>();
|
||||
@@ -65,10 +67,10 @@ class Section {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given page is within this section, false if not.
|
||||
* Returns true if the given page is within this section, false if not (or if the section is locked).
|
||||
*/
|
||||
boolean containsPage(int page){
|
||||
return startPage <= page && startPage + pages.size() > page;
|
||||
return this.isUnlocked() && startPage <= page && startPage + pages.size() > page;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,32 +79,24 @@ class Section {
|
||||
*/
|
||||
boolean isUnlocked(){
|
||||
|
||||
if(Minecraft.getMinecraft().player.isCreative()) return true;
|
||||
if(!Wizardry.settings.handbookProgression) return true; // Always unlocked if handbook progression is off
|
||||
if(triggers == null) return true; // If no triggers were defined, the section is unlocked from the start
|
||||
|
||||
// A section is automatically unlocked if one of its subsections is unlocked
|
||||
for(Section subsection : subsections.values()){
|
||||
if(subsection.isUnlocked()) return true;
|
||||
}
|
||||
|
||||
if(triggers == null) return true; // If no triggers were defined, the section is unlocked from the start
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
for(Advancement trigger : triggers){
|
||||
|
||||
try{
|
||||
|
||||
// TESTME: Is this properly synced or do we need to do that as well?
|
||||
Map<Advancement, AdvancementProgress> advancements = (Map)GuiWizardHandbook.ADVANCEMENT_TO_PROGRESS.get(Minecraft.getMinecraft().player.connection.getAdvancementManager());
|
||||
|
||||
if(advancements.get(trigger).isDone()){
|
||||
return true;
|
||||
}
|
||||
|
||||
}catch(IllegalAccessException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
/**
|
||||
* Returns true if this section has been unlocked and not read yet. Also returns true if any subsections are new.
|
||||
*/
|
||||
boolean isNew(){
|
||||
if(!Wizardry.settings.handbookProgression) return false;
|
||||
return isNew || this.subsections.values().stream().anyMatch(Section::isNew);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,12 +137,19 @@ class Section {
|
||||
|
||||
for(String line : lines){
|
||||
|
||||
int lx = centreX ? x + GuiWizardHandbook.PAGE_WIDTH / 2 - font.getStringWidth(line) / 2 : x;
|
||||
|
||||
font.drawString(line, lx, y, DrawingUtils.BLACK, false);
|
||||
if(line.startsWith(GuiWizardHandbook.FORMAT_MARKER + GuiWizardHandbook.RULER_TAG)){
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(GuiWizardHandbook.texture);
|
||||
DrawingUtils.drawTexturedRect(x-1, y-1, 0, GuiWizardHandbook.GUI_HEIGHT, GuiWizardHandbook.PAGE_WIDTH + 2, 9, GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT);
|
||||
}else{
|
||||
int lx = centreX ? x + GuiWizardHandbook.PAGE_WIDTH / 2 - font.getStringWidth(line) / 2 : x;
|
||||
font.drawString(line, lx, y, DrawingUtils.BLACK, false);
|
||||
}
|
||||
|
||||
y += font.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
isNew = false; // Now a page has been drawn, the player must have seen it so it's not new any more
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,7 +181,7 @@ class Section {
|
||||
// Adds the header if present
|
||||
if(!this.title.isEmpty()){
|
||||
lines.add(this.title);
|
||||
lines.add(GuiWizardHandbook.rulerText);
|
||||
lines.add(GuiWizardHandbook.FORMAT_MARKER + GuiWizardHandbook.RULER_TAG);
|
||||
}
|
||||
|
||||
// Adds space for the contents if it exists
|
||||
@@ -212,7 +213,7 @@ class Section {
|
||||
+ StringUtils.abbreviate(raw, 50));
|
||||
|
||||
Image image = GuiWizardHandbook.images.get(arguments[1]);
|
||||
if(image == null) throw new JsonSyntaxException("Image with id " + arguments[1] + "is undefined");
|
||||
if(image == null) throw new JsonSyntaxException("Image with id " + arguments[1] + " is undefined");
|
||||
|
||||
// Starts a new page if the image will not fit on the current one
|
||||
if((lines.size() % maxLineNumber) * font.FONT_HEIGHT + image.getHeight(font) > GuiWizardHandbook.PAGE_HEIGHT){
|
||||
@@ -248,27 +249,35 @@ class Section {
|
||||
+ StringUtils.abbreviate(raw, 50));
|
||||
|
||||
CraftingRecipe recipe = GuiWizardHandbook.recipes.get(arguments[1]);
|
||||
if(recipe == null) throw new JsonSyntaxException("Recipe with id " + arguments[1] + "is undefined");
|
||||
if(recipe == null) throw new JsonSyntaxException("Recipe with id " + arguments[1] + " is undefined");
|
||||
|
||||
// Starts a new page if the recipe will not fit on the current one
|
||||
if((lines.size() % maxLineNumber) * font.FONT_HEIGHT + CraftingRecipe.HEIGHT > GuiWizardHandbook.PAGE_HEIGHT){
|
||||
// Remaining number of lines on the page
|
||||
lines.addAll(Collections.nCopies(maxLineNumber - (lines.size() % maxLineNumber), ""));
|
||||
// Remaining number of lines on the page, plus the first blank one on the new page
|
||||
lines.addAll(Collections.nCopies(maxLineNumber - (lines.size() % maxLineNumber), " "));
|
||||
}
|
||||
|
||||
int page = startPage + (lines.size() / maxLineNumber);
|
||||
|
||||
if(lines.size() % maxLineNumber == 0) lines.add(" ");
|
||||
int startLine = lines.size() % maxLineNumber - 1;
|
||||
|
||||
recipe.addInstance(page, GuiWizardHandbook.PAGE_WIDTH / 2 - CraftingRecipe.WIDTH / 2
|
||||
+ (GuiWizardHandbook.isRightPage(page) ? GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : GuiWizardHandbook.TEXT_INSET_X),
|
||||
GuiWizardHandbook.TEXT_INSET_Y + (lines.size() % maxLineNumber) * font.FONT_HEIGHT);
|
||||
GuiWizardHandbook.TEXT_INSET_Y + startLine * font.FONT_HEIGHT);
|
||||
|
||||
// Height of the recipe in lines, rounded up
|
||||
// Uses a single space instead of an empty string so that the page trimming doesn't remove them
|
||||
lines.addAll(Collections.nCopies(CraftingRecipe.HEIGHT / font.FONT_HEIGHT, " "));
|
||||
lines.addAll(Collections.nCopies(CraftingRecipe.HEIGHT / font.FONT_HEIGHT - 1, " "));
|
||||
// This time we're not adding an extra space because it's not really needed
|
||||
|
||||
}else{ // All other paragraphs
|
||||
|
||||
// Formatting
|
||||
for(Map.Entry<String, String> entry : GuiWizardHandbook.FORMAT_TAGS.entrySet()){
|
||||
paragraph = paragraph.replace(GuiWizardHandbook.FORMAT_MARKER + entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
// Hyperlinks
|
||||
|
||||
int linkStart;
|
||||
@@ -284,15 +293,17 @@ class Section {
|
||||
|
||||
String linkRaw = paragraph.substring(linkStart, linkEnd + 1);
|
||||
String[] arguments = paragraph.substring(linkStart + 1, linkEnd).split("\\s", 2);
|
||||
String suffix = paragraph.substring(linkEnd).split("\\s", 2)[0];
|
||||
String suffix = paragraph.substring(linkEnd).split("\\s", 2)[0].substring(1); // substring(1) to remove the @
|
||||
|
||||
// The index of the single page currently being formatted, relative to the section
|
||||
int pageRelative = (lines.size() + upToLink.size() - 1) / maxLineNumber;
|
||||
// The overall index of the single page currently being formatted
|
||||
int page = startPage + pageRelative;
|
||||
// The line number on this page
|
||||
int lineNumber = (lines.size() + upToLink.size() - 1) % maxLineNumber;
|
||||
|
||||
int x = GuiWizardHandbook.isRightPage(page) ? left + GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : left + GuiWizardHandbook.TEXT_INSET_X;
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y + (((lines.size() + upToLink.size() - 1) % maxLineNumber)) * font.FONT_HEIGHT;
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y + lineNumber * font.FONT_HEIGHT;
|
||||
|
||||
// Adds any missing sub-lists
|
||||
while(this.buttons.size() <= pageRelative){
|
||||
@@ -300,18 +311,13 @@ class Section {
|
||||
}
|
||||
|
||||
// The button id only does what you use it for, so we're just not using it at all.
|
||||
this.buttons.get(pageRelative).add(GuiButtonHyperlink.create(x, y, font, upToLink, arguments, suffix));
|
||||
this.buttons.get(pageRelative).add(GuiButtonHyperlink.create(x, y, font, upToLink, arguments, suffix, maxLineNumber - lineNumber - 1, GuiWizardHandbook.isRightPage(page)));
|
||||
|
||||
// The link button should exactly overlay the display rawText in the main string
|
||||
// If the link has no display rawText specified, it displays the unformatted target string
|
||||
paragraph = paragraph.replace(linkRaw, arguments[arguments.length - 1]);
|
||||
}
|
||||
|
||||
// Formatting
|
||||
for(Map.Entry<String, String> entry : GuiWizardHandbook.FORMAT_TAGS.entrySet()){
|
||||
paragraph = paragraph.replace(GuiWizardHandbook.FORMAT_MARKER + entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
lines.addAll(font.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH));
|
||||
}
|
||||
|
||||
@@ -373,18 +379,22 @@ class Section {
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "contents")){
|
||||
section.contents = Contents.fromJson(JsonUtils.getJsonObject(json, "contents"));
|
||||
section.contents = Contents.fromJson(section, JsonUtils.getJsonObject(json, "contents"));
|
||||
GuiWizardHandbook.contentsList.put(section.contents.id, section.contents);
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "text")){
|
||||
// TESTME: Does this always preserve order?
|
||||
section.rawText = Streams.stream(JsonUtils.getJsonArray(json, "text"))
|
||||
.map(e -> JsonUtils.getString(e, "element of array rawText"))
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
// TODO: Triggers
|
||||
if(JsonUtils.hasField(json, "triggers")){
|
||||
section.triggers = Streams.stream(JsonUtils.getJsonArray(json, "triggers"))
|
||||
.map(e -> new ResourceLocation(JsonUtils.getString(e, "element of array triggers")))
|
||||
.toArray(ResourceLocation[]::new);
|
||||
// TODO: Can we validate this and throw a JSON exception if no such advancement exists?
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "centre")){
|
||||
JsonObject centre = JsonUtils.getJsonObject(json,"centre");
|
||||
@@ -416,14 +426,26 @@ class Section {
|
||||
}
|
||||
}
|
||||
|
||||
public void onAdvancement(EntityPlayer player, Advancement advancement){
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
//System.out.println(title);
|
||||
if(triggers != null && Arrays.asList(triggers).contains(advancement) && player == minecraft.player){
|
||||
//minecraft.getToastGui().drawToast(IToast);
|
||||
/**
|
||||
* Called on login and advancement completion to update this section's unlock status and display toast
|
||||
* notifications if applicable.
|
||||
*/
|
||||
public void updateUnlockStatus(boolean showToasts, ResourceLocation... completedAdvancements){
|
||||
|
||||
// Debug
|
||||
//System.out.println(title);
|
||||
if(triggers == null) return;
|
||||
|
||||
List<ResourceLocation> completed = new ArrayList<>(Arrays.asList(completedAdvancements));
|
||||
completed.retainAll(Arrays.asList(triggers));
|
||||
|
||||
// Only shows the toast when the section was locked before and is now unlocked
|
||||
if(!this.unlocked && !completed.isEmpty() && showToasts && Wizardry.settings.handbookProgression){
|
||||
// Mmmmm toast...
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
minecraft.getToastGui().add(new HandbookToast(this));
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
// Currently, this will not take subsections into account
|
||||
this.unlocked = !completed.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user