Compare commits
19 commits
master
...
VERSION_01
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a41681759f | ||
|
|
52dbfcbaa1 | ||
|
|
5c957584f6 | ||
|
|
6c3d66277e | ||
|
|
34b735f1ec | ||
|
|
d6143973e5 | ||
|
|
cfd976738e | ||
|
|
5b15d1d35d | ||
|
|
518ad38b09 | ||
|
|
77c60c33de | ||
|
|
2688cf8a18 | ||
|
|
042b286c21 | ||
|
|
1424391ec5 | ||
|
|
a89d346d95 | ||
|
|
fc13470c92 | ||
|
|
bba21bd166 | ||
|
|
14e29cf0c2 | ||
|
|
e77b903648 | ||
|
|
c4fe94dd86 |
23 changed files with 333 additions and 139 deletions
|
|
@ -10,8 +10,8 @@ android {
|
|||
applicationId namespace
|
||||
minSdk 21
|
||||
targetSdk 32
|
||||
versionCode 1046005
|
||||
versionName "1.46.5"
|
||||
versionCode 1047000
|
||||
versionName "1.47"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
targetSdkVersion 32
|
||||
minSdkVersion 21
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ package org.stendhalgame.client;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
|
|
@ -26,24 +28,108 @@ import android.util.Base64;
|
|||
* created by the web client page.
|
||||
*/
|
||||
public class DownloadHandler {
|
||||
private boolean result = false;
|
||||
private String message = null;;
|
||||
|
||||
private static enum DownloadType {
|
||||
IMAGE_PNG("image/png"),
|
||||
TEXT_PLAIN("text/plain"),
|
||||
UNSUPPORTED(null);
|
||||
|
||||
private String value;
|
||||
|
||||
private DownloadType(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static DownloadType fromString(final String value) {
|
||||
for (final DownloadType v: DownloadType.values()) {
|
||||
if (v.value != null && v.value.equals(value)) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return DownloadType.UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean result = false;
|
||||
private String message = null;
|
||||
|
||||
|
||||
/**
|
||||
* Checks for a supported MIME type.
|
||||
*
|
||||
* @param url
|
||||
* File or data URL.
|
||||
* @param mimetype
|
||||
* Detected MIME type.
|
||||
* @return
|
||||
* Download type or unsupported.
|
||||
*/
|
||||
private DownloadType checkMimeType(final String url, final String mimetype) {
|
||||
final DownloadType dtype = DownloadType.fromString(mimetype);
|
||||
if (DownloadType.UNSUPPORTED.equals(dtype) && url.startsWith("data:image/png;base64,")) {
|
||||
return DownloadType.IMAGE_PNG;
|
||||
}
|
||||
return dtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes file data to device storage.
|
||||
*
|
||||
* @param dir
|
||||
* Directory where new file is to be created.
|
||||
* @param basename
|
||||
* Filename of new file.
|
||||
* @param data
|
||||
* File contents to be written to storage.
|
||||
*/
|
||||
private void downloadInternal(final File dir, final String basename, final byte[] data) {
|
||||
String stacktrace = null;
|
||||
try {
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
final FileOutputStream fos = new FileOutputStream(new File(dir, basename));
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
this.result = true;
|
||||
} catch (final java.lang.NoClassDefFoundError e) {
|
||||
this.message = "an error occurred while decoding data (see debug log for more info)";
|
||||
stacktrace = stackTraceToString(e);
|
||||
} catch (java.io.IOException e) {
|
||||
this.message = "an error occurred while attempting to write file (see debug log for more info)";
|
||||
stacktrace = stackTraceToString(e);
|
||||
}
|
||||
|
||||
if (stacktrace != null) {
|
||||
Logger.error(stacktrace.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes file data to device storage.
|
||||
*
|
||||
* @param url
|
||||
* A data URL.
|
||||
* @param mimetype
|
||||
* Detected MIME type.
|
||||
*/
|
||||
public void download(final String url, final String mimetype) {
|
||||
|
||||
final Uri uri = Uri.parse(url);
|
||||
final String scheme = uri.getScheme();
|
||||
final String storageState = Environment.getExternalStorageState();
|
||||
final DownloadType dtype = checkMimeType(url, mimetype);
|
||||
|
||||
if (!MainActivity.get().getActiveClientView().isGameActive()) {
|
||||
this.message = "downloading from this page not supported";
|
||||
} else if (!"data".equals(scheme)) {
|
||||
// only data URL supported
|
||||
this.message = "download type \"" + scheme + "\" not supported";
|
||||
} else if (!"image/png".equals(mimetype) || !url.startsWith("data:image/png;base64,")) {
|
||||
} else if (DownloadType.UNSUPPORTED.equals(dtype)) {
|
||||
this.message = "mimetype not supported: " + mimetype;
|
||||
} else if (!Environment.MEDIA_MOUNTED.equals(storageState)) {
|
||||
this.message = "storage not available for writing (state: " + storageState + ")";
|
||||
} else {
|
||||
} else if (DownloadType.IMAGE_PNG.equals(dtype)) {
|
||||
final File targetDir = new File(Environment.getExternalStorageDirectory()
|
||||
+ "/Pictures/Screenshots");
|
||||
final String targetName = "stendhal_"
|
||||
|
|
@ -52,32 +138,43 @@ public class DownloadHandler {
|
|||
Logger.debug("Saving screenshot: " + targetDir.getPath()
|
||||
+ "/" + targetName + " (" + mimetype + ")");
|
||||
|
||||
String msg;
|
||||
String stacktrace = null;
|
||||
try {
|
||||
if (!targetDir.exists()) {
|
||||
targetDir.mkdirs();
|
||||
final byte[] data = Base64.decode(url.split("base64,")[1], Base64.DEFAULT);
|
||||
downloadInternal(targetDir, targetName, data);
|
||||
if (this.result) {
|
||||
this.message = "saved screenshot to " + targetDir.getPath() + "/" + targetName;
|
||||
}
|
||||
} else if (DownloadType.TEXT_PLAIN.equals(dtype)) {
|
||||
// `Environment.DIRECTORY_DOWNLOADS` fails on newer Android versions
|
||||
//final File targetDir = new File(Environment.DIRECTORY_DOWNLOADS);
|
||||
final File targetDir = new File(Environment.getExternalStorageDirectory() + "/Download");
|
||||
// default filename
|
||||
final String targetName = "stendhal_" + new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")
|
||||
.format(new Date()) + ".txt";
|
||||
|
||||
Logger.debug("Saving file: " + targetDir.getPath()
|
||||
+ "/" + targetName + " (" + mimetype + ")");
|
||||
|
||||
final String[] parts = url.split(",");
|
||||
byte[] data;
|
||||
if (parts[0].endsWith(";base64")) {
|
||||
data = Base64.decode(parts[1], Base64.DEFAULT);
|
||||
} else {
|
||||
try {
|
||||
data = URLDecoder.decode(parts[1], StandardCharsets.UTF_8.name())
|
||||
.getBytes(StandardCharsets.UTF_8);
|
||||
} catch (final java.io.UnsupportedEncodingException e) {
|
||||
this.message = "an error occurred while attempting to decode data URL (see debug log for more info)";
|
||||
Logger.error(stackTraceToString(e));
|
||||
return;
|
||||
}
|
||||
|
||||
final byte[] data = Base64.decode(url.split("base64,")[1], Base64.DEFAULT);
|
||||
final FileOutputStream fos = new FileOutputStream(new File(targetDir, targetName));
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
|
||||
this.result = true;
|
||||
msg = "saved screenshot to " + targetDir.getPath() + "/" + targetName;
|
||||
} catch (final java.lang.NoClassDefFoundError e) {
|
||||
msg = "an error occurred while decoding data (see debug log for more info)";
|
||||
stacktrace = stackTraceToString(e);
|
||||
} catch (java.io.IOException e) {
|
||||
msg = "an error occurred while attempting to write file (see debug log for more info)";
|
||||
stacktrace = stackTraceToString(e);
|
||||
}
|
||||
|
||||
this.message = msg;
|
||||
if (stacktrace != null) {
|
||||
Logger.error(stacktrace.toString());
|
||||
downloadInternal(targetDir, targetName, data);
|
||||
if (this.result) {
|
||||
this.message = "file saved to " + targetDir.getPath() + "/" + targetName;
|
||||
}
|
||||
} else {
|
||||
this.message = "an unknown error occurred";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,65 +1,9 @@
|
|||
Pizza is a popular meal of choice. With growing demand, Leander is looking to hire reliable workers to transport his hot and delicious cuisine to patrons. Couriers with reputations of on-time deliveries are being recognized for their work ethic.
|
||||
|
||||
1.46
|
||||
Some adventurers may be distressed and feel that their quest for the title of "Ultimate Collector" is in vain. Well fret no more, as Balduin is offering a ray of hope. Requests to bring him a different item are now accepted afer some time has passed.
|
||||
|
||||
*world*
|
||||
- renamed items:
|
||||
- "courgette" to "zucchini"
|
||||
- "iron" to "iron bar"
|
||||
- "salad" to "lettuce"
|
||||
- fixed many NPCs not responding to certain chat options
|
||||
- normalized spelling of some words to American English in most NPC dialogues
|
||||
- "colour" -> "color"
|
||||
- "defence" -> "defense"
|
||||
- "favour" -> "favor"
|
||||
- "favourite" -> "favorite"
|
||||
- "fulfil" -> "fulfill"
|
||||
- "organise" -> "organize"
|
||||
- "recognise" -> "rocognize"
|
||||
- Orchiwald will stop after entity collision instead of reversing path
|
||||
- foghorn sounds plays when ferry arrives at mainland & island
|
||||
- added commerce sound to more NPCs when exchanging money for items/services
|
||||
- seeds can be used from inventory
|
||||
Speaking of weapons, some creatures have once again taken up arms. For example the angel and dark angel have new swords.
|
||||
|
||||
*graphics*
|
||||
- redrawn placeholder tiles in Oni Palace
|
||||
- fixed some outfit errors
|
||||
- disabled unfinished weapon-style attack sprites
|
||||
Last but not least: We released an official Android App, which is available in the download section of our website or on F-Droid.
|
||||
|
||||
*web client*
|
||||
- improved movement handling with on-screen joystick & direction pad
|
||||
- alternate "floating" menu style
|
||||
- alternate "floating" chat panel style
|
||||
- software keyboard is automatically hidden after sending chat message
|
||||
- optional activity indicator for corpses & some signs
|
||||
- immediate configuration changes affect only the active client/browser tab
|
||||
- application menu dialog doesn't close when clicking on background
|
||||
- correct NPC title is displayed in chat options dialog
|
||||
- disabled highlighting joystick/d-pad button on mouse double-click & drag
|
||||
- joystick/d-pad isn't shown until user interface is ready
|
||||
- character name in stats panel uses default font size
|
||||
- fixed chest default open/close behavior (clicking on open chest opens inventory window instead of closing)
|
||||
- doesn't attempt to open corpse/chest inventory windows when character not in range
|
||||
- fixed chat history duplicates
|
||||
- chat options dialog remains open until NPC is no longer attending or player closes
|
||||
- fixed continuous movement state not always updated at startup
|
||||
- supports displaying members' HP status in group panel
|
||||
- fixed group panel not always showing membership after login
|
||||
- added support for touch drag-and-drop
|
||||
- fixed weather drawing to not appear to move with player
|
||||
- fixed subsequent single clicks on items to not count as double after double click
|
||||
- fixed deathmatch scrying orbs
|
||||
- fixed portal default action when on a collision tile
|
||||
- added autocomplete support
|
||||
- fixed positioning of door sprites (-2_orril_lich_palace 84 108)
|
||||
- optional click/touch indicator
|
||||
- fixed difficult to move small floating windows
|
||||
- supports chat commands tab completion
|
||||
- supports text highlighting in speech bubbles
|
||||
- fixed opening chat log context menu with touch
|
||||
- supports continued zone music when changed zone matches
|
||||
|
||||
*Android*
|
||||
- added option to clear WebView cache
|
||||
- added option to lock orientation
|
||||
- fixes for using the correct software keyboard
|
||||
- app ID changed to conform with F-Droid standards
|
||||
Please see the complete change log for further details: http://localhost/game/stendhal.html#changes
|
||||
|
|
@ -5,6 +5,6 @@
|
|||
<string name="app_name">Stendhal</string>
|
||||
<string name="intent_url_scheme">stendhalprod8gps5y99pu</string>
|
||||
<string name="build_type">prod</string>
|
||||
<string name="build_version">1.46.5</string>
|
||||
<string name="build_version">1.47</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/neutralinojs/neutralinojs/main/schemas/neutralino.config.schema.json",
|
||||
"applicationId": "org.stendhalgame.client",
|
||||
"version": "1.46.5",
|
||||
"version": "1.47",
|
||||
"defaultMode": "window",
|
||||
"port": 0,
|
||||
"documentRoot": "/resources/",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@echo off
|
||||
|
||||
set STENDHAL_VERSION=1.46.5
|
||||
set STENDHAL_VERSION=1.47
|
||||
set SERVER_JAR=stendhal-server-%STENDHAL_VERSION%.jar
|
||||
|
||||
:: change to server directory
|
||||
|
|
|
|||
2
bin/runserver.sh
Executable file → Normal file
2
bin/runserver.sh
Executable file → Normal file
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
STENDHAL_VERSION="1.46.5"
|
||||
STENDHAL_VERSION="1.47"
|
||||
SERVER_JAR="stendhal-server-${STENDHAL_VERSION}.jar"
|
||||
|
||||
# change to server directory
|
||||
|
|
|
|||
|
|
@ -92,9 +92,9 @@ version_server = http://arianne.sourceforge.net/stendhal.version
|
|||
|
||||
# current version of stendhal
|
||||
version.old = 1.46
|
||||
version = 1.46.5
|
||||
version = 1.47
|
||||
# FIXME: dynamic method to set this property?
|
||||
version.android = 1046005
|
||||
version.android = 1047000
|
||||
|
||||
# javac options
|
||||
javac.deprecation = true
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
site_name: Stendhal 1.46.5 Reference Documentation
|
||||
site_name: Stendhal 1.47 Reference Documentation
|
||||
site_description: A fun, friendly, & free multiplayer online adventure game.
|
||||
copyright: Copyright © 2003-2024 Stendhal
|
||||
site_url: https://stendhalgame.org/
|
||||
|
|
|
|||
|
|
@ -13,15 +13,17 @@ Changelog
|
|||
*web client*
|
||||
- fixed text extending past edge of notification bubbles
|
||||
- added support for saving chat log to file
|
||||
- bigger buttons for small touch enabled devices
|
||||
- larger buttons for touch-only devices
|
||||
- travel log displays while waiting for response from server
|
||||
- fixed infinite request loop when travel log items technically empty
|
||||
- supports optional tab placement above panel contents
|
||||
- travel log tabs match theme
|
||||
- added basic lighting effects support
|
||||
- added sound tab to settings dialog
|
||||
|
||||
*server fixes*
|
||||
- fixed Seven Cherubs not showing up as completed
|
||||
- fixed unable to start deathmatch after logout while in arena
|
||||
|
||||
|
||||
1.46
|
||||
|
|
|
|||
|
|
@ -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 = "1.46.5";
|
||||
public static final String VERSION = "1.47";
|
||||
|
||||
/** pre release suffix */
|
||||
// Note: This line is updated by build.xml using a regexp so be sure to adjust it in case you modify this line.
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ public class BuiltQuest extends AbstractQuest {
|
|||
res.add(completionsShown);
|
||||
}
|
||||
}
|
||||
history.applyOtherResults(player, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/***************************************************************************
|
||||
* (C) Copyright 2003-2023 - Stendhal *
|
||||
* (C) Copyright 2003-2024 - Stendhal *
|
||||
***************************************************************************
|
||||
***************************************************************************
|
||||
* *
|
||||
|
|
@ -16,6 +16,8 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import games.stendhal.common.Rand;
|
||||
import games.stendhal.common.grammar.Grammar;
|
||||
import games.stendhal.common.parser.Sentence;
|
||||
|
|
@ -103,19 +105,24 @@ public class DeliverItemTask extends QuestTaskBuilder {
|
|||
* time, or if he doesn't have a delivery to do currently.
|
||||
*/
|
||||
boolean isDeliveryTooLate(final Player player, String questSlot) {
|
||||
if (player.hasQuest(questSlot) && !player.isQuestCompleted(questSlot)) {
|
||||
final String[] questData = player.getQuest(questSlot).split(";");
|
||||
final String customerName = questData[0];
|
||||
final DeliverItemOrder customerData = orders.get(customerName);
|
||||
final long bakeTime = Long.parseLong(questData[1]);
|
||||
final long expectedTimeOfDelivery = bakeTime
|
||||
+ (long) 60 * 1000 * customerData.getExpectedMinutes();
|
||||
if (System.currentTimeMillis() > expectedTimeOfDelivery) {
|
||||
return true;
|
||||
try {
|
||||
if (player.hasQuest(questSlot) && !player.isQuestCompleted(questSlot)) {
|
||||
final String[] questData = player.getQuest(questSlot).split(";");
|
||||
final String customerName = questData[0];
|
||||
final DeliverItemOrder customerData = orders.get(customerName);
|
||||
final long bakeTime = Long.parseLong(questData[1]);
|
||||
final long expectedTimeOfDelivery = bakeTime
|
||||
+ (long) 60 * 1000 * customerData.getExpectedMinutes();
|
||||
if (System.currentTimeMillis() > expectedTimeOfDelivery) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (final NumberFormatException | ArrayIndexOutOfBoundsException e) {
|
||||
Logger.getLogger(DeliverItemTask.class).error(e);
|
||||
}
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Takes away the player's uniform, if the he is wearing it.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/***************************************************************************
|
||||
* (C) Copyright 2022 - Faiumoni e.V. *
|
||||
* (C) Copyright 2022-2024 - Faiumoni e.V. *
|
||||
***************************************************************************
|
||||
***************************************************************************
|
||||
* *
|
||||
|
|
@ -11,6 +11,12 @@
|
|||
***************************************************************************/
|
||||
package games.stendhal.server.entity.npc.quest;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import games.stendhal.server.entity.player.Player;
|
||||
|
||||
|
||||
/**
|
||||
* defines the "history" of player progress as shown in the travel log
|
||||
*
|
||||
|
|
@ -25,9 +31,13 @@ public class QuestHistoryBuilder {
|
|||
private String whenQuestCanBeRepeated;
|
||||
private String whenCompletionsShown;
|
||||
|
||||
private List<QuestHistoryResult> otherResults;
|
||||
|
||||
|
||||
// hide constructor
|
||||
QuestHistoryBuilder() {
|
||||
super();
|
||||
otherResults = new LinkedList<>();
|
||||
}
|
||||
|
||||
public QuestHistoryBuilder whenNpcWasMet(String whenNpcWasMet) {
|
||||
|
|
@ -72,6 +82,16 @@ public class QuestHistoryBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom conditional result to history.
|
||||
*
|
||||
* @param result
|
||||
* History result object to be called when history is requested.
|
||||
*/
|
||||
public void addResult(final QuestHistoryResult result) {
|
||||
otherResults.add(result);
|
||||
}
|
||||
|
||||
String getWhenNpcWasMet() {
|
||||
return whenNpcWasMet;
|
||||
}
|
||||
|
|
@ -100,4 +120,17 @@ public class QuestHistoryBuilder {
|
|||
return this.whenCompletionsShown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls results objects to update history items list.
|
||||
*
|
||||
* @param player
|
||||
* Player for which history is requested.
|
||||
* @param res
|
||||
* History items.
|
||||
*/
|
||||
void applyOtherResults(final Player player, List<String> res) {
|
||||
for (final QuestHistoryResult result: otherResults) {
|
||||
result.apply(player, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
/***************************************************************************
|
||||
* Copyright © 2024 - Faiumoni e. V. *
|
||||
***************************************************************************
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
package games.stendhal.server.entity.npc.quest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import games.stendhal.server.entity.player.Player;
|
||||
|
||||
|
||||
/**
|
||||
* Class for adding a customized result to BuiltQuest history.
|
||||
*/
|
||||
public interface QuestHistoryResult {
|
||||
|
||||
/**
|
||||
* Called when history is requested.
|
||||
*
|
||||
* @param player
|
||||
* Player for which history is requested.
|
||||
* @param res
|
||||
* History items.
|
||||
*/
|
||||
abstract void apply(Player player, List<String> res);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/***************************************************************************
|
||||
* (C) Copyright 2003-2010 - Stendhal *
|
||||
* (C) Copyright 2003-2024 - Stendhal *
|
||||
***************************************************************************
|
||||
***************************************************************************
|
||||
* *
|
||||
|
|
@ -54,6 +54,8 @@ public class DeathmatchArea implements LoginListener {
|
|||
"0_semos_mountain_n2_w"), 104, 123);
|
||||
}
|
||||
player.teleport(cowardSpot.getZone(), cowardSpot.getX(), cowardSpot.getY(), Direction.DOWN, player);
|
||||
// update slot so player can start again
|
||||
player.setQuest("deathmatch", 0, "cancel");
|
||||
player.sendPrivateText("You wake up far away from the city in the mountains. But you don't know what happened.");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -508,7 +508,7 @@ public class KillEnemyArmy extends AbstractQuest {
|
|||
fillQuestInfo(
|
||||
"Kill Enemy Army",
|
||||
"Despot Halb Errvl has a vendetta against any army who opposes him.",
|
||||
true);
|
||||
true, 3);
|
||||
step_1();
|
||||
}
|
||||
|
||||
|
|
@ -596,16 +596,4 @@ public class KillEnemyArmy extends AbstractQuest {
|
|||
public String getRegion() {
|
||||
return Region.SEMOS_SURROUNDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCompletedCount(final Player player) {
|
||||
int count = 0;
|
||||
if (player.hasQuest(QUEST_SLOT)) {
|
||||
final String[] state = player.getQuest(QUEST_SLOT).split(";");
|
||||
if (state.length > 0) {
|
||||
count = Integer.parseInt(state[state.length-1]);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@
|
|||
***************************************************************************/
|
||||
package games.stendhal.server.maps.quests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import games.stendhal.common.MathHelper;
|
||||
import games.stendhal.common.grammar.Grammar;
|
||||
import games.stendhal.server.core.engine.SingletonRepository;
|
||||
import games.stendhal.server.core.engine.StendhalRPZone;
|
||||
import games.stendhal.server.core.rp.StendhalQuestSystem;
|
||||
|
|
@ -18,7 +22,9 @@ import games.stendhal.server.entity.Outfit;
|
|||
import games.stendhal.server.entity.npc.NPCList;
|
||||
import games.stendhal.server.entity.npc.SpeakerNPC;
|
||||
import games.stendhal.server.entity.npc.quest.DeliverItemQuestBuilder;
|
||||
import games.stendhal.server.entity.npc.quest.QuestHistoryResult;
|
||||
import games.stendhal.server.entity.npc.quest.QuestManuscript;
|
||||
import games.stendhal.server.entity.player.Player;
|
||||
import games.stendhal.server.maps.Region;
|
||||
import games.stendhal.server.maps.quests.houses.HouseBuyingMain;
|
||||
import games.stendhal.server.maps.semos.bakery.ChefNPC;
|
||||
|
|
@ -60,6 +66,9 @@ import games.stendhal.server.util.ResetSpeakerNPC;
|
|||
*/
|
||||
public class PizzaDelivery implements QuestManuscript {
|
||||
|
||||
private static final String questSlot = "pizza_delivery";
|
||||
|
||||
|
||||
@Override
|
||||
public DeliverItemQuestBuilder story() {
|
||||
DeliverItemQuestBuilder quest = new DeliverItemQuestBuilder();
|
||||
|
|
@ -68,7 +77,7 @@ public class PizzaDelivery implements QuestManuscript {
|
|||
quest.info()
|
||||
.name("Pizza Delivery")
|
||||
.description("Leander's pizza business is doing so well that he now recruits delivery boys and girls.")
|
||||
.internalName("pizza_delivery")
|
||||
.internalName(questSlot)
|
||||
.repeatableAfterMinutes(0)
|
||||
.minLevel(0)
|
||||
.region(Region.SEMOS_CITY)
|
||||
|
|
@ -84,7 +93,8 @@ public class PizzaDelivery implements QuestManuscript {
|
|||
.whenInTime("If I hurry, I might still get there, with the pizza hot.")
|
||||
.whenOutOfTime("The pizza has already gone cold.")
|
||||
.whenQuestWasCompleted("I delivered the last pizza Leander gave to me.")
|
||||
.whenQuestCanBeRepeated("But I'd bet, Leander has more orders.");
|
||||
.whenQuestCanBeRepeated("But I'd bet, Leander has more orders.")
|
||||
.addResult(new HotDeliveryResult());
|
||||
|
||||
|
||||
quest.offer()
|
||||
|
|
@ -354,4 +364,16 @@ public class PizzaDelivery implements QuestManuscript {
|
|||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds number of hot deliveries to quest history.
|
||||
*/
|
||||
private class HotDeliveryResult implements QuestHistoryResult {
|
||||
@Override
|
||||
public void apply(Player player, List<String> res) {
|
||||
final int count = MathHelper.parseIntDefault(player.getQuest(questSlot, 3), 0);
|
||||
if (count > 0) {
|
||||
res.add("I have delivered " + count + " hot " + Grammar.plnoun(count, "pizza") + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -503,9 +503,9 @@ class LogContextMenu extends Component {
|
|||
super("contextmenu-template", true);
|
||||
this.options = options;
|
||||
|
||||
let content = "<div class=\"actionmenu\">";
|
||||
let content = "<div class=\"actionmenu verticalgroup\">";
|
||||
for (let i = 0; i < this.options.length; i++) {
|
||||
content += "<button id=\"actionbutton." + i + "\">" + stendhal.ui.html.esc(this.options[i].title) + "</button><br>";
|
||||
content += "<button class=\"actionbutton\" id=\"actionbutton." + i + "\">" + stendhal.ui.html.esc(this.options[i].title) + "</button>";
|
||||
}
|
||||
content += "</div>";
|
||||
this.componentElement.innerHTML = content;
|
||||
|
|
|
|||
|
|
@ -24,32 +24,60 @@ import { ConfigManager } from "../../../util/ConfigManager";
|
|||
|
||||
export class SoundTab extends AbstractSettingsTab {
|
||||
|
||||
private readonly sliders: SliderComponent[];
|
||||
|
||||
|
||||
constructor(parent: SettingsDialog, element: HTMLElement) {
|
||||
super(element);
|
||||
const config = ConfigManager.get();
|
||||
const sound = SoundManager.get();
|
||||
this.sliders = [];
|
||||
|
||||
const col1 = this.child("#col1")!;
|
||||
|
||||
// state of sound when dialog is created
|
||||
let soundEnabled = config.getBoolean("sound");
|
||||
|
||||
// TODO: add DOM element creation to `SettingsDialog.createCheckBox`
|
||||
const chkSound = new SettingsComponent("chk_sound", "Enable sound");
|
||||
chkSound.setValue(config.getBoolean("sound"));
|
||||
chkSound.onchange = function(evt: Event) {
|
||||
config.set("sound", (chkSound.componentElement as HTMLInputElement).checked);
|
||||
chkSound.setValue(soundEnabled);
|
||||
chkSound.onchange = (evt: Event) => {
|
||||
soundEnabled = chkSound.getValue() as boolean;
|
||||
config.set("sound", soundEnabled);
|
||||
sound.onStateChanged();
|
||||
this.setSlidersEnabled(soundEnabled);
|
||||
};
|
||||
chkSound.addTo(col1);
|
||||
|
||||
const volMaster = new SliderComponent("setting-vol-master", "Master", 0, 100);
|
||||
volMaster.setValue(sound.getVolume("master") * 100);
|
||||
volMaster.onchange = function(evt: Event) {
|
||||
sound.setVolume("master", volMaster.getValue() / 100);
|
||||
const layers = [
|
||||
["master", "Master"],
|
||||
["gui", "GUI"],
|
||||
["sfx", "Effects"],
|
||||
["creature", "Creatures"],
|
||||
["ambient", "Ambient"],
|
||||
["music", "Music"]
|
||||
];
|
||||
|
||||
for (const group of layers) {
|
||||
const layer = group[0];
|
||||
const label = group[1];
|
||||
const slider = new SliderComponent("setting-vol-" + layer, label, 0, 100);
|
||||
slider.setValue(sound.getVolume(layer) * 100);
|
||||
slider.onchange = function(evt: Event) {
|
||||
sound.setVolume(layer, slider.getValue() / 100);
|
||||
}
|
||||
slider.addTo(col1);
|
||||
this.sliders.push(slider);
|
||||
}
|
||||
volMaster.addTo(col1);
|
||||
this.setSlidersEnabled(soundEnabled);
|
||||
|
||||
// TODO:
|
||||
// - add sliders for remaining sound channels
|
||||
// - disable sliders when sound is disabled
|
||||
// - show volume level value
|
||||
}
|
||||
|
||||
private setSlidersEnabled(enabled: boolean) {
|
||||
for (const slider of this.sliders) {
|
||||
slider.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,4 +57,14 @@ export abstract class ComponentBase {
|
|||
hasFocus(): boolean {
|
||||
return document.activeElement == this.componentElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the HTML element.
|
||||
*
|
||||
* @param {boolean} enabled
|
||||
* Enabled state to be set.
|
||||
*/
|
||||
setEnabled(enabled: boolean) {
|
||||
(this.componentElement as any).disabled = !enabled;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,6 +164,33 @@ export class SettingsComponent extends WidgetComponent {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves current value of component.
|
||||
*
|
||||
* Returns the following for each component type:
|
||||
* - select: (`number`) selected index
|
||||
* - check box: (`boolean`) checked state
|
||||
* - default: (`string`) elements text value
|
||||
*
|
||||
* @returns {string|number|boolean}
|
||||
* Component value.
|
||||
*/
|
||||
getValue(): string|number|boolean {
|
||||
switch(this._type) {
|
||||
case WidgetType.SELECT:
|
||||
// selected number index
|
||||
return (this.componentElement as HTMLSelectElement).selectedIndex;
|
||||
break;
|
||||
case WidgetType.CHECK:
|
||||
// checked boolean state
|
||||
return (this.componentElement as HTMLInputElement).checked;
|
||||
break;
|
||||
default:
|
||||
// text value
|
||||
return (this.componentElement as HTMLInputElement).value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a selectable option.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue