Compare commits

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

19 commits

Author SHA1 Message Date
Jordan Irwin
a41681759f Don't assume on-time delivery when timestamp isn't available
Some checks failed
Java CI / build (push) Has been cancelled
2024-06-07 07:58:32 +02:00
Jordan Irwin
52dbfcbaa1 Fix parsing completions count of Kill Enemy Army quest 2024-06-07 07:58:16 +02:00
Jordan Irwin
5c957584f6 Add failsafe to DeliverItemTask.isDeliveryTooLate 2024-06-07 07:57:40 +02:00
Hendrik Brummermann
6c3d66277e updated Android release notes 2024-05-30 23:23:26 +02:00
Jordan Irwin
34b735f1ec
Reword changes 2024-05-26 12:51:59 -07:00
Jordan Irwin
d6143973e5
Disable sliders when sound not enabled 2024-05-26 12:49:36 -07:00
Jordan Irwin
cfd976738e
Add ComponentBase.setEnabled method 2024-05-26 12:49:35 -07:00
Jordan Irwin
5b15d1d35d
Add SettingsComponent.getValue method 2024-05-26 12:49:35 -07:00
Jordan Irwin
518ad38b09
Add sliders for remaining sound layers in settings dialog 2024-05-26 12:49:34 -07:00
Jordan Irwin
77c60c33de
Fix button spacing of chat log context menu 2024-05-26 05:03:42 -07:00
Jordan Irwin
2688cf8a18
Support plain text download in Android client
Closes: https://github.com/arianne/stendhal/issues/727
2024-05-26 04:30:05 -07:00
Jordan Irwin
042b286c21
Clean up extra trailing semicolon 2024-05-26 04:29:45 -07:00
Jordan Irwin
1424391ec5
Update changes pertaining to Android release 2024-05-25 20:08:57 -07:00
Jordan Irwin
a89d346d95
Reword changes 2024-05-25 01:12:50 -07:00
Jordan Irwin
fc13470c92
Disable Portuguese translation pending review 2024-05-25 01:06:19 -07:00
Jordan Irwin
bba21bd166
In case of logout in arena, update deathmatch slot after teleport to...
...allow player to start again

Closes: https://github.com/arianne/stendhal/issues/724
2024-05-25 01:01:04 -07:00
Jordan Irwin
14e29cf0c2
Show number of hot deliveries in Pizza Delivery quest history 2024-05-24 21:04:22 -07:00
Jordan Irwin
e77b903648
Class for adding a customized result to BuiltQuest history 2024-05-24 21:04:09 -07:00
Hendrik Brummermann
c4fe94dd86 updated version numbers 2024-05-24 06:13:17 +02:00
23 changed files with 333 additions and 139 deletions

View file

@ -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

View file

@ -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";
}
}

View file

@ -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

View file

@ -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>

View file

@ -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/",

View file

@ -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
View 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

View file

@ -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

View file

@ -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/

View file

@ -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

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 = "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.

View file

@ -86,6 +86,7 @@ public class BuiltQuest extends AbstractQuest {
res.add(completionsShown);
}
}
history.applyOtherResults(player, res);
return res;
}

View file

@ -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.

View file

@ -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);
}
}
}

View file

@ -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);
}

View file

@ -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.");
}

View file

@ -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;
}
}

View file

@ -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") + ".");
}
}
}
}

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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.
*