Compare commits

...
Sign in to create a new pull request.

15 commits

Author SHA1 Message Date
Katie Russell
dec8d95f08 Backport fix by Bluelads4: Changed trigger words and Conversation States to let Jef answer to them (fixed Bug ID: 3195349) and minor fix by kymara: duplicate entry to set the quest slot to a timestamp 2011-03-23 22:58:12 +00:00
Hendrik Brummermann
6be67dcee0 backported remove 'up' portal to prevent warnings by kymara 2011-02-27 11:35:03 +00:00
Hendrik Brummermann
56385f72cc backported elf princess pending achievement fixed for those who did have the slot timestamped but didn't already have the count by kymara 2011-02-27 11:34:24 +00:00
Hendrik Brummermann
caa03304fb set version number in preparation for release 2011-02-25 09:32:50 +00:00
Hendrik Brummermann
e1cfe25b96 use transaction provided as parameter in execute(). (fixes #3192026) 2011-02-25 07:46:54 +00:00
Hendrik Brummermann
7f9b16abaf fixed sql syntax error 2011-02-25 07:36:55 +00:00
Hendrik Brummermann
bfb306a2f7 temporary reverted changes to Bootstrap 2011-02-24 23:21:54 +00:00
Hendrik Brummermann
6b4c98ba2c backported merge of santa and bunny achievement and move to friend category (by kymara) 2011-02-24 19:12:48 +00:00
Hendrik Brummermann
e85a5f0937 backported new color for group messages (by kiheru) 2011-02-24 19:12:11 +00:00
Hendrik Brummermann
4d5019eb9b normal players cannot invite ghostmoded admins anymore 2011-02-24 09:53:54 +00:00
Hendrik Brummermann
247c492b08 increased group size limit to 5 2011-02-24 09:44:15 +00:00
Hendrik Brummermann
3e0776c909 if neither harp-1.ogg nor the_old_tavern.ogg is found, do not initialize the sound system. 2011-02-23 23:39:46 +00:00
Hendrik Brummermann
327a51a7cb Sato does not misslead players to seel the quest sheep anymore (by kymara) (fixes #3190547) 2011-02-23 22:24:16 +00:00
Hendrik Brummermann
84bfd76b9a deleted javascript 2011-02-23 20:56:20 +00:00
Hendrik Brummermann
bc59aa52a6 updated version number 2011-02-23 20:26:57 +00:00
33 changed files with 313 additions and 770 deletions

View file

@ -39,12 +39,12 @@ Java is included in most Linux distributions. Both Sun Java and OpenJDK are supp
Please download Stendhal from http://arianne.sourceforge.net
Then unzip the client zip into a new folder.
You can double click on stendhal-0.91.5.jar and the client will run.
You can double click on stendhal-0.92.jar and the client will run.
If you prefer to run from command line, then just execute this command
in the stendhal folder:
java -jar stendhal-0.91.5.jar
java -jar stendhal-0.92.jar
Alternatively you can run it using Java Webstart technology at:

View file

@ -73,7 +73,7 @@ version_server = http://arianne.sourceforge.net/stendhal.version
# current version of stendhal
version.old = 0.91
version = 0.91.5
version = 0.92
# javac options
javac.deprecation = true

View file

@ -1,3 +1,3 @@
set STENDHAL_VERSION=0.91.5
set STENDHAL_VERSION=0.92
set LOCALCLASSPATH=.;data\script;data\conf;stendhal-server-%STENDHAL_VERSION%.jar;marauroa.jar;mysql-connector.jar;log4j.jar;commons-lang.jar;h2.jar
java -Xmx400m -cp "%LOCALCLASSPATH%" marauroa.server.marauroad -c server.ini -l

View file

@ -1,5 +1,5 @@
#!/bin/sh
STENDHAL_VERSION="0.91.5"
STENDHAL_VERSION="0.92"
LOCALCLASSPATH=.:data/script/:data/conf/:stendhal-server-$STENDHAL_VERSION.jar:marauroa.jar:mysql-connector.jar:log4j.jar:commons-lang.jar:h2.jar

View file

@ -91,7 +91,7 @@ public class j2DClient implements UserInterface {
* A shared [singleton] copy.
*/
private static j2DClient sharedUI;
/**
* Get the default UI.
* @return the instance
@ -126,7 +126,7 @@ public class j2DClient implements UserInterface {
private JLayeredPane pane;
private KTextEdit gameLog;
private ContainerPanel containerPanel;
private boolean gameRunning;
@ -148,11 +148,11 @@ public class j2DClient implements UserInterface {
/** the inventory.*/
private SlotWindow inventory;
private Spells spells;
private User lastuser;
private boolean offline;
@ -184,7 +184,7 @@ public class j2DClient implements UserInterface {
addEventLine(new HeaderLessEventLine("Unsynced: Resynchronizing...",
NotificationType.CLIENT));
}
}
};
@ -208,7 +208,7 @@ public class j2DClient implements UserInterface {
this.client = client;
this.userContext = userContext;
setDefault(this);
Dimension screenSize = stendhal.getScreenSize();
/*
@ -223,7 +223,7 @@ public class j2DClient implements UserInterface {
*/
pane.setMaximumSize(screenSize);
pane.setMinimumSize(new Dimension(screenSize.width, 0));
/*
* Create the main game screen
*/
@ -231,17 +231,17 @@ public class j2DClient implements UserInterface {
screenController = new ScreenController(screen);
GameScreen.setDefaultScreen(screen);
screen.setMinimumSize(new Dimension(screenSize.width, 0));
// ... and put it on the ground layer of the pane
pane.add(screen, Component.LEFT_ALIGNMENT, JLayeredPane.DEFAULT_LAYER);
client.setScreen(screen);
positionChangeListener.add(screenController);
final KeyAdapter tabcompletion = new ChatCompletionHelper(chatText, World.get().getPlayerList().getNamesList());
chatText.addKeyListener(tabcompletion);
/*
* Always redirect focus to chat field
*/
@ -255,7 +255,7 @@ public class j2DClient implements UserInterface {
}
});
// On Screen windows
/*
* Quit dialog
@ -293,7 +293,7 @@ public class j2DClient implements UserInterface {
final WtWindowManager windowManager = WtWindowManager.getInstance();
windowManager.setDefaultProperties("corpse", false, 0, 190);
windowManager.setDefaultProperties("chest", false, 100, 190);
/*
* Finally create the window, and place all the components in it
*/
@ -303,40 +303,40 @@ public class j2DClient implements UserInterface {
JComponent glassPane = DragLayer.get();
mainFrame.getMainFrame().setGlassPane(glassPane);
glassPane.setVisible(true);
// *** Create the layout ***
// left side panel
JComponent leftColumn = createLeftPanel();
// Chat entry and chat log
final JComponent chatBox = SBoxLayout.createContainer(SBoxLayout.VERTICAL);
// Set maximum size to prevent the entry requesting massive widths, but
// force expand if there's extra space anyway
// force expand if there's extra space anyway
chatText.getPlayerChatText().setMaximumSize(new Dimension(screenSize.width, Integer.MAX_VALUE));
chatBox.add(chatText.getPlayerChatText(), SBoxLayout.constraint(SLayout.EXPAND_X));
chatBox.add(gameLog, SBoxLayout.constraint(SLayout.EXPAND_X, SLayout.EXPAND_Y));
chatBox.setMinimumSize(chatText.getPlayerChatText().getMinimumSize());
chatBox.setMaximumSize(new Dimension(screenSize.width, Integer.MAX_VALUE));
// Give the user the ability to make the the game area less tall
final JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pane, chatBox);
splitPane.setBorder(null);
// Works for showing the resize, but is extremely flickery
//splitPane.setContinuousLayout(true);
pane.addComponentListener(new SplitPaneResizeListener(screen, splitPane));
containerPanel = createContainerPanel();
// Avoid panel drawing overhead
final Container windowContent = SBoxLayout.createContainer(SBoxLayout.HORIZONTAL);
mainFrame.getMainFrame().setContentPane(windowContent);
// Finally add the left pane, and the games screen + chat combo
// Make the panel take any horizontal resize
windowContent.add(leftColumn, SBoxLayout.constraint(SLayout.EXPAND_X, SLayout.EXPAND_Y));
leftColumn.setMinimumSize(new Dimension());
/*
* Put the splitpane and the container panel to a subcontainer to make
* squeezing the window affect the left pane first rather than the right
@ -346,7 +346,7 @@ public class j2DClient implements UserInterface {
rightSide.add(containerPanel, SBoxLayout.constraint(SLayout.EXPAND_Y));
rightSide.setMinimumSize(rightSide.getPreferredSize());
windowContent.add(rightSide, SBoxLayout.constraint(SLayout.EXPAND_Y));
/*
* Handle focus assertion and window closing
*/
@ -371,7 +371,7 @@ public class j2DClient implements UserInterface {
requestQuit();
}
});
mainFrame.getMainFrame().pack();
setInitialWindowStates();
@ -380,7 +380,7 @@ public class j2DClient implements UserInterface {
* different java versions seem to take the window decorations
* in account in rather random ways.
*/
final int width = mainFrame.getMainFrame().getWidth()
final int width = mainFrame.getMainFrame().getWidth()
- minimap.getComponent().getWidth() - containerPanel.getWidth();
final int height = mainFrame.getMainFrame().getHeight() - gameLog.getHeight();
@ -393,7 +393,7 @@ public class j2DClient implements UserInterface {
*/
Rectangle maxBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
Dimension current = mainFrame.getMainFrame().getSize();
mainFrame.getMainFrame().setSize(Math.min(current.width, maxBounds.width),
mainFrame.getMainFrame().setSize(Math.min(current.width, maxBounds.width),
Math.min(current.height, maxBounds.height));
/*
@ -405,17 +405,17 @@ public class j2DClient implements UserInterface {
maxBounds.height - 80));
directionRelease = null;
// register the slash actions in the client side command line parser
SlashActionRepository.register();
checkAndComplainAboutJavaImplementation();
WorldObjects.addWorldListener(getSoundSystemFacade());
} // constructor
/**
* Create the left side panel of the client.
*
*
* @return A component containing the components left of the game screen
*/
private JComponent createLeftPanel() {
@ -426,11 +426,11 @@ public class j2DClient implements UserInterface {
buddyScroll.setScrollingSpeed(SCROLLING_SPEED);
final JComponent buddyPane = buddyScroll.getComponent();
buddyPane.setBorder(null);
final JComponent leftColumn = SBoxLayout.createContainer(SBoxLayout.VERTICAL);
leftColumn.add(minimap.getComponent(), SBoxLayout.constraint(SLayout.EXPAND_X));
leftColumn.add(stats.getComponent(), SBoxLayout.constraint(SLayout.EXPAND_X));
// Add a background for the tabs. The column itself has none.
JPanel tabBackground = new JPanel();
tabBackground.setBorder(null);
@ -444,24 +444,24 @@ public class j2DClient implements UserInterface {
}
tabs.setFocusable(false);
tabs.add("Friends", buddyPane);
tabs.add("Group", GroupPanelController.get().getComponent());
tabBackground.add(tabs, SBoxLayout.constraint(SLayout.EXPAND_X, SLayout.EXPAND_Y));
leftColumn.add(tabBackground, SBoxLayout.constraint(SLayout.EXPAND_X, SLayout.EXPAND_Y));
return leftColumn;
}
/**
* Create the container panel (right side panel), and its child components.
*
*
* @return container panel
*/
private ContainerPanel createContainerPanel() {
ContainerPanel containerPanel = new ContainerPanel();
containerPanel.setMinimumSize(new Dimension(0, 0));
/*
* Contents of the containerPanel
*/
@ -472,31 +472,31 @@ public class j2DClient implements UserInterface {
settings.add("rp");
settings.add("help");
containerPanel.add(settings, SBoxLayout.constraint(SLayout.EXPAND_X));
// Character window
character = new Character();
character.setAlignmentX(Component.LEFT_ALIGNMENT);
containerPanel.addRepaintable(character);
// Create the bag window
inventory = new SlotWindow("bag", 3, 4);
inventory.setCloseable(false);
inventory.setInspector(containerPanel);
containerPanel.addRepaintable(inventory);
keyring = new KeyRing();
keyring.setAlignmentX(Component.LEFT_ALIGNMENT);
containerPanel.addRepaintable(keyring);
client.addFeatureChangeListener(keyring);
spells = new Spells();
spells.setAlignmentX(Component.LEFT_ALIGNMENT);
containerPanel.addRepaintable(spells);
client.addFeatureChangeListener(spells);
return containerPanel;
}
/**
* Modify the states of the on screen windows. The window manager normally
* restores the state of the window as it was on the previous session. For
@ -515,10 +515,10 @@ public class j2DClient implements UserInterface {
inventory.setVisible(true);
/*
* Keyring, on the other hand, *should* be hidden until revealed
* by feature change
* by feature change
*/
keyring.setVisible(false);
// spells should also be invisible until revealed by a feature change
spells.setVisible(false);
}
@ -526,9 +526,9 @@ public class j2DClient implements UserInterface {
private void checkAndComplainAboutJavaImplementation() {
final String vmName = System.getProperty("java.vm.name", "unknown").toLowerCase(Locale.ENGLISH);
if ((vmName.indexOf("hotspot") < 0) && (vmName.indexOf("openjdk") < 0)) {
final String text = "Stendhal is developed and tested on Sun Java and OpenJDK. You are using "
+ System.getProperty("java.vm.vendor", "unknown") + " "
+ System.getProperty("java.vm.name", "unknown")
final String text = "Stendhal is developed and tested on Sun Java and OpenJDK. You are using "
+ System.getProperty("java.vm.vendor", "unknown") + " "
+ System.getProperty("java.vm.name", "unknown")
+ " so there may be some problems like a black or grey screen.\n"
+ " If you have coding experience with your JDK, we are looking for help.";
addEventLine(new HeaderLessEventLine(text, NotificationType.ERROR));
@ -553,14 +553,14 @@ public class j2DClient implements UserInterface {
/**
* Start the game loop thread.
*
*
* @param gameScreen
*/
public void startGameLoop(final GameScreen gameScreen) {
Thread loop = new Thread(new Runnable() {
public void run() {
gameLoop(gameScreen);
// gameLoop runs until the client quit
// gameLoop runs until the client quit
cleanup();
}
}, "Game loop");
@ -579,7 +579,7 @@ public class j2DClient implements UserInterface {
} catch (RuntimeException e) {
logger.error(e, e);
}
// keep looping until the game ends
long refreshTime = System.currentTimeMillis();
long lastFpsTime = refreshTime;
@ -599,10 +599,10 @@ public class j2DClient implements UserInterface {
final long now = System.currentTimeMillis();
final int delta = (int) (now - refreshTime);
refreshTime = now;
logger.debug("Move objects");
gameObjects.update(delta);
if (gameLayers.isAreaChanged()) {
// Same thread as the ClientFramework loop, so these should
// be save
@ -637,20 +637,20 @@ public class j2DClient implements UserInterface {
}
triggerPainting();
logger.debug("Query network");
if (client.loop(0)) {
lastMessageHandle = refreshTime;
}
/*
* Process delayed direction release
*/
if ((directionRelease != null) && directionRelease.hasExpired()) {
client.removeDirection(directionRelease.getDirection(),
directionRelease.isFacing());
directionRelease = null;
}
@ -659,10 +659,10 @@ public class j2DClient implements UserInterface {
logger.debug("FPS: " + fps);
final long freeMemory = Runtime.getRuntime().freeMemory() / 1024;
final long totalMemory = Runtime.getRuntime().totalMemory() / 1024;
logger.debug("Total/Used memory: " + totalMemory + "/"
+ (totalMemory - freeMemory));
fps = 0;
lastFpsTime = refreshTime;
}
@ -687,16 +687,16 @@ public class j2DClient implements UserInterface {
logger.info("Waiting " + wait + " ms");
wait = 100L;
}
try {
Thread.sleep(wait);
} catch (final InterruptedException e) {
logger.error(e, e);
}
}
logger.debug("End sleeping");
if (!gameRunning) {
logger.info("Request logout");
try {
@ -722,7 +722,7 @@ public class j2DClient implements UserInterface {
logger.error(e, e);
}
}
getSoundSystemFacade().exit();
}
@ -964,10 +964,10 @@ public class j2DClient implements UserInterface {
final boolean isTalking) {
screenController.addText(x, y, text, type, isTalking);
}
/**
* Display a box for a reached achievement
*
*
* @param title the title of the achievement
* @param description the description of the achievement
* @param category the category of the achievement
@ -994,10 +994,10 @@ public class j2DClient implements UserInterface {
final OutfitDialog dialog = new OutfitDialog(mainFrame.getMainFrame(), "Set outfit", outfit);
dialog.setVisible(true);
}
/**
* Get the main window component.
*
*
* @return main window
*/
public Frame getMainFrame() {
@ -1032,9 +1032,9 @@ public class j2DClient implements UserInterface {
*/
public void setChatLine(final String text) {
chatText.setChatLine(text);
}
public void clearGameLog() {
gameLog.clear();
}
@ -1186,20 +1186,20 @@ public class j2DClient implements UserInterface {
public StendhalClient getClient() {
return client;
}
/**
* The layered pane where the game screen is does not automatically resize
* The layered pane where the game screen is does not automatically resize
* the game screen. This handler is needed to do that work.
*/
private static class SplitPaneResizeListener implements ComponentListener {
private Component child;
private JSplitPane splitPane;
private final Component child;
private final JSplitPane splitPane;
public SplitPaneResizeListener(Component child, JSplitPane splitPane) {
this.child = child;
this.splitPane = splitPane;
}
public void componentHidden(ComponentEvent e) {
// do nothing
}
@ -1245,12 +1245,15 @@ public class j2DClient implements UserInterface {
public SoundSystemFacade getSoundSystemFacade() {
if (soundSystemFacade == null) {
try {
soundSystemFacade = new games.stendhal.client.sound.sound.SoundSystemFacadeImpl();
if ((j2DClient.class.getClassLoader().getResource("data/sound/harp-1.ogg") != null)
|| (j2DClient.class.getClassLoader().getResource("data/music/the_old_tavern.ogg") != null)) {
soundSystemFacade = new games.stendhal.client.sound.sound.SoundSystemFacadeImpl();
} else {
soundSystemFacade = new NoSoundFacade();
}
} catch (RuntimeException e) {
soundSystemFacade = new NoSoundFacade();
logger.error(e, e);
soundSystemFacade = new NoSoundFacade();
logger.error(e, e);
}
}
return soundSystemFacade;

View file

@ -16,9 +16,7 @@ import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
/**
* Starts a program after doing some classpath magic.
@ -240,7 +238,7 @@ public class Bootstrap {
} catch (final IOException e) {
JOptionPane.showMessageDialog(
null,
new SelectableLabel("Sorry, an error occurred while downloading the update. Could not write bootProperties"));
"Sorry, an error occurred while downloading the update. Could not write bootProperties");
}
}
@ -314,9 +312,9 @@ public class Bootstrap {
final int res = JOptionPane.showConfirmDialog(
null,
new SelectableLabel(" Sorry an error occurred because of inconsistent code signing.\n"
" Sorry an error occurred because of inconsistent code signing.\n"
+ " Delete update files so that they are downloaded again after you restart Stendhal?\n"
+ " Note: This exception can occur if you include signed jars into a self build client."),
+ " Note: This exception can occur if you include signed jars into a self build client.",
"Stendhal", JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (res == JOptionPane.YES_OPTION) {
@ -337,8 +335,8 @@ public class Bootstrap {
} catch (final Exception err) {
err.printStackTrace(System.err);
JOptionPane.showMessageDialog(null,
new SelectableLabel("Something nasty happened while trying to start your self build client: "
+ err));
"Something nasty happened while trying to start your self build client: "
+ err);
}
}
}
@ -365,8 +363,7 @@ public class Bootstrap {
} else if (e instanceof LinkageError || e instanceof SecurityException) {
final int res = JOptionPane.showConfirmDialog(
null,
new SelectableLabel(message
+ " Sorry an error occurred because of an inconsistent update state. Delete update files so that they are downloaded again after you restart Stendhal?"),
message + " Sorry an error occurred because of an inconsistent update state. Delete update files so that they are downloaded again after you restart Stendhal?",
"Stendhal", JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (res == JOptionPane.YES_OPTION) {
@ -376,9 +373,8 @@ public class Bootstrap {
String errorMessage = stacktraceToString(e);
JOptionPane.showMessageDialog(
null,
new SelectableLabel(
message + " An unexpected error occurred.\r\nPlease open a bug report at http://sf.net/projects/arianne with this error message:\r\n"
+ errorMessage));
+ errorMessage);
}
System.exit(1);
}
@ -390,7 +386,7 @@ public class Bootstrap {
try {
saveBootProp();
} catch (final IOException e1) {
JOptionPane.showMessageDialog(null, new SelectableLabel("Could not write jar.properties"));
JOptionPane.showMessageDialog(null, "Could not write jar.properties");
}
}
@ -408,25 +404,4 @@ public class Bootstrap {
}
return sb.toString();
}
/**
* A fake label component to work around JLabels not being
* selectable. JOptionPane creates JLabels for strings, but keeps
* custom components, like this.
*/
private static class SelectableLabel extends JTextArea {
SelectableLabel(String text) {
super(text);
setEditable(false);
setBorder(null);
setOpaque(false);
/*
* Get the font directly from JLabel, in case the user is
* using a theme where it does not come from an UIManager
* property.
*/
JLabel tmp = new JLabel();
setFont(tmp.getFont());
}
}
}

View file

@ -10,7 +10,7 @@ package games.stendhal.client.update;
public class Version {
/** Version Number. */
private static final String VERSION = "0.91.5";
private static final String VERSION = "0.92";
/**
* Extract the specified number of parts from a version-string.

View file

@ -26,7 +26,7 @@ public class Debug {
/** version. */
// Note: This line is updated by build.xml using a regexp so be sure to adjust it in case you modify this line.
public static final String VERSION = "0.91.5";
public static final String VERSION = "0.92";
public static final String PRE_RELEASE_VERSION = null;

View file

@ -22,8 +22,8 @@ import java.awt.Color;
* This would be similar to logical styles vs. physical styles in HTML.
*/
public enum NotificationType {
CLIENT("client") {
@Override
public Color getColor() {
@ -140,7 +140,8 @@ public enum NotificationType {
public static final Color COLOR_POSITIVE = Color.green;
public static final Color COLOR_GROUP = new Color(99, 61, 139);
// dark blue
public static final Color COLOR_GROUP = new Color(00, 00, 160);
// muted purple
public static final Color COLOR_EMOTE = new Color(99, 61, 139);
@ -149,8 +150,8 @@ public enum NotificationType {
// dark green
public static final Color COLOR_RESPONSE = new Color(0x006400);
// dark brown
// dark brown
public static final Color COLOR_SCENE_SETTING = new Color(0x572002);
public static final Color COLOR_SIGNIFICANT_NEGATIVE = Color.pink;
@ -161,21 +162,21 @@ public enum NotificationType {
// purple
public static final Color COLOR_TUTORIAL = new Color(172, 0, 172);
// strong bright orange
public static final Color COLOR_SUPPORT = new Color(0xff7200);
// TODO: review thinking here of using constants.
// these are tied to the ones in client.KTextEdit.gui.initStylesForTextPane
// so should we tie them together somehow?
// also the definitions are crazy.
// normal is bold
public static final String NORMALSTYLE = "normal";
// regular is not bold
public static final String REGULAR = "regular";
// fwiw, "bold" is blue, italic, bigger than normal, bold and blue.
/**
* The mapping mnemonic.
*/

View file

@ -13,6 +13,7 @@
package games.stendhal.server.actions;
import games.stendhal.common.NotificationType;
import games.stendhal.server.actions.admin.AdministrationAction;
import games.stendhal.server.core.engine.GameEvent;
import games.stendhal.server.core.engine.SingletonRepository;
import games.stendhal.server.core.rp.group.Group;
@ -107,6 +108,10 @@ public class GroupManagementAction implements ActionListener {
player.sendPrivateText(NotificationType.ERROR, "You cannot invite yourself into a group.");
return;
}
if (targetPlayer.isGhost() && player.getAdminLevel() < AdministrationAction.getLevelForCommand("ghostmode").intValue()) {
player.sendPrivateText(NotificationType.ERROR, "Player " + targetPlayer.getName() + " is not online");
return;
}
if (GagManager.checkIsGaggedAndInformPlayer(player)) {
return;

View file

@ -271,7 +271,7 @@ public class GenerateINI {
out.println();
out.println("server_typeGame=" + gameName);
out.println("server_name=" + gameName + " Marauroa server");
out.println("server_version=0.91.5");
out.println("server_version=0.92");
out.println("server_contact=https://sourceforge.net/tracker/?atid=514826&group_id=66537&func=browse");
out.println();
out.println("# Extensions configured on the server. Enable at will.");

View file

@ -135,7 +135,8 @@ public class StendhalPlayerDatabase {
+ "'age.week.one', 'age.month.one', 'age.month.two', 'age.month.three', "
+ "'age.month.four', 'age.month.five', 'age.month.six', 'age.month.seven', "
+ "'age.month.eight', 'age.month.nine', 'age.month.ten', 'age.month.eleven', "
+ "'age.year.one', 'quest.special.dm.025', 'quest.special.susi', 'item.produce.flour')", null);
+ "'age.year.one', 'quest.special.dm.025', 'quest.special.susi', 'item.produce.flour', "
+ "'quest.special.santa', 'quest.special.bunny')", null);
transaction.execute("UPDATE achievement SET identifier='xp.level.010' WHERE identifier='xp.level.10'", null);
transaction.execute("UPDATE achievement SET identifier='xp.level.050' WHERE identifier='xp.level.50'", null);
}

View file

@ -29,7 +29,7 @@ import marauroa.server.db.TransactionPool;
*
*/
public class AchievementDAO {
/**
* logs a reached achievement into the database
*
@ -39,19 +39,19 @@ public class AchievementDAO {
*/
public void saveReachedAchievement(Integer achievementId, String playerName) throws SQLException {
DBTransaction transaction = TransactionPool.get().beginWork();
saveReachedAchievement(achievementId, playerName, transaction);
saveReachedAchievement(transaction, achievementId, playerName);
TransactionPool.get().commit(transaction);
}
/**
* logs a reached achievement into the database
*
* @param transaction
* @param achievementId
* @param playerName
* @param transaction
* @throws SQLException
*/
public void saveReachedAchievement(Integer achievementId, String playerName, DBTransaction transaction) throws SQLException {
public void saveReachedAchievement(DBTransaction transaction, Integer achievementId, String playerName) throws SQLException {
String query = "INSERT INTO reached_achievement " +
"(charname, achievement_id) VALUES" +
"('[charname]','[achievement_id]');";
@ -70,7 +70,7 @@ public class AchievementDAO {
*/
public int saveAchievement(Achievement achievement) throws SQLException {
DBTransaction transaction = TransactionPool.get().beginWork();
int achievementId = saveAchievement(achievement, transaction);
int achievementId = saveAchievement(transaction, achievement);
TransactionPool.get().commit(transaction);
return achievementId;
}
@ -83,8 +83,7 @@ public class AchievementDAO {
* @return the id of the stored achievement
* @throws SQLException
*/
public int saveAchievement(Achievement achievement,
DBTransaction transaction) throws SQLException {
public int saveAchievement(DBTransaction transaction, Achievement achievement) throws SQLException {
int achievementId = 0;
String query = "INSERT INTO achievement " +
"(identifier, title, category, description, base_score) VALUES " +
@ -99,17 +98,17 @@ public class AchievementDAO {
achievementId = transaction.getLastInsertId("achievement", "id");
return achievementId;
}
/**
* Updates the achievement with the given id
*
* @param id
* @param achievement
* @throws SQLException
* @throws SQLException
*/
public void updateAchievement(Integer id, Achievement achievement) throws SQLException {
DBTransaction transaction = TransactionPool.get().beginWork();
updateAchievement(id, achievement, transaction);
updateAchievement(transaction, id, achievement);
TransactionPool.get().commit(transaction);
}
@ -119,10 +118,9 @@ public class AchievementDAO {
* @param id
* @param achievement
* @param transaction
* @throws SQLException
* @throws SQLException
*/
public void updateAchievement(Integer id, Achievement achievement,
DBTransaction transaction) throws SQLException {
public void updateAchievement(DBTransaction transaction, Integer id, Achievement achievement) throws SQLException {
String query = "UPDATE achievement SET " +
"identifier='[identifier]', " +
"title='[title]', " +
@ -171,28 +169,28 @@ public class AchievementDAO {
}
return map;
}
/**
* Loads all achievements a player has reached
* @param playerName
* @return set identifiers of achievements reached by playerName
* @throws SQLException
* @throws SQLException
*/
public Set<String> loadAllReachedAchievementsOfPlayer(String playerName) throws SQLException {
DBTransaction transaction = TransactionPool.get().beginWork();
Set<String> set = loadAllReachedAchievementsOfPlayer(playerName, transaction);
Set<String> set = loadAllReachedAchievementsOfPlayer(transaction, playerName);
TransactionPool.get().commit(transaction);
return set;
}
/**
* Loads all achievements a player has reached
* @param playerName
* @param transaction
* @return set identifiers of achievements reached by playerName
* @throws SQLException
* @throws SQLException
*/
public Set<String> loadAllReachedAchievementsOfPlayer(String playerName, DBTransaction transaction) throws SQLException {
public Set<String> loadAllReachedAchievementsOfPlayer(DBTransaction transaction, String playerName) throws SQLException {
Map<String, Object> params = new HashMap<String, Object>();
params.put("playername", playerName);
String query = "SELECT identifier FROM achievement a JOIN reached_achievement ra ON ra.achievement_id = a.id WHERE ra.charname = '[playername]';";

View file

@ -28,31 +28,33 @@ import marauroa.server.db.TransactionPool;
*
*/
public class PendingAchievementDAO {
/**
* Get details on pending achievements for a given player
* Delete used records for a pending achievement for a given player
*
* @param charname
* return details as param and count
* @param transaction
* @param charname
* @throws SQLException
*/
public Map<String, Map<String, Integer>> getPendingAchievementDetails(String charname) throws SQLException {
DBTransaction transaction = TransactionPool.get().beginWork();
Map<String, Map<String, Integer>> map = getPendingAchievementDetails(charname, transaction);
TransactionPool.get().commit(transaction);
return map;
public void deletePendingAchievementDetails(DBTransaction transaction, String charname) throws SQLException {
String query = "DELETE FROM pending_achievement " +
"WHERE charname = '[charname]';";
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("charname", charname);
transaction.execute(query, parameters);
}
/**
* Get details on pending achievements for a given player
* Get details on pending achievements for a given player
*
* @param charname
* @param transaction
* @param charname
* @return details as param and count
* @throws SQLException
*/
public Map<String, Map<String, Integer>> getPendingAchievementDetails(String charname, DBTransaction transaction) throws SQLException {
public Map<String, Map<String, Integer>> getPendingAchievementDetails(DBTransaction transaction, String charname) throws SQLException {
MapOfMaps<String, String, Integer> map = new MapOfMaps<String, String, Integer>();
String query = "SELECT identifier, param, cnt FROM pending_achievement " +
"JOIN achievement on achievement_id = achievement.id " +
@ -69,33 +71,31 @@ public class PendingAchievementDAO {
return map;
}
/**
* Get details on pending achievements for a given player
*
* @param charname
* return details as param and count
* @throws SQLException
*/
public Map<String, Map<String, Integer>> getPendingAchievementDetails(String charname) throws SQLException {
DBTransaction transaction = TransactionPool.get().beginWork();
Map<String, Map<String, Integer>> map = getPendingAchievementDetails(transaction, charname);
TransactionPool.get().commit(transaction);
return map;
}
/**
* Delete used records for a pending achievement for a given player
*
* @param charname
* @param charname
* @throws SQLException
*/
public void deletePendingAchievementDetails(String charname) throws SQLException {
DBTransaction transaction = TransactionPool.get().beginWork();
deletePendingAchievementDetails(charname, transaction);
deletePendingAchievementDetails(transaction, charname);
TransactionPool.get().commit(transaction);
}
/**
* Delete used records for a pending achievement for a given player
*
* @param charname
* @param transaction
* @throws SQLException
*/
public void deletePendingAchievementDetails(String charname, DBTransaction transaction) throws SQLException {
String query = "DELETE FROM pending_achievement " +
"WHERE charname = '[charname]';";
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("charname", charname);
transaction.execute(query, parameters);
}
}

View file

@ -24,10 +24,10 @@ import marauroa.server.game.db.DAORegister;
/**
* Deletes used details for pending or partially gained achievements from a table
*
* @author kymara
* @author kymara
*/
public class DeletePendingAchievementDetailsCommand extends AbstractDBCommand {
private final Player player;
/**
@ -40,7 +40,7 @@ public class DeletePendingAchievementDetailsCommand extends AbstractDBCommand {
@Override
public void execute(DBTransaction transaction) throws SQLException,
IOException {
DAORegister.get().get(PendingAchievementDAO.class).deletePendingAchievementDetails(player.getName(), transaction);
DAORegister.get().get(PendingAchievementDAO.class).deletePendingAchievementDetails(transaction, player.getName());
}
/**

View file

@ -24,8 +24,8 @@ import marauroa.server.game.db.DAORegister;
*
* @author kymara
*/
public class GetCharacterCreationDateCommand extends AbstractDBCommand {
private String charname;
public class GetCharacterCreationDateCommand extends AbstractDBCommand {
private final String charname;
private Date date = null;
/**
@ -40,7 +40,7 @@ public class GetCharacterCreationDateCommand extends AbstractDBCommand {
@Override
public void execute(DBTransaction transaction) throws SQLException {
CharacterDAO dao = DAORegister.get().get(CharacterDAO.class);
date = dao.getCreationDate(charname);
date = dao.getCreationDate(transaction, charname);
}
/**

View file

@ -28,8 +28,8 @@ import marauroa.server.game.db.DAORegister;
* @author kymara / M. Fuchs
*/
public class QueryCanonicalCharacterNamesCommand extends AbstractDBCommand {
private Player player;
private Collection<String> namesToCheck;
private final Player player;
private final Collection<String> namesToCheck;
private Collection<String> validNames;
/**
@ -51,9 +51,9 @@ public class QueryCanonicalCharacterNamesCommand extends AbstractDBCommand {
for(String name : namesToCheck) {
// check for existing accounts (may be merged with the following call into only one DB query)
if (dao.getAccountName(name) != null) {
if (dao.getAccountName(transaction, name) != null) {
// get the real character name independent from the client character case
String canonicalName = dao.getCanonicalName(name);
String canonicalName = dao.getCanonicalName(transaction, name);
if (canonicalName != null) {
validNames.add(canonicalName);
@ -61,7 +61,7 @@ public class QueryCanonicalCharacterNamesCommand extends AbstractDBCommand {
}
}
}
/**
* To access the player sending the query
*
@ -91,7 +91,7 @@ public class QueryCanonicalCharacterNamesCommand extends AbstractDBCommand {
/**
* Returns a string suitable for debug output of this DBCommand.
*
*
* @return debug string
*/
@Override

View file

@ -25,7 +25,7 @@ import marauroa.server.db.command.AbstractDBCommand;
import marauroa.server.game.db.DAORegister;
public class ReadAchievementsForPlayerCommand extends AbstractDBCommand {
private Set<String> identifiers = new HashSet<String>();
private final Player player;
@ -39,7 +39,7 @@ public class ReadAchievementsForPlayerCommand extends AbstractDBCommand {
@Override
public void execute(DBTransaction transaction) throws SQLException,
IOException {
identifiers = DAORegister.get().get(AchievementDAO.class).loadAllReachedAchievementsOfPlayer(getPlayer().getName(), transaction);
identifiers = DAORegister.get().get(AchievementDAO.class).loadAllReachedAchievementsOfPlayer(transaction, getPlayer().getName());
}
public Set<String> getIdentifiers() {

View file

@ -27,8 +27,8 @@ import marauroa.server.game.db.DAORegister;
* @author hendrik
*/
public class ReadCharactersFromHallOfFameCommand extends AbstractDBCommand {
private String fametype;
private final String fametype;
private List<String> characterNames;
private int max = 10;
private boolean ascending = true;
@ -49,8 +49,7 @@ public class ReadCharactersFromHallOfFameCommand extends AbstractDBCommand {
@Override
public void execute(DBTransaction transaction) throws SQLException {
StendhalHallOfFameDAO dao = DAORegister.get().get(StendhalHallOfFameDAO.class);
characterNames = dao.getCharactersByFametype(fametype, max, ascending);
characterNames = dao.getCharactersByFametype(transaction, fametype, max, ascending);
}
/**

View file

@ -27,10 +27,10 @@ import marauroa.server.game.db.DAORegister;
/**
* Reads details for pending or partially gained achievements from a table
*
* @author kymara
* @author kymara
*/
public class ReadPendingAchievementDetailsCommand extends AbstractDBCommand {
private final Player player;
private Map<String, Map<String, Integer>> details = new HashMap<String, Map<String, Integer>>();
@ -44,17 +44,17 @@ public class ReadPendingAchievementDetailsCommand extends AbstractDBCommand {
@Override
public void execute(DBTransaction transaction) throws SQLException,
IOException {
details = DAORegister.get().get(PendingAchievementDAO.class).getPendingAchievementDetails(getPlayer().getName(), transaction);
details = DAORegister.get().get(PendingAchievementDAO.class).getPendingAchievementDetails(transaction, getPlayer().getName());
}
public Map<String, Map<String, Integer>> getDetails() {
return details;
}
public Map<String, Integer> getDetails(String identifier) {
return details.get(identifier);
}
public Player getPlayer() {
return player;
}

View file

@ -27,13 +27,13 @@ import marauroa.server.game.db.DAORegister;
* @author kymara
*/
public class StoreMessageCommand extends AbstractDBCommand {
private String source;
private String target;
private String message;
private String messagetype;
private final String source;
private final String target;
private final String message;
private final String messagetype;
private String accountName;
/**
* creates a new StoreMessageCommand
*
@ -44,30 +44,30 @@ public class StoreMessageCommand extends AbstractDBCommand {
*/
public StoreMessageCommand(String source, String target, String message, String messagetype) {
this.source = source;
this.target = target;
this.message = message;
this.messagetype = messagetype;
this.target = target;
this.message = message;
this.messagetype = messagetype;
}
@Override
public void execute(DBTransaction transaction) throws SQLException {
CharacterDAO characterdao = DAORegister.get().get(CharacterDAO.class);
accountName = characterdao.getAccountName(target);
accountName = characterdao.getAccountName(transaction, target);
if (accountName != null) {
PostmanDAO postmandao = DAORegister.get().get(PostmanDAO.class);
postmandao.storeMessage(source, target, message, messagetype);
postmandao.storeMessage(transaction, source, target, message, messagetype);
}
}
/**
* checks if account name could be found - which tells if the character whom the message was for, existed
*
* @return true if an account was found for that character name
*/
public boolean targetCharacterExists() {
return accountName != null;
return accountName != null;
}
/**
* To access the character name we queried
*
@ -76,7 +76,7 @@ public class StoreMessageCommand extends AbstractDBCommand {
public String getTarget() {
return target;
}
/**
* To access the source message sender
*
@ -85,9 +85,9 @@ public class StoreMessageCommand extends AbstractDBCommand {
public String getSource() {
return source;
}
/**
* To access the message
* To access the message
*
* @return message
*/

View file

@ -23,14 +23,14 @@ import marauroa.server.db.command.AbstractDBCommand;
import marauroa.server.game.db.DAORegister;
/**
* command to log a reached achievement to the database
*
*
* @author madmetzger
*/
public class WriteReachedAchievementCommand extends AbstractDBCommand {
private final Integer id;
private final String playerName;
/**
* create a new command
* @param id database id of the achievement
@ -45,7 +45,7 @@ public class WriteReachedAchievementCommand extends AbstractDBCommand {
public void execute(DBTransaction transaction) throws SQLException,
IOException {
AchievementDAO dao = DAORegister.get().get(AchievementDAO.class);
dao.saveReachedAchievement(id, playerName, transaction);
dao.saveReachedAchievement(transaction, id, playerName);
}
/**

View file

@ -29,20 +29,20 @@ import java.util.LinkedList;
import java.util.List;
/**
* Factory for quest achievements
*
*
* @author kymara
*/
public class FriendAchievementFactory extends AbstractAchievementFactory {
@Override
public Collection<Achievement> createAchievements() {
List<Achievement> achievements = new LinkedList<Achievement>();
// TODO: add Pacifist achievement for not participating in pvp for 6 months or more (last_pvp_action_time)
// Befriend Susi and complete quests for all children
achievements.add(createAchievement("friend.quests.children", "Childrens' friend", "Complete quests for all children",
Achievement.MEDIUM_BASE_SCORE,
Achievement.MEDIUM_BASE_SCORE,
new AndCondition(
// Susi Quest is never set to done, therefore we check just if the quest has been started (condition "anyFriends" from FoundGirl.java)
new QuestStartedCondition("susi"),
@ -63,10 +63,10 @@ public class FriendAchievementFactory extends AbstractAchievementFactory {
new QuestCompletedCondition("find_jefs_mom"),
// Hughie, Ados farmhouse
new AndCondition(new QuestActiveCondition("fishsoup_for_hughie"), new QuestNotInStateCondition("fishsoup_for_hughie", "start")))));
// quests about finding people
achievements.add(createAchievement("friend.quests.find", "Private Detective", "Find all lost and hidden people",
Achievement.HARD_BASE_SCORE,
Achievement.HARD_BASE_SCORE,
new AndCondition(
// Rat Children (Agnus)
new QuestCompletedCondition("find_rat_kids"),
@ -84,15 +84,20 @@ public class FriendAchievementFactory extends AbstractAchievementFactory {
return left < 0;
}
})));
// earn over 250 karma
achievements.add(createAchievement("friend.karma.250", "Good Samaritan", "Earn 250 Karma",
Achievement.MEDIUM_BASE_SCORE,
Achievement.MEDIUM_BASE_SCORE,
new ChatCondition() {
public boolean fire(final Player player, final Sentence sentence, final Entity entity) {
return player.getKarma()>250;
}
}));
// meet Santa Claus and Easter Bunny
achievements.add(createAchievement("friend.meet.seasonal", "Still Believing", "Meet Santa Claus and Easter Bunny",
Achievement.EASY_BASE_SCORE, new AndCondition(new QuestWithPrefixCompletedCondition("meet_santa_"), new QuestWithPrefixCompletedCondition("meet_bunny_"))));
return achievements;
}

View file

@ -22,54 +22,48 @@ import java.util.LinkedList;
import java.util.List;
/**
* Factory for quest achievements
*
*
* @author madmetzger
*/
public class QuestAchievementFactory extends AbstractAchievementFactory {
@Override
public Collection<Achievement> createAchievements() {
List<Achievement> questAchievements = new LinkedList<Achievement>();
//daily monster quest achievements
questAchievements.add(createAchievement("quest.special.daily.0010", "Semos' Protector", "Finish daily monster quest 10 times",
Achievement.EASY_BASE_SCORE, new QuestStateGreaterThanCondition("daily", 2, 9)));
questAchievements.add(createAchievement("quest.special.daily.0050", "Semos' Guardian", "Finish daily monster quest 50 times",
questAchievements.add(createAchievement("quest.special.daily.0050", "Semos' Guardian", "Finish daily monster quest 50 times",
Achievement.EASY_BASE_SCORE, new QuestStateGreaterThanCondition("daily", 2, 49)));
questAchievements.add(createAchievement("quest.special.daily.0100", "Semos' Hero", "Finish daily monster quest 100 times",
questAchievements.add(createAchievement("quest.special.daily.0100", "Semos' Hero", "Finish daily monster quest 100 times",
Achievement.MEDIUM_BASE_SCORE, new QuestStateGreaterThanCondition("daily", 2, 99)));
questAchievements.add(createAchievement("quest.special.daily.0250", "Semos' Champion", "Finish daily monster quest 250 times",
questAchievements.add(createAchievement("quest.special.daily.0250", "Semos' Champion", "Finish daily monster quest 250 times",
Achievement.MEDIUM_BASE_SCORE, new QuestStateGreaterThanCondition("daily", 2, 249)));
questAchievements.add(createAchievement("quest.special.daily.0500", "Semos' Vanquisher", "Finish daily monster quest 500 times",
questAchievements.add(createAchievement("quest.special.daily.0500", "Semos' Vanquisher", "Finish daily monster quest 500 times",
Achievement.HARD_BASE_SCORE, new QuestStateGreaterThanCondition("daily", 2, 499)));
//elf princess quest achievement
questAchievements.add(createAchievement("quest.special.elf_princess.0025", "Faiumoni's Casanova", "Finish elf princess quest 25 times",
questAchievements.add(createAchievement("quest.special.elf_princess.0025", "Faiumoni's Casanova", "Finish elf princess quest 25 times",
Achievement.MEDIUM_BASE_SCORE, new QuestStateGreaterThanCondition("elf_princess", 2, 24)));
//Maze
questAchievements.add(createAchievement("quest.special.maze", "Pathfinder", "Finish the maze",
questAchievements.add(createAchievement("quest.special.maze", "Pathfinder", "Finish the maze",
Achievement.EASY_BASE_SCORE, new QuestStateGreaterThanCondition("maze", 2, 0)));
// Meet Santa Claus
questAchievements.add(createAchievement("quest.special.santa", "Santa likes me!", "Find Santa Claus",
Achievement.EASY_BASE_SCORE, new QuestWithPrefixCompletedCondition("meet_santa_")));
// Meet Easter Bunny
questAchievements.add(createAchievement("quest.special.bunny", "Happy Easter, Bunny!", "Find Easter Bunny",
Achievement.EASY_BASE_SCORE, new QuestWithPrefixCompletedCondition("meet_bunny_")));
// Ados Deathmatch
// disabled. Currently the wrong index is being checked (it would be index 6)
// disabled. Currently the wrong index is being checked (it would be index 6)
// and as per bug report https://sourceforge.net/tracker/?func=detail&aid=3148365&group_id=1111&atid=101111 the count is not saved anyway
// questAchievements.add(createAchievement("quest.special.dm.025", "Gladiator", "Fight 25 Deathmatches",
// Achievement.HARD_BASE_SCORE, new QuestStateGreaterThanCondition("deathmatch", 1, 24)));
// have completed all quests in Semos City?
questAchievements.add(createAchievement("quest.special.semos", "Aide to Semos folk", "Complete all quests in Semos City",
Achievement.MEDIUM_BASE_SCORE, new QuestsInRegionCompletedCondition(Region.SEMOS_CITY)));
// have completed all quests in Ados City?
questAchievements.add(createAchievement("quest.special.ados", "Helper of Ados city dwellers", "Complete all quests in Ados City",
Achievement.MEDIUM_BASE_SCORE, new QuestsInRegionCompletedCondition(Region.ADOS_CITY)));
return questAchievements;
}

View file

@ -38,7 +38,7 @@ import marauroa.common.game.RPEvent;
*/
public class Group {
private static long TIMEOUT = 5*60*1000;
private static int MAX_MEMBERS = 3;
private static int MAX_MEMBERS = 5;
private final HashMap<String, Long> membersAndLastSeen = new LinkedHashMap<String, Long>();
private final HashMap<String, Long> openInvites = new HashMap<String, Long>();

View file

@ -33,9 +33,9 @@ import marauroa.server.db.command.ResultHandle;
* @author kymara
*/
public class UpdatePendingAchievementsOnLogin implements LoginListener, TurnListener {
private ResultHandle handle = new ResultHandle();
private final ResultHandle handle = new ResultHandle();
public void onLoggedIn(Player player) {
DBCommand command = new ReadPendingAchievementDetailsCommand(player);
DBCommandQueue.get().enqueueAndAwaitResult(command, handle);
@ -50,46 +50,49 @@ public class UpdatePendingAchievementsOnLogin implements LoginListener, TurnList
return;
}
Player player = command.getPlayer();
updateElfPrincessAchievement(player, command.getDetails("quest.special.elf_princess.0025"));
updateItemLoots(player, command.getDetails("item.set.black"));
updateItemLoots(player, command.getDetails("item.set.chaos"));
updateItemLoots(player, command.getDetails("item.set.shadow"));
updateItemLoots(player, command.getDetails("item.set.golden"));
// Could also check for reached achievements here. This is also checked on login but the order may vary due to the async access?
// delete the entries. We don't need feedback
DBCommand deletecommand = new DeletePendingAchievementDetailsCommand(player);
DBCommandQueue.get().enqueue(deletecommand);
}
private static void updateElfPrincessAchievement(final Player player, final Map<String, Integer> details) {
// nothing to update
if (details == null) {
return;
}
final String QUEST_SLOT = "elf_princess";
// if player didn't start this quest yet, do nothing (shouldn't be details in this case but check anyway)
if(!player.hasQuest(QUEST_SLOT)) {
return;
}
// param (key) should be "" for this one, all we need to know is the count
int missingcount = details.get("");
if (missingcount > 0) {
final String[] parts = player.getQuest(QUEST_SLOT).split(";");
// is quest slot already time stamped?
if(parts.length>=2) {
if(parts.length>2) {
int newcount = MathHelper.parseInt(parts[2])+missingcount;
player.setQuest(QUEST_SLOT, 2, "" + newcount);
return;
} else if (parts.length==2) {
// the count was not stored, so we just store the count we had in the table
player.setQuest(QUEST_SLOT, 2, "" + missingcount);
} else {
// we didn't store a time before. so we just choose the value '1' for a long ago system time
player.setQuest(QUEST_SLOT, 1, "1");
@ -98,19 +101,19 @@ public class UpdatePendingAchievementsOnLogin implements LoginListener, TurnList
}
}
}
private static void updateItemLoots(final Player player, final Map<String, Integer> details) {
// nothing to update
if (details == null) {
return;
}
// update player loots which have been stored as param (key) = itemname, count (value) = number of loots
for (Map.Entry<String, Integer> detail : details.entrySet())
{
player.incLootForItem(detail.getKey(), detail.getValue());
}
}
}

View file

@ -77,7 +77,7 @@ import java.util.List;
*
* REPETITIONS:
* <ul>
* <li> Once every 7200 hours. (5 days)</li>
* <li> Once every 7200 minutes. (5 days)</li>
* </ul>
*
* @author Vanessa Julius
@ -150,31 +150,31 @@ public class FindJefsMom extends AbstractQuest {
ConversationStates.ATTENDING,
Arrays.asList("mum", "mother", "mom"),
null,
ConversationStates.QUEST_OFFERED,
ConversationStates.ATTENDING,
"My mother Amber left me for buying some food on the market, but she didn't return #yet.",
null);
npc.add(
ConversationStates.QUEST_OFFERED,
ConversationStates.ATTENDING,
("yet"),
null,
ConversationStates.QUEST_OFFERED,
ConversationStates.ATTENDING,
"The only thing I know is, that she had a little argument with her boyfriend, #Roger #Frampton earlier...",
null);
npc.add(
ConversationStates.QUEST_OFFERED,
ConversationStates.ATTENDING,
("Jef"),
null,
ConversationStates.QUEST_OFFERED,
ConversationStates.ATTENDING,
"Yes, that is me :)",
null);
npc.add(
ConversationStates.QUEST_OFFERED,
ConversationStates.ATTENDING,
Arrays.asList("Roger Frampton", "Roger", "Frampton"),
null,
ConversationStates.QUEST_OFFERED,
ConversationStates.ATTENDING,
"Maybe Roger has some guess about where she went to. I'm not sure where he is either, I just know he sells houses somewhere here in Kirdneh.",
null);
@ -192,7 +192,7 @@ public class FindJefsMom extends AbstractQuest {
"Oh I see :) My son Jef asked you to take a look after me. He is such a nice and gentle boy! Please give him this zantedeschia here. I love these flowers! Please give it to him and tell him that I'm #fine.",
new MultipleActions(new EquipItemAction("zantedeschia", 1, true),
new SetQuestAction(QUEST_SLOT, 0, "found_mom")));
// don't put the flower on the ground - if player has no space, tell them
amber.add(ConversationStates.ATTENDING, "Jef",
@ -235,19 +235,12 @@ public class FindJefsMom extends AbstractQuest {
new MultipleActions(new DropItemAction("zantedeschia"),
new IncreaseXPAction(5000),
new IncreaseKarmaAction(15),
new SetQuestToTimeStampAction(QUEST_SLOT, 1),
addRandomNumberOfItemsAction,
new SetQuestAction(QUEST_SLOT, 0, "done"),
addRandomNumberOfItemsAction,
new IncrementQuestAction(QUEST_SLOT, 2, 1),
new SetQuestToTimeStampAction(QUEST_SLOT,1)));
npc.add(ConversationStates.ATTENDING,
Arrays.asList("flower", "zantedeschia", "fine", "amber", "done", "mum", "mother", "mom"),
new NotCondition(new PlayerHasItemWithHimCondition("zantedeschia")),
ConversationStates.ATTENDING,
"Please, that isn't nice. You didn't find my mother and you can neither prove that you did. I don't believe in you, sorry.",
null);
new SetQuestToTimeStampAction(QUEST_SLOT,1),
new SetQuestAction(QUEST_SLOT, 0, "done")));
}
@Override

View file

@ -41,13 +41,13 @@ import java.util.List;
/**
* QUEST: Sheep Growing for Nishiya
*
*
* PARTICIPANTS:
* <ul>
* <li>Nishiya (the sheep seller in Semos village)</li>
* <li>Sato (the sheep buyer in Semos city)</li>
* </ul>
*
*
* STEPS:
* <ul>
* <li>Nishiya asks you to grow a sheep.</li>
@ -55,13 +55,13 @@ import java.util.List;
* <li>Sheep is handed over to Sato.</li>
* <li>Nishiya thanks you.</li>
* </ul>
*
*
* REWARD:
* <ul>
* <li>Maximum of (XP to level 2) or (30XP)</li>
* <li>Karma: 10</li>
* </ul>
*
*
* REPETITIONS:
* <ul>
* <li>None</li>
@ -87,7 +87,7 @@ public class SheepGrowing extends AbstractQuest {
preparePlayerHandsOverSheepStep();
preparePlayerReturnsStep();
}
@Override
public String getSlotName() {
return QUEST_SLOT;
@ -100,7 +100,7 @@ public class SheepGrowing extends AbstractQuest {
return res;
}
res.add("Nishiya asked me if I could raise a sheep for him.");
final String questState = player.getQuest(QUEST_SLOT);
if (questState.equals("rejected")) {
res.add("I told Nishiya that I have to do other things now... maybe I have time for the task later.");
@ -116,30 +116,30 @@ public class SheepGrowing extends AbstractQuest {
}
return res;
}
@Override
public String getName() {
return TITLE;
}
/**
* General information for the player related to the quest.
*/
private void generalInformationDialogs() {
final SpeakerNPC npc = npcs.get("Nishiya");
npc.add(ConversationStates.ATTENDING, "Sato", null, ConversationStates.ATTENDING, "Sato is the sheep buyer of Semos city. " +
"You will find him if you follow the path to the east.", null);
npc.add(ConversationStates.QUEST_OFFERED, "Sato", null, ConversationStates.QUEST_OFFERED, "Sato is the sheep buyer of Semos city. " +
"You will find him if you follow the path to the east.", null);
List<String> berryStrings = new ArrayList<String>();
berryStrings.add("red berries");
berryStrings.add("berries");
berryStrings.add("sheepfood");
berryStrings.add("sheep food");
npc.addReply(berryStrings, "Sheep like to eat the red berries from the aeryberry bushes.");
npc.addReply("sheep", "I sell fluffy sheep, it's my #job.");
}
/**
@ -147,7 +147,7 @@ public class SheepGrowing extends AbstractQuest {
*/
private void preparePlayerGetsSheepStep() {
final SpeakerNPC npc = npcs.get("Nishiya");
// If quest is not done or started yet ask player for help (if he does not have a sheep already)
ChatCondition playerHasNoSheep = new ChatCondition() {
public boolean fire(Player player, Sentence sentence, Entity npc) {
@ -157,8 +157,8 @@ public class SheepGrowing extends AbstractQuest {
npc.add(
ConversationStates.ATTENDING,
ConversationPhrases.QUEST_MESSAGES,
new AndCondition(playerHasNoSheep,
new QuestNotInStateCondition(QUEST_SLOT, "start"),
new AndCondition(playerHasNoSheep,
new QuestNotInStateCondition(QUEST_SLOT, "start"),
new QuestNotInStateCondition(QUEST_SLOT, "handed_over"),
new QuestNotInStateCondition(QUEST_SLOT, "done")),
ConversationStates.QUEST_OFFERED,
@ -167,17 +167,17 @@ public class SheepGrowing extends AbstractQuest {
"You only have to let it eat some red berries until it reaches a weight of " + Sheep.MAX_WEIGHT + "." +
"Would you do that?",
new SetQuestAction(QUEST_SLOT, "asked"));
// If quest is offered and player says no reject the quest
npc.add(
ConversationStates.QUEST_OFFERED,
ConversationPhrases.NO_MESSAGES,
new AndCondition(playerHasNoSheep,
new AndCondition(playerHasNoSheep,
new QuestInStateCondition(QUEST_SLOT, "asked")),
ConversationStates.IDLE,
"Ok... then I have to work twice as hard these days...",
new SetQuestAction(QUEST_SLOT, "rejected"));
// If quest is still active but not handed over do not give an other sheep to the player
npc.add(
ConversationStates.ATTENDING,
@ -190,7 +190,7 @@ public class SheepGrowing extends AbstractQuest {
"I already gave you one of my sheep. " +
"If you left it on its own I can sell you a new one. Just say #buy #sheep.",
null);
// If quest is offered and player says yes, give a sheep to him.
List<ChatAction> sheepActions = new LinkedList<ChatAction>();
sheepActions.add(new SetQuestAction(QUEST_SLOT, "start"));
@ -203,7 +203,7 @@ public class SheepGrowing extends AbstractQuest {
npc.add(
ConversationStates.QUEST_OFFERED,
ConversationPhrases.YES_MESSAGES,
new AndCondition(playerHasNoSheep,
new AndCondition(playerHasNoSheep,
new QuestInStateCondition(QUEST_SLOT, "asked")),
ConversationStates.IDLE,
"Thanks! *smiles* Here is your fluffy fosterling. Be careful with her. " +
@ -239,7 +239,7 @@ public class SheepGrowing extends AbstractQuest {
}
});
removeSheepAction.add(new SetQuestAction(QUEST_SLOT, "handed_over"));
// Hand-Over condition
ChatCondition playerHasFullWeightSheep = new ChatCondition() {
public boolean fire(Player player, Sentence sentence, Entity npc) {
@ -247,13 +247,7 @@ public class SheepGrowing extends AbstractQuest {
&& player.getSheep().getWeight() >= Sheep.MAX_WEIGHT;
}
};
ChatCondition sheepIsNearEnough = new ChatCondition() {
public boolean fire(Player player, Sentence sentence, Entity npc) {
return player.hasSheep()
&& npc.squaredDistance(player.getSheep()) <= 5 * 5;
}
};
// Sato asks for sheep
final SpeakerNPC npc = npcs.get("Sato");
npc.add(
@ -261,12 +255,21 @@ public class SheepGrowing extends AbstractQuest {
ConversationPhrases.GREETING_MESSAGES,
new AndCondition(
new QuestInStateCondition(QUEST_SLOT,"start"),
playerHasFullWeightSheep,
sheepIsNearEnough),
playerHasFullWeightSheep),
ConversationStates.QUEST_ITEM_BROUGHT,
"Hello. What a nice and healthy sheep is following you there! Is that one for me?",
null);
npc.add(
ConversationStates.IDLE,
ConversationPhrases.GREETING_MESSAGES,
new AndCondition(
new QuestInStateCondition(QUEST_SLOT,"start"),
new NotCondition(playerHasFullWeightSheep)),
ConversationStates.IDLE,
"Hello. You should have sheep from Nishiya for me, he owes me one! But I want a full weight one, so come back when you have one. Bye!",
null);
// Player answers yes - Sheep is given to Sato
npc.add(
ConversationStates.QUEST_ITEM_BROUGHT,
@ -279,7 +282,7 @@ public class SheepGrowing extends AbstractQuest {
"It is a gift for a friend of mine and it would be a shame if I had no birthday present. " +
"Give thanks to Nishiya.",
new MultipleActions(removeSheepAction));
// Player answers no - Sheep stays at player
npc.add(
ConversationStates.QUEST_ITEM_BROUGHT,
@ -292,7 +295,7 @@ public class SheepGrowing extends AbstractQuest {
"He wanted to send me one a while ago...",
null);
}
/**
* The step where the player returns to Nishiya to get his reward.
*/
@ -312,7 +315,7 @@ public class SheepGrowing extends AbstractQuest {
});
reward.add(new SetQuestAction(QUEST_SLOT, "done"));
reward.add(new IncreaseKarmaAction( 10 ));
final SpeakerNPC npc = npcs.get("Nishiya");
// Asks player if he handed over the sheep
npc.add(
@ -331,7 +334,7 @@ public class SheepGrowing extends AbstractQuest {
"Thank you! You doesn't know how much I have to do these days. " +
"You really helped me out.",
new MultipleActions(reward));
// Player answers no -
// Player answers no -
npc.add(
ConversationStates.QUEST_ITEM_QUESTION,
ConversationPhrases.NO_MESSAGES,
@ -339,7 +342,7 @@ public class SheepGrowing extends AbstractQuest {
ConversationStates.IDLE,
"Well... ok. But don't forget it. Sato needs the sheep very soon.",
null);
// Player asks for quest after solving the quest
npc.add(ConversationStates.ATTENDING,
ConversationPhrases.QUEST_MESSAGES,
@ -348,9 +351,9 @@ public class SheepGrowing extends AbstractQuest {
"Sorry. I have nothing to do for you at the moment. But thank you again for your help.",
null);
}
@Override
public String getRegion() {
return Region.SEMOS_CITY;
}
}
}

View file

@ -1,230 +0,0 @@
/* $Id$ */
/***************************************************************************
* (C) Copyright 2003-2011 - Stendhal *
***************************************************************************
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
stendhal.slashActionRepository = {
"ban": {
execute: function(type, params, remainder) {
var action = {
type: "ban",
target: params[0],
hours: params[1],
reason: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 2,
getMaxParams: 2
},
"chat": {
execute: function(type, params, remainder) {
var action = {
type: type,
text: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 0,
getMaxParams: 0
},
"clear": {
execute: function(type, params, remainder) {
stendhal.ui.chatLog.clear();
return true;
},
getMinParams: 0,
getMaxParams: 0
},
"gag": {
execute: function(type, params, remainder) {
var action = {
type: "gag",
target: params[0],
minutes: params[1],
reason: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 2,
getMaxParams: 2
},
"jail": {
execute: function(type, params, remainder) {
var action = {
type: "jail",
target: params[0],
minutes: params[1],
reason: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 2,
getMaxParams: 2
},
"/" : {
execute: function(type, params, remainder) {
if (typeof(stendhal.slashActionRepository.lastPlayerTell) != "undefined") {
var action = {
type: "tell",
target: stendhal.slashActionRepository.lastPlayerTell,
text: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
}
},
getMinParams: 0,
getMaxParams: 0
},
"me": {
execute: function(type, params, remainder) {
var action = {
type: "emote",
text: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 0,
getMaxParams: 0
},
"msg" : {
execute: function(type, params, remainder) {
stendhal.slashActionRepository.lastPlayerTell = params[0];
var action = {
type: "tell",
target: params[0],
text: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 1,
getMaxParams: 1
},
"support": {
execute: function(type, params, remainder) {
var action = {
type: "support",
text: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 0,
getMaxParams: 0
},
"supportanswer" : {
execute: function(type, params, remainder) {
var action = {
type: "supportanswer",
target: params[0],
text: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 1,
getMaxParams: 1
},
"teleport": {
execute: function(type, params, remainder) {
var action = {
type: "teleport",
target: params[0],
zone: params[1],
x: params[2],
y: params[3]
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 4,
getMaxParams: 4
},
"tellall": {
execute: function(type, params, remainder) {
var action = {
type: "tellall",
text: remainder
};
marauroa.clientFramework.sendAction(action);
return true;
},
getMinParams: 0,
getMaxParams: 0
},
"_default": {
execute: function(type, params, remainder) {
var action = {
"type": type
};
if (typeof(params[0] != "undefined")) {
action.target = params[0];
if (remainder != "") {
action.args = remainder;
}
}
marauroa.clientFramework.sendAction(action);
},
getMinParams: 0,
getMaxParams: 1
},
execute: function(line) {
var array = line.trim().split(" ");
var name = array[0];
if (name[0] != "/") {
name = "/chat";
} else {
array.shift();
}
name = name.substr(1);
var action;
if (typeof(this[name]) == "undefined") {
action = this["_default"];
} else {
action = this[name];
}
if (action.getMinParams <= array.length) {
var remainder = "";
for (var i = action.getMaxParams; i < array.length; i++) {
remainder = remainder + array[i] + " ";
}
array.slice(action.getMaxParams);
return action.execute(name, array, remainder);
} else {
stendhal.ui.chatLog.addLine("error", "Missing arguments. Try /help");
return false;
}
}
}
stendhal.slashActionRepository.supporta = stendhal.slashActionRepository.supportanswer;

View file

@ -1,88 +0,0 @@
<!doctype html>
<html>
<head>
<title>Stendhal Admin Client</title>
<style type="text/css">
body {font-family: "Arial"}
#chat {border: 1px solid #000; padding:0.5em; overflow-y: scroll}
#chat p {margin: 0}
p.logerror {color: #F00}
p.logprivmsg {color: #444}
p.logclient {color: #888}
p.logerror {color: #F00}
p.loginformation {color: #ffc800}
p.lognegative {color: #f00}
p.lognormal {color: #000}
p.logpositive {color: #0f0}
p.loggroup {color: #633d8b}
p.logemote {color: #633d8b}
p.logresponse {color: #006400}
p.logscene_setting {color: #572002}
p.logsignificant_negative {color: #ff8f8f}
p.logsignificant_positive {color: #4169e1}
p.logtutorial {color: #ac00ac}
p.logsupport {color: #ff7200}
</style>
<script src="json.js"></script>
<script src="/socket.io/socket.io.js"></script>
<!-- <script src="/marauroa-compressed.js"></script> -->
<script type="text/javascript" src="marauroa.js"></script>
<script type="text/javascript" src="client-framework.js"></script>
<script type="text/javascript" src="message-factory.js"></script>
<script type="text/javascript" src="perception.js"></script>
<script type="text/javascript" src="rpobject-factory.js"></script>
<script type="text/javascript" src="stendhal-webui.js"></script>
<script type="text/javascript" src="stendhal-actions.js"></script>
</head>
<body>
<script>
function esc(msg){
return msg.replace(/</g, '&lt;').replace(/>/g, '&gt;');
};
marauroa.rpobjectFactory._default = function() {
this.onEvent = function(e) {
if (e.c == "private_text") {
stendhal.ui.chatLog.addLine(e.a.texttype.toLowerCase(), e.a.text);
}
}
}
marauroa.clientFramework.onDisconnect = function(reason, error){
stendhal.ui.chatLog.addLine("error", "Disconnected: " + error);
}
marauroa.clientFramework.onAvailableCharacterDetails = function(characters) {
if (window.location.hash) {
marauroa.clientFramework.chooseCharacter(window.location.hash.substring(1));
} else {
marauroa.clientFramework.chooseCharacter(marauroa.util.first(characters).a.name);
}
}
marauroa.clientFramework.onLoginFailed = function(reason, text) {
alert("Login failed. Please login on the Stendhal website first.");
marauroa.clientFramework.close();
document.getElementById("chatbar").disabled = true;
document.getElementById("chat").style.backgroundColor = "#AAA";
}
marauroa.clientFramework.connect(null, null);
document.onkeydown=stendhal.ui.chatBar.keydown;
</script>
<h1>Sample chat client</h1>
<form id="form" onsubmit="stendhal.ui.chatBar.send(); return false">
<input type="text" name="chatbar" id="chatbar" style="width:90%">
<input type="submit" value="Send">
</form>
<div id="chat" style="height:200px">
<p>Connecting...</p>
<noscript>JavaScript is required by the Stendhal Web Client.</noscript>
</div>
</body>
</html>

View file

@ -1,117 +0,0 @@
stendhal = {};
/**
* Stendhal User Interface
*/
stendhal.ui = {
/**
* HTML code manipulation.
*/
html: {
esc: function(msg){
return msg.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace("\n", "<br>");
}
},
//*************************************************************************
// Chat Bar
//*************************************************************************
chatBar: {
history: [],
historyIndex: 0,
clear: function() {
document.getElementById('chatbar').value = '';
},
fromHistory: function(i) {
this.historyIndex = this.historyIndex + i;
if (this.historyIndex < 0) {
this.historyIndex = 0;
}
if (this.historyIndex >= this.history.length) {
this.historyIndex = this.history.length;
this.clear();
} else {
document.getElementById('chatbar').value = this.history[this.historyIndex];
}
},
keydown: function(e) {
var event = e
if (!event) {
event = window.event;
}
if (event.shiftKey) {
var code;
if (event.which) {
code = event.which;
} else {
code = e.keyCode;
}
if (code == 38) {
stendhal.ui.chatBar.fromHistory(-1);
} else if (code == 40){
stendhal.ui.chatBar.fromHistory(1);
}
}
},
remember: function(text) {
if (this.history.length > 100) {
this.history.shift();
}
this.history[this.history.length] = text;
this.historyIndex = this.history.length;
},
send: function() {
var val = document.getElementById('chatbar').value;
var array = val.split(" ");
if (array[0] == "/choosecharacter") {
marauroa.clientFramework.chooseCharacter(array[1]);
} else if (val == '/close') {
marauroa.clientFramework.close();
} else {
if (stendhal.slashActionRepository.execute(val)) {
this.remember(val);
}
}
this.clear();
}
},
//*************************************************************************
// Chat Log
//*************************************************************************
chatLog: {
addLine: function(type, msg) {
var e = document.createElement('p');
e.className = "log" + stendhal.ui.html.esc(type);
var date = new Date();
var time = "" + date.getHours() + ":";
if (date.getHours < 10) {
time = "0" + time;
}
if (date.getMinutes() < 10) {
time = time + "0";
};
time = time + date.getMinutes();
e.innerHTML = "[" + time + "] " + stendhal.ui.html.esc(msg);
document.getElementById('chat').appendChild(e);
document.getElementById('chat').scrollTop = 1000000;
},
clear: function() {
document.getElementById("chat").innerHTML = "";
}
}
}

View file

@ -22,7 +22,7 @@ Some of these people are NPC, they will give you tasks to accomplish and hints t
</security>
<resources>
<j2se href="http://java.sun.com/products/autodl/j2se" version="1.5+" /> <!-- max-heap-size="120" -->
<jar href="http://arianne.sourceforge.net/jws/stendhal-starter-0.91.5.jar" download="eager" main="true" />
<jar href="http://arianne.sourceforge.net/jws/stendhal-starter-0.92.jar" download="eager" main="true" />
</resources>
<application-desc/>
</jnlp>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE map SYSTEM "http:/mapeditor.org/dtd/1.0/map.dtd">
<map version="1.0" orientation="orthogonal" width="16" height="16" tilewidth="32" tileheight="32">
<?xml version="1.0"?>
<map version="0.99b" orientation="orthogonal" width="16" height="16" tilewidth="32" tileheight="32">
<tileset name="wall" firstgid="1" tilewidth="32" tileheight="32">
<image source="../../tileset/building/wall/int_wall_pale.png"/>
</tileset>
@ -154,18 +153,17 @@
</layer>
<layer name="objects" width="16" height="16">
<data encoding="base64" compression="gzip">
H4sIAAAAAAAAAGNgGAWjYBRQChoZaWt+M43MBwCSYUlqAAQAAA==
H4sIAAAAAAAAAGNgGAWjYBQMdtDMSBtzAUvV+ewABAAA
</data>
</layer>
<layer name="collision" width="16" height="16" opacity="0.29">
<layer name="collision" width="16" height="16" visible="0" opacity="0.29">
<data encoding="base64" compression="gzip">
H4sIAAAAAAAAAKtlZGCoHWQYFyBHD7FmETKfXDeT4hdK9CLrJ5ZPK/2Uup8S/YTcTYr9hMQJuZ0YOVLSFy3yGgA/gMXpAAQAAA==
</data>
</layer>
<layer name="protection" width="16" height="16" opacity="0.22">
<layer name="protection" width="16" height="16" visible="0" opacity="0.22">
<data encoding="base64" compression="gzip">
H4sIAAAAAAAAAGNgoB+oY8QvRwoe1T+qfzjpHygAAD14KIcABAAA
</data>
</layer>
</map>