Skip to content

Instantly share code, notes, and snippets.

@AmauryCarrade AmauryCarrade/Tutorial.java Secret
Created Nov 21, 2015

Embed
What would you like to do?
Tutorials API. CeCILL-B license.
package fr.zcraft.zlib.tools.tutorials;
import fr.zcraft.zlib.core.ZLib;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* <h1>Tutorial API</h1>
*
* <p>
* This API is used to display tutorials to any players.
* </p>
* <p>
* A tutorial is a set of {@code /title}s displayed to the players while they are frozen
* somewhere in the sky (or not). Tutorials are divided in chapters containing contents;
* each chapter contains a title displayed as the main {@code /title}, a location where
* the player is frozen during this chapter, and a list of “pages” displayed successively
* under that title.
* </p>
* <p>
* To use this, extend this class and use the following methods to create your tutorial.
* <ul>
* <li>
* {@link #addChapter(TutorialChapter)}—to add a chapter to this tutorial.
* Order matter!
* </li>
* <li>
* {@link #setReadingTime(long)}—to change the time each chapter's part is displayed.
* </li>
* <li>
* {@link #setTutorialHour(Long)}—to change the day of time this tutorial will be
* played.
* </li>
* </ul>
* </p>
* <p>
* Then, launch the tutorial using {@link #start(UUID)}.
* </p>
* <p>
* The tutorial can be force-stopped using {@link #stop(UUID)}. You can setup a callback
* executed when the tutorial is stopped by overriding {@link #onTutorialEnds(Player, boolean)}.
* </p>
*
* @author Amaury Carrade
*/
public abstract class Tutorial implements Listener
{
private final Plugin p;
/**
* Map: player's UUID -> task executing the tutorial
*/
private Map<UUID, BukkitTask> viewers = new ConcurrentHashMap<>();
/**
* Chapter's contents
*/
private List<TutorialChapter> content = new LinkedList<>();
private long timeNeededToPlayThisTutorial = 0l;
private long readingTime = 50l;
private Long tutorialHour = null;
public Tutorial()
{
p = ZLib.getPlugin();
p.getServer().getPluginManager().registerEvents(this, p);
}
/* * *** PUBLIC TUTORIAL API *** * */
/**
* Adds a chapter in the tutorial.
*
* @param chapter The chapter to add.
*/
public void addChapter(TutorialChapter chapter)
{
content.add(chapter);
timeNeededToPlayThisTutorial += readingTime * chapter.getContent().size();
}
/**
* Sets the amount of ticks each content of the tutorial is displayed.
*
* By default, 50 ticks (2.5 seconds).
*
* @param readingTime The ticks.
*/
public void setReadingTime(final long readingTime)
{
this.readingTime = readingTime;
}
/**
* The time of the day this tutorial is displayed.
*
* You can set a value above 24000 ticks if you want to play with moon phases.<br/>
* If {@code null}, the time is not changed. This is the default behavior.
*
* @param tutorialHour The time of the day this tutorial will be played.
*/
public void setTutorialHour(final Long tutorialHour)
{
this.tutorialHour = tutorialHour;
}
/**
* Returns this tutorial's duration.
*
* @return The duration, in ticks.
*/
public long getTimeNeededToPlayThisTutorial()
{
return timeNeededToPlayThisTutorial;
}
/* * *** TUTORIAL EVENTS *** * */
/**
* This method is called when the tutorial is finished for the given player.
*
* @param player The player who just finished the tutorial. May be {@code null}.
* @param interrupted {@code true} if the tutorial is stopped because it was interrupted
* using {@link #stopForAll(String)}, {@link #stop(UUID)} or if the
* user disconnected.
*/
protected void onTutorialEnds(Player player, boolean interrupted) {}
/* * *** TUTORIAL LAUNCHERS AND HALTERS *** * */
/**
* Starts the tutorial for the given player.
*
* @param id The UUID of the player.
*/
public void start(UUID id)
{
if (isWatchingTutorial(id))
{
p.getLogger().info(p.getServer().getPlayer(id).getName() + "(" + id + ") is trying to see the tutorial whilst watching it.");
return;
}
Player player = p.getServer().getPlayer(id);
// The player cannot move anymore (except with our teleportations)
player.setFlySpeed(0f);
player.setAllowFlight(true);
player.setFlying(true);
// All other players are hidden
for (Player other : p.getServer().getOnlinePlayers())
{
player.hidePlayer(other);
other.hidePlayer(player);
}
// The player's hour is updated, if needed
if(tutorialHour != null)
{
player.setPlayerTime(tutorialHour, false);
}
// The tutorial is started
viewers.put(
id, p.getServer().getScheduler().runTaskTimer(p, new TutorialRunner(this, id), 20l, readingTime)
);
}
/**
* Stops the tutorial for the given player.
*
* @param id The UUID of the player.
*/
public void stop(UUID id)
{
stop(id, true);
}
/**
* Stops the tutorial for everyone.
*
* @param reason A reason displayed to the viewers. Nothing sent if null.
*/
public void stopForAll(String reason)
{
for (UUID viewerID : viewers.keySet())
{
stop(viewerID, true);
if(reason != null)
{
p.getServer().getPlayer(viewerID).sendMessage(reason);
}
}
}
/* * *** INTERNAL API *** * */
/**
* Stops the tutorial for the given player.
*
* @param id The UUID of the player.
* @param interrupted {@code true} if the tutorial is stopped because it was interrupted
* using {@link #stopForAll(String)}, {@link #stop(UUID)} or if the
* user disconnected.
*/
void stop(UUID id, final Boolean interrupted) // package-private
{
if (!isWatchingTutorial(id)) return;
Player player = p.getServer().getPlayer(id);
if (player != null)
{
// The player can now move.
player.setFlySpeed(0.1f);
player.setFlying(false);
player.setAllowFlight(false);
// All other players are displayed
for (Player other : p.getServer().getOnlinePlayers())
{
player.showPlayer(other);
other.showPlayer(player);
}
player.resetPlayerTime();
}
try
{
viewers.get(id).cancel();
}
catch (IllegalStateException ignored) {}
viewers.remove(id);
onTutorialEnds(player, interrupted);
}
/**
* @return A list of {@link TutorialChapter}s.
*/
List<TutorialChapter> getContent()
{
return content;
}
/**
* Returns the amount of ticks each content of the tutorial is displayed.
*
* @return the amount of ticks each content of the tutorial is displayed.
*/
long getReadingTime()
{
return readingTime;
}
/**
* Checks if the given player is currently watching this tutorial.
*
* @param id The player's UUID.
*
* @return {@code true} if the player is watching this tutorial.
*/
boolean isWatchingTutorial(UUID id)
{
return viewers.containsKey(id);
}
/* * *** INTERNAL EVENTS *** * */
@EventHandler
public void onPlayerQuits(PlayerQuitEvent ev)
{
onPlayerQuits(ev.getPlayer());
}
@EventHandler
public void onPlayerQuits(PlayerKickEvent ev)
{
onPlayerQuits(ev.getPlayer());
}
public void onPlayerQuits(Player player)
{
if(isWatchingTutorial(player.getUniqueId()))
{
stop(player.getUniqueId(), true);
}
}
}
package fr.zcraft.zlib.tools.tutorials;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
/**
* Represents a chapter of an in-game tutorial.
* <p/>
* A chapter contains a title, displayed the whole time above, and one or more
* “pages”, displayed as subtitle of the {@code /title} command, ordered.
*
* @author Amaury Carrade
*/
public class TutorialChapter
{
private Location location;
private String title;
private List<String> content = new LinkedList<>();
private boolean displayInChat = true;
/**
* @param location The watching point of the chapter.
* @param title The title of this chapter.
* @param content The content of this chapter.
*/
public TutorialChapter(Location location, String title, List<String> content)
{
this.location = location;
this.title = title;
this.content = content;
}
/**
* @param location The watching point of the chapter.
* @param title The title of this chapter.
* @param content The content of this chapter.
*/
public TutorialChapter(Location location, String title, List<String> content, boolean displayInChat)
{
this(location, title, content);
this.displayInChat = displayInChat;
}
/**
* Teleports the given player to the watching point of this chapter.
*
* @param uuid The UUID of the player.
*/
public void teleport(UUID uuid)
{
teleport(Bukkit.getPlayer(uuid));
}
/**
* Teleports the given player to the watching point of this chapter.
*
* @param player The player.
*/
public void teleport(Player player)
{
player.teleport(location);
}
/**
* Returns the title of this chapter.
*
* @return The title.
*/
public String getTitle()
{
return title;
}
/**
* Returns the content of this chapter (a list of strings).
*
* @return The content.
*/
public List<String> getContent()
{
return content;
}
/**
* Returns true if this has to be displayed in the chat.
*
* @return {@code true} if displayed in the chat.
*/
public boolean isDisplayedInChat()
{
return displayInChat;
}
}
package fr.zcraft.zlib.tools.tutorials;
import fr.zcraft.zlib.core.ZLib;
import fr.zcraft.zlib.tools.Titles;
import org.bukkit.ChatColor;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.UUID;
public class TutorialRunner implements Runnable
{
private Plugin p;
private final String tutorialInChatPrefix = ChatColor.GRAY + "" + ChatColor.RESET;
private Player player;
private Tutorial tutorial;
private int currentChapter = 0;
private int currentText = 0;
public TutorialRunner(Tutorial tutorial, UUID playerId)
{
this.p = ZLib.getPlugin();
this.player = p.getServer().getPlayer(playerId);
this.tutorial = tutorial;
}
@Override
public void run()
{
if (!player.isOnline())
{
tutorial.stop(player.getUniqueId(), true);
return;
}
if (currentChapter == tutorial.getContent().size()) // The end.
{
tutorial.stop(player.getUniqueId(), false);
return;
}
TutorialChapter chapter = tutorial.getContent().get(currentChapter);
// Delays of fade-in, fade-out and display
int fadeIn = (currentText == 0) ? 10 : 0;
int fadeOut = (currentText == chapter.getContent().size() - 1) ? 10 : 0;
int readingTime = (int) (fadeOut == 10 ? tutorial.getReadingTime() - 10 : tutorial.getReadingTime() + 5);
// New chapter, new location
if (currentText == 0)
{
chapter.teleport(player);
player.playSound(player.getLocation(), Sound.LEVEL_UP, 1L, 2L);
}
// Title version
Titles.displayTitle(
player,
fadeIn, readingTime, fadeOut,
chapter.getTitle(),
chapter.getContent().get(currentText)
);
// Chat version
if (chapter.isDisplayedInChat())
{
if (currentText == 0) player.sendMessage(tutorialInChatPrefix + chapter.getTitle());
player.sendMessage(tutorialInChatPrefix + chapter.getContent().get(currentText));
}
// Next one?
currentText++;
if (currentText == chapter.getContent().size())
{
currentChapter++;
currentText = 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.