Compare commits
44 commits
master
...
VERSION_00
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d122d2f312 | ||
|
|
63f75892e1 | ||
|
|
0d96d840e1 | ||
|
|
d266544259 | ||
|
|
33bd04089d | ||
|
|
b9f1705f98 | ||
|
|
b9f9da1b2b | ||
|
|
68b202feab | ||
|
|
72e5f94fc2 | ||
|
|
9c7e4016a2 | ||
|
|
5481e959ea | ||
|
|
db5314d95c | ||
|
|
bbbce08552 | ||
|
|
acd2dfa173 | ||
|
|
47af4d3b82 | ||
|
|
7f39d3f3a4 | ||
|
|
b125e64b71 | ||
|
|
47489f8805 | ||
|
|
573e7525e1 | ||
|
|
4ce712f90f | ||
|
|
4f368781a5 | ||
|
|
77c090b027 | ||
|
|
3af7a3de81 | ||
|
|
a736031491 | ||
|
|
b8b9e40ef4 | ||
|
|
558cc536aa | ||
|
|
061a9c90e2 | ||
|
|
8a1315a96b | ||
|
|
34e51b64f6 | ||
|
|
02319cbac9 | ||
|
|
6f20d5ddb6 | ||
|
|
0f6a9dcd01 | ||
|
|
34b1865a53 | ||
|
|
4794177320 | ||
|
|
d11ef4bdd7 | ||
|
|
cf5413d6a1 | ||
|
|
cbdc147d4d | ||
|
|
61131b8810 | ||
|
|
ac309393aa | ||
|
|
4a977c5ed4 | ||
|
|
7f63885bc3 | ||
|
|
c8d62e9db4 | ||
|
|
f668fc51c9 | ||
|
|
6d52587f4e |
59 changed files with 1695 additions and 1039 deletions
|
|
@ -75,8 +75,8 @@ updates_server = http://arianne.sourceforge.net/stendhal/updates
|
|||
version_server = http://arianne.sourceforge.net/stendhal.version
|
||||
|
||||
# current version of stendhal
|
||||
version.old = 0.80
|
||||
version = 0.80.5
|
||||
version.old = 0.81
|
||||
version = 0.81.3
|
||||
|
||||
# javac options
|
||||
javac.deprecation = true
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@
|
|||
<jar jarfile="${lib}/${server_jarname}" basedir="${build_server}">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="marauroa.server.marauroad"/>
|
||||
<attribute name="Class-path" value="marauroa.jar ${maps_jarname} ${xmlconf_jarname} ${maps_jarname} commons-lang.jar log4j.jar groovy.jar ${mysqldriver_jarname} mysql.jar h2.jar"/>
|
||||
<attribute name="Class-path" value=". marauroa.jar ${maps_jarname} ${xmlconf_jarname} ${maps_jarname} commons-lang.jar log4j.jar groovy.jar ${mysqldriver_jarname} mysql.jar h2.jar"/>
|
||||
</manifest>
|
||||
</jar>
|
||||
|
||||
|
|
@ -514,10 +514,13 @@
|
|||
<include name="*.html"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy todir="${buildroot}/stendhal-${version}/doc">
|
||||
<copy todir="${buildroot}/stendhal-server-${version}/doc">
|
||||
<fileset dir="doc"/>
|
||||
<fileset dir="." includes="LICENSE.txt"/>
|
||||
</copy>
|
||||
|
||||
<copy todir="${buildroot}/stendhal-server-${version}/data/conf" file="data/conf/admins.txt"/>
|
||||
|
||||
<copy todir="${buildroot}/stendhal-server-${version}" file="README.txt"/>
|
||||
<mkdir dir="${buildroot}/stendhal-server-${version}/log" />
|
||||
<touch file="${buildroot}/stendhal-server-${version}/log/empty" />
|
||||
|
|
|
|||
0
data/conf/admins.txt
Normal file
0
data/conf/admins.txt
Normal file
|
|
@ -11,6 +11,14 @@
|
|||
<zone name="7_kikareukin_clouds" level="7" x="499616" y="499744" file="Level 7/kikareukin/clouds.tmx">
|
||||
<!--Don't allow teleporting in as we don't want marked scrolls made or ring abused-->
|
||||
<configurator class-name="games.stendhal.server.core.config.zone.NoTeleportIn"/>
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.LoopedAmbientSoundSource">
|
||||
<parameter name="sound">wind-2</parameter>
|
||||
<parameter name="volume">20</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
|
||||
<entity x="0" y="0">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.area.MessagingArea">
|
||||
<parameter name="coversZone">true</parameter>
|
||||
|
|
@ -31,6 +39,14 @@
|
|||
<!-- Level 6 -->
|
||||
|
||||
<zone name="6_kikareukin_islands" level="6" x="499616" y="499744" file="Level 6/kikareukin/islands.tmx">
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.LoopedAmbientSoundSource">
|
||||
<parameter name="sound">wind-2</parameter>
|
||||
<parameter name="volume">20</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
|
||||
<entity x="0" y="0">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.area.MessagingArea">
|
||||
<parameter name="coversZone">true</parameter>
|
||||
|
|
|
|||
|
|
@ -360,12 +360,20 @@ The sharper minded amongst you might prefer to double-click directly on the crea
|
|||
</portal>
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.LoopedAmbientSoundSource">
|
||||
<parameter name="sound">wind-2</parameter>
|
||||
<parameter name="volume">25</parameter>
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_old_tavern</parameter>
|
||||
<parameter name="volume">20</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
|
||||
<!--
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.LoopedAmbientSoundSource">
|
||||
<parameter name="sound">wind-2</parameter>
|
||||
<parameter name="volume">5</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
-->
|
||||
<entity x="13" y="42">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.PeriodicAmbientSoundSource">
|
||||
<parameter name="sound">treecreak-1</parameter>
|
||||
|
|
@ -503,6 +511,13 @@ Mature sheep wanted: please speak to Sato.
|
|||
<destination zone="int_semos_bakery" ref="entrance" />
|
||||
</portal>
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_old_tavern</parameter>
|
||||
<parameter name="volume">20</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
|
||||
<!-- birds and chicken -->
|
||||
<entity x="29" y="8">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.PeriodicAmbientSoundSource">
|
||||
|
|
@ -709,7 +724,7 @@ Shepherds please note: keep watch for the wolves while searching for berries her
|
|||
</entity>
|
||||
|
||||
<!-- table on the left -->
|
||||
<entity x="44" y="39">
|
||||
<entity x="45" y="39">
|
||||
<implementation
|
||||
class-name="games.stendhal.server.entity.mapstuff.area.WalkBlocker" />
|
||||
<attribute name="description">You see a nice clean table for resting your drinks on.</attribute>
|
||||
|
|
@ -718,7 +733,7 @@ Shepherds please note: keep watch for the wolves while searching for berries her
|
|||
</entity>
|
||||
|
||||
<!-- table on the right -->
|
||||
<entity x="47" y="39">
|
||||
<entity x="48" y="39">
|
||||
<implementation
|
||||
class-name="games.stendhal.server.entity.mapstuff.area.WalkBlocker" />
|
||||
<attribute name="description">You see a nice clean table for resting your drinks on.</attribute>
|
||||
|
|
@ -737,6 +752,7 @@ Shepherds please note: keep watch for the wolves while searching for berries her
|
|||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_old_tavern</parameter>
|
||||
<parameter name="volume">80</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
|
||||
|
|
@ -1109,6 +1125,13 @@ Holy mackerel! These chips are tasty.
|
|||
class-name="games.stendhal.server.entity.mapstuff.portal.OneWayPortalDestination" />
|
||||
</portal>
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_old_tavern</parameter>
|
||||
<parameter name="volume">20</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
|
||||
<entity x="9" y="23">
|
||||
<implementation
|
||||
class-name="games.stendhal.server.maps.semos.bank.CustomerAdvisorNPC">
|
||||
|
|
@ -1954,15 +1977,35 @@ Mature sheep wanted: please speak to Sato
|
|||
<configurator
|
||||
class-name="games.stendhal.server.maps.semos.caves.BabyDragonSellerNPC" />
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
</zone>
|
||||
|
||||
<zone name="-1_kotoch_entrance_n" level="-1" x="499744" y="500000"
|
||||
file="Level -1/kotoch/entrance_n.tmx">
|
||||
<configurator class-name="games.stendhal.server.maps.kotoch.SmithNPC" />
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
</zone>
|
||||
|
||||
<zone name="-1_semos_dungeon" level="-1" x="500064" y="500000"
|
||||
file="Level -1/semos/dungeon.tmx">
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
</zone>
|
||||
|
||||
<zone name="-1_semos_jail" level="-1" x="499872" y="500000"
|
||||
|
|
@ -2004,10 +2047,24 @@ Mature sheep wanted: please speak to Sato
|
|||
|
||||
<zone name="-1_semos_catacombs_ne" level="-1" x="500064" y="499872"
|
||||
file="Level -1/semos/catacombs_ne.tmx">
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
</zone>
|
||||
|
||||
<zone name="-1_semos_catacombs_nw" level="-1" x="500000" y="499872"
|
||||
file="Level -1/semos/catacombs_nw.tmx">
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
</zone>
|
||||
|
||||
<zone name="-1_semos_catacombs_se" level="-1" x="500064" y="499936"
|
||||
|
|
@ -2015,10 +2072,24 @@ Mature sheep wanted: please speak to Sato
|
|||
<portal x="28" y="37" ref="entrance" replacing="true">
|
||||
<destination zone="0_semos_plains_n" ref="catacombs_entrance" />
|
||||
</portal>
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
</zone>
|
||||
|
||||
<zone name="-1_semos_catacombs_sw" level="-1" x="500000" y="499936"
|
||||
file="Level -1/semos/catacombs_sw.tmx">
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
</zone>
|
||||
|
||||
|
||||
|
|
@ -2091,6 +2162,13 @@ Mature sheep wanted: please speak to Sato
|
|||
|
||||
<zone name="-2_semos_dungeon" level="-2" x="500096" y="500032"
|
||||
file="Level -2/semos/dungeon.tmx">
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
</zone>
|
||||
|
||||
<zone name="-2_semos_jail" level="-2" x="499872" y="500000"
|
||||
|
|
@ -2296,6 +2374,13 @@ Mature sheep wanted: please speak to Sato
|
|||
|
||||
<zone name="-3_semos_dungeon" level="-3" x="500096" y="500032"
|
||||
file="Level -3/semos/dungeon.tmx">
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
</zone>
|
||||
|
||||
<zone name="-3_semos_catacombs" level="-3" x="500064" y="499872"
|
||||
|
|
@ -2305,6 +2390,14 @@ Mature sheep wanted: please speak to Sato
|
|||
<portal x="30" y="57" ref="upstairs" replacing="true">
|
||||
<destination zone="-2_semos_catacombs" ref="downstairs" />
|
||||
</portal>
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
|
||||
<entity x="60" y="61">
|
||||
<!--
|
||||
all dialogs for this NPC are quest-related, so we don't need a
|
||||
|
|
@ -2328,6 +2421,14 @@ Mature sheep wanted: please speak to Sato
|
|||
|
||||
<zone name="-4_semos_dungeon" level="-4" x="500096" y="500064"
|
||||
file="Level -4/semos/dungeon.tmx">
|
||||
|
||||
<entity x="1" y="1">
|
||||
<implementation class-name="games.stendhal.server.entity.mapstuff.sound.BackgroundMusicSource">
|
||||
<parameter name="sound">the_forsaken_mine</parameter>
|
||||
<parameter name="volume">40</parameter>
|
||||
</implementation>
|
||||
</entity>
|
||||
|
||||
<entity x="67" y="13">
|
||||
<implementation
|
||||
class-name="games.stendhal.server.maps.semos.dungeon.SheepBuyerNPC">
|
||||
|
|
|
|||
BIN
data/sounds/creak-1.ogg
Normal file
BIN
data/sounds/creak-1.ogg
Normal file
Binary file not shown.
BIN
data/sounds/fire-2.ogg
Normal file
BIN
data/sounds/fire-2.ogg
Normal file
Binary file not shown.
BIN
data/sounds/frog-1.ogg
Normal file
BIN
data/sounds/frog-1.ogg
Normal file
Binary file not shown.
BIN
data/sounds/gorilla-4.ogg
Normal file
BIN
data/sounds/gorilla-4.ogg
Normal file
Binary file not shown.
BIN
data/sounds/kettle-1.ogg
Normal file
BIN
data/sounds/kettle-1.ogg
Normal file
Binary file not shown.
|
|
@ -1,5 +1,32 @@
|
|||
Changelog
|
||||
---------
|
||||
0.81.3
|
||||
- prevent crash on "No line matching interface" (fixes #2970406)
|
||||
|
||||
0.81.2
|
||||
*bug fixes*
|
||||
- fixed a crash on Linux using alsa (non pulse) audio drivers and Sun Java (not OpenJDK)
|
||||
if all channels are in use by another application
|
||||
- fixed rare InvalidVersionExceptions in the network code on high latency connections
|
||||
(for example for people living outside Europe to an server in Europe)
|
||||
- handle failure to load sprite images more gracefully
|
||||
- improved logging of uncaught exceptions
|
||||
|
||||
0.81.2 contains the first results of the Client Quality Weeks. Thanks everyone for providing
|
||||
your error log files and pleace continue to do so.
|
||||
|
||||
|
||||
|
||||
0.81.1
|
||||
*bug fixes*
|
||||
- fixed an issue with 0 hp created by logging out while dying (thanks to Andrzej Kofański)
|
||||
- prevent client crash on empty soundNameList
|
||||
- renamed admins.list to admins.txt to simplify installation on windows with hidden file name extensions
|
||||
- fixed a server side classpath issue
|
||||
- updated to Marauroa 3.5 to fix an issue with null-values in the timedate columns in gameEvents and account
|
||||
|
||||
|
||||
|
||||
0.81
|
||||
*features*
|
||||
- player stats moved from the character window to the side panel
|
||||
|
|
@ -8,6 +35,7 @@ Changelog
|
|||
- having multiple correct answers for a riddle now possible
|
||||
- athor labyrinth redrawn
|
||||
- h2 database support
|
||||
- ignored players are drawn as black forms
|
||||
|
||||
*bug fixes*
|
||||
- numerous small map fixes
|
||||
|
|
@ -18,6 +46,7 @@ Changelog
|
|||
- feedback on using portals and chests from too far away
|
||||
- fixed "Drag and drop sometimes misses slots and hits ground instead" (was for drag and drop onto same slot)
|
||||
- better reject message for inactive accounts
|
||||
- fixed leander stealing all the pizzas of a player who has failed the quest previously
|
||||
|
||||
*code*
|
||||
- sound system rewritten and using oggs
|
||||
|
|
@ -31,6 +60,8 @@ Changelog
|
|||
|
||||
|
||||
|
||||
0.80.1: server only release
|
||||
* fixed logout while dying
|
||||
|
||||
0.80
|
||||
*features*
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,3 +1,3 @@
|
|||
set STENDHAL_VERSION=0.80.5
|
||||
set LOCALCLASSPATH=.;data\script;data\conf;stendhal-server-%STENDHAL_VERSION%.jar;marauroa.jar;mysql-connector.jar;log4j.jar;commons-lang.jar
|
||||
set STENDHAL_VERSION=0.81.3
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
STENDHAL_VERSION="0.80.5"
|
||||
STENDHAL_VERSION="0.81.3"
|
||||
|
||||
LOCALCLASSPATH=.:data/script/:data/conf/:stendhal-server-$STENDHAL_VERSION.jar:marauroa.jar:mysql-connector.jar:log4j.jar:commons-lang.jar
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public class GameObjects implements RPObjectChangeListener, Iterable<IEntity> {
|
|||
|
||||
while (it.hasNext()) {
|
||||
final IEntity entity = it.next();
|
||||
logger.error("Residual entity: " + entity);
|
||||
logger.debug("Residual entity: " + entity);
|
||||
entity.release();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ import org.apache.log4j.Logger;
|
|||
* can not be used for anything but trivial cases, so using Canvas instead
|
||||
*/
|
||||
public class GameScreen implements PositionChangeListener, IGameScreen {
|
||||
private static Logger logger = Logger.getLogger(GameScreen.class);
|
||||
|
||||
/**
|
||||
* Comparator used to sort entities to display.
|
||||
*/
|
||||
|
|
@ -81,7 +83,6 @@ public class GameScreen implements PositionChangeListener, IGameScreen {
|
|||
|
||||
private Canvas canvas;
|
||||
private BufferStrategy buffer;
|
||||
private Graphics g2d;
|
||||
/**
|
||||
* Static game layers.
|
||||
*/
|
||||
|
|
@ -184,14 +185,12 @@ public class GameScreen implements PositionChangeListener, IGameScreen {
|
|||
|
||||
public void componentResized(ComponentEvent e) {
|
||||
Dimension size = canvas.getSize();
|
||||
if (!size.equals(stendhal.screenSize)) {
|
||||
sw = Math.min(canvas.getWidth(), stendhal.screenSize.width);
|
||||
sh = Math.min(canvas.getHeight(), stendhal.screenSize.height);
|
||||
// Set the user clip to avoid drawing artefacts on some setups
|
||||
g2d.setClip(0, 0, sw, sh);
|
||||
calculateView();
|
||||
center();
|
||||
}
|
||||
|
||||
sw = Math.min(canvas.getWidth(), stendhal.screenSize.width);
|
||||
sh = Math.min(canvas.getHeight(), stendhal.screenSize.height);
|
||||
// Reset the view so that the player is in the center
|
||||
calculateView();
|
||||
center();
|
||||
}
|
||||
|
||||
public void componentShown(ComponentEvent e) { }
|
||||
|
|
@ -482,6 +481,7 @@ public class GameScreen implements PositionChangeListener, IGameScreen {
|
|||
draw((Graphics2D) g);
|
||||
|
||||
buffer.show();
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -489,22 +489,13 @@ public class GameScreen implements PositionChangeListener, IGameScreen {
|
|||
/*
|
||||
* swing does not want to give a valid GraphicsConfiguration until the
|
||||
* window has been drawn, so this can not be done in the constructor.
|
||||
*/
|
||||
*/
|
||||
if (buffer == null) {
|
||||
canvas.createBufferStrategy(2);
|
||||
buffer = canvas.getBufferStrategy();
|
||||
g2d = buffer.getDrawGraphics();
|
||||
} else if (buffer.contentsLost() || buffer.contentsRestored()) {
|
||||
/*
|
||||
* On some systems (windows) the draw buffer can get lost in
|
||||
* certain conditions. Recreate it if that has happened.
|
||||
*/
|
||||
final Graphics old = g2d;
|
||||
g2d = buffer.getDrawGraphics();
|
||||
old.dispose();
|
||||
buffer = canvas.getBufferStrategy();
|
||||
}
|
||||
|
||||
return g2d;
|
||||
return buffer.getDrawGraphics();
|
||||
}
|
||||
|
||||
private void draw(final Graphics2D g2d) {
|
||||
|
|
@ -605,7 +596,11 @@ public class GameScreen implements PositionChangeListener, IGameScreen {
|
|||
*/
|
||||
private void drawEntities(final Graphics2D g) {
|
||||
for (final EntityView view : views) {
|
||||
view.draw(g);
|
||||
try {
|
||||
view.draw(g);
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
47
src/games/stendhal/client/LogUncaughtExceptionHandler.java
Normal file
47
src/games/stendhal/client/LogUncaughtExceptionHandler.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* $Id$ */
|
||||
/***************************************************************************
|
||||
* (C) Copyright 2003-2010 - Marauroa *
|
||||
***************************************************************************
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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.client;
|
||||
|
||||
import marauroa.common.Log4J;
|
||||
import marauroa.common.Logger;
|
||||
|
||||
/**
|
||||
* logs uncaught exceptions to the logging system before
|
||||
* the exception is propagated to the Java VM which will
|
||||
* kill the thread. The Java VM only logs to stderr so
|
||||
* there would be no information in the logfile without
|
||||
* this class.
|
||||
*/
|
||||
public class LogUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
private static Logger logger = Log4J.getLogger(LogUncaughtExceptionHandler.class);
|
||||
private Thread.UncaughtExceptionHandler next;
|
||||
|
||||
public void uncaughtException(Thread thread, Throwable exception) {
|
||||
logger.error("Exception in thread " + thread.getName(), exception);
|
||||
System.err.println("Exception in thread " + thread.getName());
|
||||
exception.printStackTrace();
|
||||
if (next != null) {
|
||||
next.uncaughtException(thread, exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* installs this uncaught exception handler
|
||||
*/
|
||||
public static void setup() {
|
||||
LogUncaughtExceptionHandler handler = new LogUncaughtExceptionHandler();
|
||||
handler.next = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(handler);
|
||||
}
|
||||
|
||||
}
|
||||
48
src/games/stendhal/client/actions/FAQAction.java
Normal file
48
src/games/stendhal/client/actions/FAQAction.java
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package games.stendhal.client.actions;
|
||||
|
||||
import games.stendhal.client.ClientSingletonRepository;
|
||||
import games.stendhal.client.gui.BareBonesBrowserLaunch;
|
||||
import games.stendhal.client.gui.chatlog.HeaderLessEventLine;
|
||||
import games.stendhal.common.NotificationType;
|
||||
|
||||
/**
|
||||
* Opens the manual
|
||||
*/
|
||||
class FAQAction implements SlashAction {
|
||||
|
||||
/**
|
||||
* Opens the faq
|
||||
*
|
||||
* @param params ignored
|
||||
* @param remainder ignored
|
||||
* @return <code>true</code>
|
||||
*/
|
||||
public boolean execute(final String[] params, final String remainder) {
|
||||
ClientSingletonRepository.getUserInterface().addEventLine(new HeaderLessEventLine(
|
||||
"Trying to open #http://arianne.sourceforge.net/wiki/index.php/StendhalFAQ in your browser.",
|
||||
NotificationType.CLIENT));
|
||||
|
||||
BareBonesBrowserLaunch.openURL("http://stendhal.game-host.org/wiki/index.php/StendhalFAQ");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the maximum number of formal parameters.
|
||||
*
|
||||
* @return The parameter count.
|
||||
*/
|
||||
public int getMaximumParameters() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum number of formal parameters.
|
||||
*
|
||||
* @return The parameter count.
|
||||
*/
|
||||
public int getMinimumParameters() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
47
src/games/stendhal/client/actions/ManualAction.java
Normal file
47
src/games/stendhal/client/actions/ManualAction.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package games.stendhal.client.actions;
|
||||
|
||||
import games.stendhal.client.ClientSingletonRepository;
|
||||
import games.stendhal.client.gui.BareBonesBrowserLaunch;
|
||||
import games.stendhal.client.gui.chatlog.HeaderLessEventLine;
|
||||
import games.stendhal.common.NotificationType;
|
||||
|
||||
/**
|
||||
* Opens the faq
|
||||
*/
|
||||
class ManualAction implements SlashAction {
|
||||
|
||||
/**
|
||||
* Opens the manual
|
||||
*
|
||||
* @param params ignored
|
||||
* @param remainder ignored
|
||||
* @return <code>true</code>
|
||||
*/
|
||||
public boolean execute(final String[] params, final String remainder) {
|
||||
ClientSingletonRepository.getUserInterface().addEventLine(new HeaderLessEventLine(
|
||||
"Trying to open #http://arianne.sourceforge.net/wiki/index.php/StendhalManual in your browser.",
|
||||
NotificationType.CLIENT));
|
||||
|
||||
BareBonesBrowserLaunch.openURL("http://stendhal.game-host.org/wiki/index.php/StendhalManual");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of formal parameters.
|
||||
*
|
||||
* @return The parameter count.
|
||||
*/
|
||||
public int getMaximumParameters() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum number of formal parameters.
|
||||
*
|
||||
* @return The parameter count.
|
||||
*/
|
||||
public int getMinimumParameters() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
package games.stendhal.client.actions;
|
||||
|
||||
import games.stendhal.client.ClientSingletonRepository;
|
||||
import games.stendhal.client.gui.chatlog.StandardEventLine;
|
||||
import games.stendhal.client.gui.wt.core.WtWindowManager;
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.client.sound.system.Time;
|
||||
|
||||
|
||||
/**
|
||||
* Set sound characteristics.
|
||||
|
|
@ -17,7 +23,15 @@ class MuteAction implements SlashAction {
|
|||
* @return <code>true</code> if was handled.
|
||||
*/
|
||||
public boolean execute(final String[] params, final String remainder) {
|
||||
new SoundAction().toggleMute();
|
||||
boolean play = Boolean.parseBoolean(WtWindowManager.getInstance().getProperty("sound.play", "true"));
|
||||
play = !play;
|
||||
WtWindowManager.getInstance().setProperty("sound.play", Boolean.toString(play));
|
||||
SoundSystemFacade.get().mute(!play, new Time(2, Time.Unit.SEC));
|
||||
if (play) {
|
||||
ClientSingletonRepository.getUserInterface().addEventLine(new StandardEventLine("Sounds are now on."));
|
||||
} else {
|
||||
ClientSingletonRepository.getUserInterface().addEventLine(new StandardEventLine("Sounds are now off. (You may need to change the zone to stop the background music)."));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ public class SlashActionRepository {
|
|||
|
||||
actions.put("drop", new DropAction());
|
||||
|
||||
actions.put("faq", new FAQAction());
|
||||
|
||||
actions.put("gag", new GagAction());
|
||||
actions.put("gmhelp", new GMHelpAction());
|
||||
actions.put("grumpy", new GrumpyAction());
|
||||
|
|
@ -47,6 +49,7 @@ public class SlashActionRepository {
|
|||
actions.put("jail", new JailAction());
|
||||
actions.put("joinguild", new CreateGuildAction());
|
||||
|
||||
actions.put("manual", new ManualAction());
|
||||
actions.put("me", new EmoteAction());
|
||||
actions.put("msg", msg);
|
||||
actions.put("mute", new MuteAction());
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package games.stendhal.client.actions;
|
||||
|
||||
import games.stendhal.client.ClientSingletonRepository;
|
||||
import games.stendhal.client.gui.j2DClient;
|
||||
import games.stendhal.client.gui.chatlog.StandardEventLine;
|
||||
import games.stendhal.client.gui.wt.core.WtWindowManager;
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.common.MathHelper;
|
||||
|
||||
/**
|
||||
|
|
@ -25,7 +25,7 @@ class SoundAction implements SlashAction {
|
|||
final String command = params[0];
|
||||
|
||||
if (command.equals("mute")) {
|
||||
toggleMute();
|
||||
ClientSingletonRepository.getUserInterface().addEventLine(new StandardEventLine("Please use /mute."));
|
||||
} else if (command.equals("volume")) {
|
||||
volume(params);
|
||||
}
|
||||
|
|
@ -33,21 +33,6 @@ class SoundAction implements SlashAction {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* toggles the mute state of sounds.
|
||||
*/
|
||||
void toggleMute() {
|
||||
boolean play = Boolean.parseBoolean(WtWindowManager.getInstance().getProperty("sound.play", "true"));
|
||||
play = !play;
|
||||
WtWindowManager.getInstance().setProperty("sound.play", Boolean.toString(play));
|
||||
SoundSystemFacade.get().setMute(!play);
|
||||
String qualifier = " ";
|
||||
if (!play) {
|
||||
qualifier=" not ";
|
||||
}
|
||||
j2DClient.get().addEventLine(new StandardEventLine(
|
||||
"Sounds are" +qualifier + "now on."));
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the volume of sounds
|
||||
|
|
|
|||
90
src/games/stendhal/client/entity/AudibleEntity.java
Normal file
90
src/games/stendhal/client/entity/AudibleEntity.java
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package games.stendhal.client.entity;
|
||||
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.client.sound.manager.AudibleCircleArea;
|
||||
import games.stendhal.client.sound.manager.SoundFile.Type;
|
||||
import games.stendhal.client.sound.manager.SoundManager.Sound;
|
||||
import games.stendhal.client.sound.system.Time;
|
||||
import games.stendhal.common.Rand;
|
||||
import games.stendhal.common.math.Algebra;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author silvio
|
||||
*/
|
||||
public abstract class AudibleEntity extends RPEntity {
|
||||
|
||||
private AudibleCircleArea mAudibleArea = new AudibleCircleArea(Algebra.vecf(0, 0), 1.5f, 23);
|
||||
private HashMap<String, ArrayList<String>> mSoundGroups = new HashMap<String, ArrayList<String>>();
|
||||
private long mWaitTime = 0;
|
||||
|
||||
protected void addSoundsToGroup(String groupName, String... soundNames) {
|
||||
ArrayList<String> soundNameList = mSoundGroups.get(groupName);
|
||||
|
||||
if (soundNameList == null) {
|
||||
soundNameList = new ArrayList<String>();
|
||||
mSoundGroups.put(groupName, soundNameList);
|
||||
}
|
||||
|
||||
for (String name : soundNames) {
|
||||
if (SoundSystemFacade.get().loadSound(name, "audio:/" + name + ".ogg", Type.OGG, false) != null) {
|
||||
soundNameList.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Sound getRandomSoundFromGroup(String groupName, boolean getCloned) {
|
||||
ArrayList<String> soundNameList = mSoundGroups.get(groupName);
|
||||
|
||||
if ((soundNameList != null) && !soundNameList.isEmpty()) {
|
||||
String name = soundNameList.get(Rand.rand(soundNameList.size()));
|
||||
Sound sound = SoundSystemFacade.get().getSound(name);
|
||||
|
||||
if (sound != null && getCloned) {
|
||||
return sound.clone();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Sound getSound(String soundName, boolean getCloned) {
|
||||
Sound sound = SoundSystemFacade.get().getSound(soundName);
|
||||
|
||||
if (sound != null && getCloned) {
|
||||
sound = sound.clone();
|
||||
}
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPosition(double x, double y) {
|
||||
super.onPosition(x, y);
|
||||
mAudibleArea.setPosition(Algebra.vecf((float) x, (float) y));
|
||||
SoundSystemFacade.get().update();
|
||||
}
|
||||
|
||||
protected void playRandomSoundFromGroup(String groupName, float volume) {
|
||||
Sound sound = getRandomSoundFromGroup(groupName, true);
|
||||
SoundSystemFacade.get().play(sound, volume, 0, mAudibleArea, false, new Time());
|
||||
}
|
||||
|
||||
protected void playRandomSoundFromGroup(String groupName, float volume, long waitTimeInMilliSec) {
|
||||
if (mWaitTime < System.currentTimeMillis() && Rand.rand(100) < 5) {
|
||||
playRandomSoundFromGroup(groupName, volume);
|
||||
mWaitTime = System.currentTimeMillis() + waitTimeInMilliSec;
|
||||
}
|
||||
}
|
||||
|
||||
protected void playSound(String soundName, float volume) {
|
||||
Sound sound = getSound(soundName, true);
|
||||
SoundSystemFacade.get().play(sound, volume, 0, mAudibleArea, false, new Time());
|
||||
}
|
||||
}
|
||||
|
|
@ -12,16 +12,12 @@
|
|||
***************************************************************************/
|
||||
package games.stendhal.client.entity;
|
||||
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.common.Rand;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import marauroa.common.game.RPObject;
|
||||
|
||||
public class Creature extends RPEntity {
|
||||
public class Creature extends AudibleEntity {
|
||||
/**
|
||||
* Debug property.
|
||||
*/
|
||||
|
|
@ -102,95 +98,42 @@ public class Creature extends RPEntity {
|
|||
if (object.has("name")) {
|
||||
final String name = object.get("name");
|
||||
|
||||
addSoundsToGroup("attack",
|
||||
"punch-1" , "punch-2", "punch-3",
|
||||
"punch-4" , "punch-5", "punch-6",
|
||||
"swingaxe-1", "slap-1" , "arrow-1");
|
||||
|
||||
if (type.startsWith("creature")) {
|
||||
if (name.equals("wolf")) {
|
||||
moveSounds = new String[] { "bark-1", "howl-5",
|
||||
"howl-2", "howl-11" };
|
||||
|
||||
} else if (name.equals("rat") || name.equals("caverat")
|
||||
|| name.equals("venomrat")) {
|
||||
moveSounds = new String[2];
|
||||
moveSounds[0] = "rats-2";
|
||||
moveSounds[1] = "rats-41";
|
||||
// moveSounds[2]="rats-3";
|
||||
|
||||
addSoundsToGroup("move", "bark-1", "howl-5", "howl-2", "howl-11");
|
||||
} else if (name.equals("rat") || name.equals("caverat") || name.equals("venomrat")) {
|
||||
addSoundsToGroup("move", "rats-2", "rats-41"/*, "rats-3*/);
|
||||
} else if (name.equals("razorrat")) {
|
||||
moveSounds = new String[1];
|
||||
moveSounds[0] = "rats-1";
|
||||
|
||||
addSoundsToGroup("move", "rats-1");
|
||||
} else if (name.equals("gargoyle")) {
|
||||
moveSounds = new String[3];
|
||||
moveSounds[0] = "hyena-1";
|
||||
moveSounds[1] = "hyena-2";
|
||||
moveSounds[2] = "hyena-3";
|
||||
|
||||
addSoundsToGroup("move", "hyena-1", "hyena-2", "hyena-3");
|
||||
} else if (name.equals("boar")) {
|
||||
moveSounds = new String[2];
|
||||
moveSounds[0] = "pig-1";
|
||||
moveSounds[1] = "pig-2";
|
||||
|
||||
addSoundsToGroup("move", "pig-1", "pig-2");
|
||||
} else if (name.equals("bear")) {
|
||||
moveSounds = new String[3];
|
||||
moveSounds[0] = "bear-1";
|
||||
moveSounds[1] = "bear-2";
|
||||
moveSounds[2] = "bear-3";
|
||||
|
||||
addSoundsToGroup("move", "bear-1", "bear-2", "bear-3");
|
||||
} else if (name.equals("giantrat")) {
|
||||
moveSounds = new String[2];
|
||||
moveSounds[0] = "bobcat-1";
|
||||
moveSounds[1] = "leopard-11";
|
||||
|
||||
addSoundsToGroup("move", "bobcat-1", "leopard-11");
|
||||
} else if (name.equals("cobra")) {
|
||||
moveSounds = new String[1];
|
||||
moveSounds[0] = "snake-1";
|
||||
|
||||
addSoundsToGroup("move", "snake-1");
|
||||
} else if (name.equals("kobold")) {
|
||||
moveSounds = new String[4];
|
||||
moveSounds[0] = "panda-1";
|
||||
moveSounds[1] = "panda-2";
|
||||
moveSounds[2] = "racoon-1";
|
||||
moveSounds[3] = "lama-1";
|
||||
|
||||
addSoundsToGroup("move", "panda-1", "panda-2", "racoon-1", "lama-1");
|
||||
} else if (name.equals("goblin")) {
|
||||
moveSounds = new String[2];
|
||||
moveSounds[0] = "saur-3";
|
||||
moveSounds[1] = "saur-4";
|
||||
|
||||
addSoundsToGroup("move", "saur-3", "saur-4");
|
||||
} else if (name.equals("troll")) {
|
||||
moveSounds = new String[5];
|
||||
moveSounds[0] = "gorilla-1";
|
||||
moveSounds[1] = "gorilla-2";
|
||||
moveSounds[2] = "gorilla-3";
|
||||
moveSounds[3] = "gorilla-4";
|
||||
moveSounds[4] = "gorilla-5";
|
||||
|
||||
addSoundsToGroup("move", "gorilla-1", "gorilla-2", "gorilla-3", "gorilla-4", "gorilla-5");
|
||||
} else if (name.equals("orc")) {
|
||||
moveSounds = new String[2];
|
||||
moveSounds[0] = "lion-11";
|
||||
moveSounds[1] = "lion-22";
|
||||
|
||||
addSoundsToGroup("move", "lion-11", "lion-22");
|
||||
} else if (name.equals("ogre")) {
|
||||
moveSounds = new String[4];
|
||||
moveSounds[0] = "yell-1";
|
||||
moveSounds[1] = "groan-1";
|
||||
moveSounds[2] = "moan-1";
|
||||
moveSounds[3] = "fart-1";
|
||||
|
||||
addSoundsToGroup("move", "yell-1", "groan-1", "moan-1", "fart-1");
|
||||
} else if (name.equals("skeleton")) {
|
||||
moveSounds = new String[5];
|
||||
moveSounds[0] = "bones-1";
|
||||
moveSounds[1] = "evillaugh-3";
|
||||
moveSounds[2] = "evillaugh-5";
|
||||
moveSounds[3] = "ghost-1";
|
||||
moveSounds[4] = "ghost-2";
|
||||
|
||||
addSoundsToGroup("move", "bones-1", "evillaugh-3", "evillaugh-5", "ghost-1", "ghost-2");
|
||||
} else if (name.equals("cyclops")) {
|
||||
moveSounds = new String[4];
|
||||
moveSounds[0] = "laugh-33";
|
||||
moveSounds[1] = "evillaugh-4";
|
||||
moveSounds[2] = "grunt-1";
|
||||
moveSounds[3] = "grunt-2";
|
||||
|
||||
addSoundsToGroup("move", "yell-1", "laugh-33", "evillaugh-4", "grunt-1", "grunt-2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -202,8 +145,6 @@ public class Creature extends RPEntity {
|
|||
}
|
||||
}
|
||||
|
||||
private long soundWait;
|
||||
|
||||
/**
|
||||
* When the entity's position changed.
|
||||
*
|
||||
|
|
@ -215,16 +156,16 @@ public class Creature extends RPEntity {
|
|||
@Override
|
||||
protected void onPosition(final double x, final double y) {
|
||||
super.onPosition(x, y);
|
||||
|
||||
if ((soundWait < System.currentTimeMillis()) && (Rand.rand(100) < 5)) {
|
||||
if (moveSounds != null) {
|
||||
SoundSystemFacade.get().play(moveSounds[Rand.rand(moveSounds.length)], x, y, SoundLayer.CREATURE_NOISE, 100);
|
||||
}
|
||||
|
||||
soundWait = System.currentTimeMillis() + 10000L;
|
||||
}
|
||||
playRandomSoundFromGroup("move", 1.0f, 10000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDamaged(Entity attacker, int damage) {
|
||||
super.onDamaged(attacker, damage);
|
||||
playRandomSoundFromGroup("attack", 1.0f);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// RPObjectChangeListener
|
||||
//
|
||||
|
|
|
|||
|
|
@ -9,14 +9,12 @@ package games.stendhal.client.entity;
|
|||
//
|
||||
//
|
||||
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
import marauroa.common.game.RPObject;
|
||||
|
||||
/**
|
||||
* A domestic animal entity.
|
||||
*/
|
||||
public abstract class DomesticAnimal extends RPEntity {
|
||||
public abstract class DomesticAnimal extends AudibleEntity {
|
||||
/**
|
||||
* DomesticAnimal idea property.
|
||||
*/
|
||||
|
|
@ -90,7 +88,8 @@ public abstract class DomesticAnimal extends RPEntity {
|
|||
@Override
|
||||
public void initialize(final RPObject object) {
|
||||
super.initialize(object);
|
||||
|
||||
addSoundsToGroup("eat", "eat-1");
|
||||
|
||||
/*
|
||||
* Idea
|
||||
*/
|
||||
|
|
@ -145,7 +144,7 @@ public abstract class DomesticAnimal extends RPEntity {
|
|||
weight = changes.getInt("weight");
|
||||
|
||||
if (weight > oldWeight) {
|
||||
SoundSystemFacade.get().play("eat-1", getX(), getY(), SoundLayer.CREATURE_NOISE, 100);
|
||||
playRandomSoundFromGroup("eat", 1.0f);
|
||||
}
|
||||
|
||||
fireChange(PROP_WEIGHT);
|
||||
|
|
|
|||
|
|
@ -28,14 +28,6 @@ public class Entity implements RPObjectChangeListener, IEntity {
|
|||
*
|
||||
*/
|
||||
public final byte[] ID_Token = new byte[0];
|
||||
|
||||
/**
|
||||
* an array of sounds. out of these randomnly chosen sounds are played while
|
||||
* moving.
|
||||
*/
|
||||
protected String[] moveSounds;
|
||||
|
||||
|
||||
|
||||
/** The current x location of this entity. */
|
||||
protected double x;
|
||||
|
|
@ -86,11 +78,6 @@ public class Entity implements RPObjectChangeListener, IEntity {
|
|||
*/
|
||||
protected String type;
|
||||
|
||||
/**
|
||||
* Defines the distance in which the entity is heard by Player.
|
||||
*/
|
||||
protected double audibleRange = Double.POSITIVE_INFINITY;
|
||||
|
||||
/**
|
||||
* Quick work-around to prevent fireMovementEvent() from calling in
|
||||
* onChangedAdded() from other initialize() hack.
|
||||
|
|
@ -270,26 +257,6 @@ public class Entity implements RPObjectChangeListener, IEntity {
|
|||
return !rpObject.isContained();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see games.stendhal.client.entity.IEntity#getAudibleArea()
|
||||
*/
|
||||
public Rectangle2D getAudibleArea() {
|
||||
if (audibleRange == Double.POSITIVE_INFINITY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final double tempWidth = audibleRange * 2;
|
||||
return new Rectangle2D.Double(getX() - audibleRange, getY()
|
||||
- audibleRange, tempWidth, tempWidth);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see games.stendhal.client.entity.IEntity#setAudibleRange(double)
|
||||
*/
|
||||
public void setAudibleRange(final double range) {
|
||||
audibleRange = range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process attribute changes that may affect positioning. This is needed
|
||||
* because different entities may want to process coordinate changes more
|
||||
|
|
|
|||
|
|
@ -12,14 +12,12 @@
|
|||
***************************************************************************/
|
||||
package games.stendhal.client.entity;
|
||||
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
import marauroa.common.game.RPObject;
|
||||
|
||||
/**
|
||||
* A food entity.
|
||||
*/
|
||||
public abstract class Food extends Entity {
|
||||
public abstract class Food extends AudibleEntity {
|
||||
/**
|
||||
* Amount property.
|
||||
*/
|
||||
|
|
@ -64,6 +62,7 @@ public abstract class Food extends Entity {
|
|||
@Override
|
||||
public void initialize(final RPObject object) {
|
||||
super.initialize(object);
|
||||
addSoundsToGroup("food", "pop-2");
|
||||
|
||||
if (object.has("amount")) {
|
||||
amount = object.getInt("amount");
|
||||
|
|
@ -95,7 +94,7 @@ public abstract class Food extends Entity {
|
|||
// TODO this causes problems because of unidentified content refresh
|
||||
// events (e.g. synchronizing)
|
||||
if (amount > oldAmount) {
|
||||
SoundSystemFacade.get().play("pop-2", getX(), getY(), SoundLayer.CREATURE_NOISE, 100);
|
||||
playRandomSoundFromGroup("food", 1.0f);
|
||||
}
|
||||
|
||||
fireChange(PROP_AMOUNT);
|
||||
|
|
|
|||
|
|
@ -145,24 +145,6 @@ public interface IEntity {
|
|||
*/
|
||||
boolean isOnGround();
|
||||
|
||||
/**
|
||||
* @return the absolute world area (coordinates) to which audibility of
|
||||
* entity sounds is confined. Returns <b>null</b> if confines do not exist
|
||||
* (audible everywhere).
|
||||
*/
|
||||
Rectangle2D getAudibleArea();
|
||||
|
||||
/**
|
||||
* Sets the audible range as radius distance from this entity's position,
|
||||
* expressed in coordinate units. This reflects an abstract capacity of this
|
||||
* unit to emit sounds and influences the result of
|
||||
* <code>getAudibleArea()</code>.
|
||||
*
|
||||
* @param range
|
||||
* double audibility area radius in coordinate units
|
||||
*/
|
||||
void setAudibleRange(final double range);
|
||||
|
||||
/**
|
||||
* Get the resistance this has on other entities (0-100).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,48 +7,48 @@
|
|||
* Created 21.01.2006
|
||||
* Version
|
||||
*
|
||||
This program is free software. You can use, redistribute and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, version 2 of the License.
|
||||
This program is free software. You can use, redistribute and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
|
||||
http://www.gnu.org/copyleft/gpl.html.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
|
||||
http://www.gnu.org/copyleft/gpl.html.
|
||||
*/
|
||||
|
||||
package games.stendhal.client.entity;
|
||||
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.client.sound.manager.AudibleCircleArea;
|
||||
import games.stendhal.client.sound.manager.SoundFile;
|
||||
import games.stendhal.client.sound.manager.SoundManager.Sound;
|
||||
import games.stendhal.client.sound.system.Time;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
import games.stendhal.common.math.Algebra;
|
||||
import games.stendhal.common.math.Numeric;
|
||||
import marauroa.common.game.RPObject;
|
||||
|
||||
|
||||
public class LoopedSoundSource extends InvisibleEntity {
|
||||
|
||||
private String sound;
|
||||
private Sound sound;
|
||||
private int radius;
|
||||
private int volume;
|
||||
private SoundLayer layer = SoundLayer.AMBIENT_SOUND;
|
||||
|
||||
private Sound soundObject;
|
||||
private Time fadingDuration = new Time();
|
||||
|
||||
@Override
|
||||
public void onChangedAdded(RPObject object, RPObject changes) {
|
||||
// stop the current sound
|
||||
if (sound != null) {
|
||||
SoundSystemFacade.get().stop(soundObject);
|
||||
}
|
||||
SoundSystemFacade.get().stop(sound, fadingDuration);
|
||||
|
||||
// udpate
|
||||
super.onChangedAdded(object, changes);
|
||||
update(changes);
|
||||
|
||||
update(changes);
|
||||
play();
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +59,8 @@ public class LoopedSoundSource extends InvisibleEntity {
|
|||
*/
|
||||
private void update(RPObject object) {
|
||||
if (object.has("sound")) {
|
||||
sound = object.get("sound");
|
||||
String soundName = object.get("sound");
|
||||
sound = SoundSystemFacade.get().loadSound(soundName, "audio:/" + soundName + ".ogg", SoundFile.Type.OGG, true);
|
||||
}
|
||||
if (object.has("radius")) {
|
||||
radius = object.getInt("radius");
|
||||
|
|
@ -72,6 +73,18 @@ public class LoopedSoundSource extends InvisibleEntity {
|
|||
if (idx < SoundLayer.values().length) {
|
||||
layer = SoundLayer.values()[idx];
|
||||
}
|
||||
|
||||
if (layer == SoundLayer.BACKGROUND_MUSIC) {
|
||||
fadingDuration.set(3, Time.Unit.SEC);
|
||||
} else {
|
||||
fadingDuration.set(100, Time.Unit.MILLI);
|
||||
}
|
||||
}
|
||||
|
||||
if (object.has("sound") || object.has("layer")) {
|
||||
if (layer != SoundLayer.BACKGROUND_MUSIC) {
|
||||
sound = sound.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +92,9 @@ public class LoopedSoundSource extends InvisibleEntity {
|
|||
* plays the sound
|
||||
*/
|
||||
private void play() {
|
||||
soundObject = SoundSystemFacade.get().start(sound, x, y, radius, layer, volume, true);
|
||||
AudibleCircleArea area = new AudibleCircleArea(Algebra.vecf((float) x, (float) y), radius / 2.0f, radius);
|
||||
float vol = Numeric.intToFloat(volume, 100.0f);
|
||||
SoundSystemFacade.get().play(sound, vol, 0, area, true, fadingDuration);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,6 +106,6 @@ public class LoopedSoundSource extends InvisibleEntity {
|
|||
@Override
|
||||
public void release() {
|
||||
super.release();
|
||||
SoundSystemFacade.get().stop(soundObject);
|
||||
SoundSystemFacade.get().stop(sound, fadingDuration);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,12 @@
|
|||
***************************************************************************/
|
||||
package games.stendhal.client.entity;
|
||||
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.common.Rand;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
import marauroa.common.game.RPObject;
|
||||
|
||||
/**
|
||||
* An NPC entity.
|
||||
*/
|
||||
public class NPC extends RPEntity {
|
||||
public class NPC extends AudibleEntity {
|
||||
//
|
||||
// Entity
|
||||
//
|
||||
|
|
@ -40,46 +37,20 @@ public class NPC extends RPEntity {
|
|||
final String type = getType();
|
||||
|
||||
if (type.startsWith("npc")) {
|
||||
setAudibleRange(3);
|
||||
if (name.equals("Diogenes")) {
|
||||
moveSounds = new String[2];
|
||||
moveSounds[0] = "laugh-1";
|
||||
moveSounds[1] = "laugh-2";
|
||||
// SoundSystem.startSoundCycle(this, "Diogenes-patrol", 10000,
|
||||
// 20, 50, 100);
|
||||
addSoundsToGroup("move", "laugh-1", "laugh-2");
|
||||
} else if (name.equals("Carmen")) {
|
||||
moveSounds = new String[2];
|
||||
moveSounds[0] = "giggle-1";
|
||||
moveSounds[1] = "giggle-2";
|
||||
|
||||
// SoundSystem.startSoundCycle(this, "Carmen-patrol", 60000, 20,
|
||||
// 50, 75);
|
||||
addSoundsToGroup("move", "giggle-1", "giggle-2");
|
||||
} else if (name.equals("Nishiya")) {
|
||||
moveSounds = new String[3];
|
||||
moveSounds[0] = "cough-11";
|
||||
moveSounds[1] = "cough-2";
|
||||
moveSounds[2] = "cough-3";
|
||||
// SoundSystem.startSoundCycle(this, "Nishiya-patrol", 40000,
|
||||
// 20, 50, 80);
|
||||
addSoundsToGroup("move", "cough-11", "cough-2", "cough-3");
|
||||
} else if (name.equals("Margaret")) {
|
||||
moveSounds = new String[3];
|
||||
moveSounds[0] = "hiccup-1";
|
||||
moveSounds[1] = "hiccup-2";
|
||||
moveSounds[2] = "hiccup-3";
|
||||
|
||||
// SoundSystem.startSoundCycle(this, "Margaret-patrol", 30000,
|
||||
// 10, 30, 70);
|
||||
addSoundsToGroup("move", "hiccup-1", "hiccup-2", "hiccup-3");
|
||||
} else if (name.equals("Sato")) {
|
||||
moveSounds = new String[1];
|
||||
moveSounds[0] = "sneeze-1";
|
||||
// SoundSystem.startSoundCycle(this, "Sato-patrol", 60000, 30,
|
||||
// 50, 70);
|
||||
addSoundsToGroup("move", "hiccup-1", "sneeze-1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long soundWait;
|
||||
|
||||
/**
|
||||
* When the entity's position changed.
|
||||
*
|
||||
|
|
@ -91,13 +62,6 @@ public class NPC extends RPEntity {
|
|||
@Override
|
||||
protected void onPosition(final double x, final double y) {
|
||||
super.onPosition(x, y);
|
||||
|
||||
if ((soundWait < System.currentTimeMillis()) && (Rand.rand(1000) < 5)) {
|
||||
|
||||
if (moveSounds != null) {
|
||||
SoundSystemFacade.get().play(moveSounds[Rand.rand(moveSounds.length)], x, y, SoundLayer.CREATURE_NOISE, 100);
|
||||
}
|
||||
soundWait = System.currentTimeMillis() + 2000L;
|
||||
}
|
||||
playRandomSoundFromGroup("move", 1.0f, 20000);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import games.stendhal.common.NotificationType;
|
|||
import marauroa.common.game.RPObject;
|
||||
|
||||
/** A Player entity. */
|
||||
public class Player extends RPEntity {
|
||||
public class Player extends AudibleEntity {
|
||||
|
||||
/**
|
||||
* Away property.
|
||||
|
|
@ -129,6 +129,21 @@ public class Player extends RPEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(RPObject object) {
|
||||
super.initialize(object);
|
||||
|
||||
addSoundsToGroup("attack",
|
||||
"punch-1" , "punch-2", "punch-3",
|
||||
"punch-4" , "punch-5", "punch-6",
|
||||
"swingaxe-1", "slap-1" , "arrow-1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDamaged(Entity attacker, int damage) {
|
||||
super.onDamaged(attacker, damage);
|
||||
playRandomSoundFromGroup("attack", 1.0f);
|
||||
}
|
||||
|
||||
//
|
||||
// RPObjectChangeListener
|
||||
|
|
|
|||
|
|
@ -18,12 +18,9 @@ import games.stendhal.client.stendhal;
|
|||
import games.stendhal.client.gui.chatlog.HeaderLessEventLine;
|
||||
import games.stendhal.client.gui.chatlog.StandardEventLine;
|
||||
import games.stendhal.client.gui.chatlog.StandardHeaderedEventLine;
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.common.Grammar;
|
||||
import games.stendhal.common.ItemTools;
|
||||
import games.stendhal.common.NotificationType;
|
||||
import games.stendhal.common.Rand;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
|
@ -85,11 +82,11 @@ public abstract class RPEntity extends ActiveEntity {
|
|||
* The entities attacking this entity.
|
||||
*/
|
||||
protected List<Entity> attackers;
|
||||
|
||||
/*
|
||||
String[] attackSounds = { "punch-1", "punch-2", "punch-3",
|
||||
"punch-4", "punch-5", "punch-6", "swingaxe-1",
|
||||
"slap-1", "arrow-1" };
|
||||
|
||||
*/
|
||||
private boolean showBladeStrike;
|
||||
|
||||
public enum Resolution {
|
||||
|
|
@ -440,11 +437,11 @@ public abstract class RPEntity extends ActiveEntity {
|
|||
public void onDamaged(final Entity attacker, final int damage) {
|
||||
combatIconTime = System.currentTimeMillis();
|
||||
resolution = Resolution.HIT;
|
||||
try {
|
||||
/*try {
|
||||
SoundSystemFacade.get().play(attackSounds[Rand.rand(attackSounds.length)], x, y, SoundLayer.CREATURE_NOISE, 100);
|
||||
} catch (final NullPointerException e) {
|
||||
// ignore errors
|
||||
}
|
||||
}*/
|
||||
|
||||
boolean showAttackInfoForPlayer = (this.isUser() || attacker.isUser());
|
||||
showAttackInfoForPlayer = showAttackInfoForPlayer
|
||||
|
|
|
|||
|
|
@ -12,9 +12,8 @@
|
|||
***************************************************************************/
|
||||
package games.stendhal.client.entity;
|
||||
|
||||
import games.stendhal.client.sound.SoundSystemFacade;
|
||||
import games.stendhal.common.Rand;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
import marauroa.common.game.RPObject;
|
||||
|
||||
/** A Sheep entity. */
|
||||
public class Sheep extends DomesticAnimal {
|
||||
|
|
@ -23,19 +22,20 @@ public class Sheep extends DomesticAnimal {
|
|||
//
|
||||
|
||||
@Override
|
||||
protected void probableChat(final int chance) {
|
||||
public void initialize(RPObject object) {
|
||||
super.initialize(object);
|
||||
addSoundsToGroup("small", "sheep-2", "sheep-4");
|
||||
addSoundsToGroup("big" , "sheep-1", "sheep-3");
|
||||
}
|
||||
|
||||
final String[][] soundnames = { { "sheep-1", "sheep-3" },
|
||||
{ "sheep-2", "sheep-4" } };
|
||||
final int which = Rand.rand(2);
|
||||
@Override
|
||||
protected void probableChat(final int chance) {
|
||||
if (Rand.rand(100) < chance) {
|
||||
final String token;
|
||||
if (getWeight() > 50) {
|
||||
token = soundnames[0][which];
|
||||
playRandomSoundFromGroup("big", 1.0f);
|
||||
} else {
|
||||
token = soundnames[1][which];
|
||||
playRandomSoundFromGroup("small", 1.0f);
|
||||
}
|
||||
SoundSystemFacade.get().play(token, x, y, SoundLayer.CREATURE_NOISE, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import games.stendhal.client.stendhal;
|
|||
import games.stendhal.client.gui.chatlog.HeaderLessEventLine;
|
||||
import games.stendhal.client.update.Version;
|
||||
import games.stendhal.common.Grammar;
|
||||
import games.stendhal.common.KeyedSlotUtil;
|
||||
import games.stendhal.common.NotificationType;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.HashSet;
|
||||
|
||||
import marauroa.common.game.RPObject;
|
||||
|
||||
|
|
@ -22,6 +22,7 @@ import marauroa.common.game.RPObject;
|
|||
public class User extends Player {
|
||||
|
||||
private static User instance;
|
||||
private HashSet<String> ignore = new HashSet<String>();
|
||||
|
||||
private String serverVersion = null;
|
||||
|
||||
|
|
@ -267,6 +268,13 @@ public class User extends Player {
|
|||
NotificationType.ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.hasSlot("!ignore")) {
|
||||
RPObject ign = changes.getSlot("!ignore").getFirst();
|
||||
if (ign != null) {
|
||||
addIgnore(ign);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -274,6 +282,12 @@ public class User extends Player {
|
|||
public void onChangedRemoved(final RPObject base, final RPObject diff) {
|
||||
modificationCount++;
|
||||
super.onChangedRemoved(base, diff);
|
||||
if (diff.hasSlot("!ignore")) {
|
||||
RPObject ign = diff.getSlot("!ignore").getFirst();
|
||||
if (ign != null) {
|
||||
removeIgnore(ign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -362,11 +376,34 @@ public class User extends Player {
|
|||
return false;
|
||||
}
|
||||
|
||||
// compatibility with 0.79 server
|
||||
if (!User.get().rpObject.hasSlot("!ignore")) {
|
||||
return false;
|
||||
return User.get().ignore.contains(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add players to the set of ignored players.
|
||||
* Player names are the attributes prefixed with '_'.
|
||||
*
|
||||
* @param ignoreObj The container object for player names
|
||||
*/
|
||||
private void addIgnore(RPObject ignoreObj) {
|
||||
for (String attr : ignoreObj) {
|
||||
if (attr.charAt(0) == '_') {
|
||||
ignore.add(attr.substring(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove players from the set of ignored players.
|
||||
* Player names are the attributes prefixed with '_'.
|
||||
*
|
||||
* @param ignoreObj The container object for player names
|
||||
*/
|
||||
private void removeIgnore(RPObject ignoreObj) {
|
||||
for (String attr : ignoreObj) {
|
||||
if (attr.charAt(0) == '_') {
|
||||
ignore.remove(attr.substring(1));
|
||||
}
|
||||
}
|
||||
// end compatibility
|
||||
return KeyedSlotUtil.getKeyedSlot(User.get().rpObject, "!ignore", "_" + name) != null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@ public class SoundEvent extends Event<Entity> {
|
|||
if (event.has("volume")) {
|
||||
volume = event.getInt("volume");
|
||||
}
|
||||
SoundSystemFacade.get().play(event.get("sound"), entity.getX(), entity.getY(), event.getInt("radius"), layer, volume);
|
||||
int radius = 100000;
|
||||
if (event.has("radius")) {
|
||||
radius = event.getInt("radius");
|
||||
}
|
||||
SoundSystemFacade.get().play(event.get("sound"), entity.getX(), entity.getY(), radius, layer, volume);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ public class j2DClient implements UserInterface {
|
|||
chatBox.setMinimumSize(chatText.getPlayerChatText().getMinimumSize());
|
||||
|
||||
// Give the user the ability to make the the game area less tall
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pane, chatBox);
|
||||
final JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pane, chatBox);
|
||||
// Works for showing the resize, but is extremely flickery
|
||||
//splitPane.setContinuousLayout(true);
|
||||
pane.addComponentListener(new SplitPaneResizeListener(screen.getComponent(), splitPane));
|
||||
|
|
@ -397,6 +397,11 @@ public class j2DClient implements UserInterface {
|
|||
|
||||
mainFrame.getMainFrame().setMinimumSize(new Dimension(width, height));
|
||||
mainFrame.getMainFrame().setVisible(true);
|
||||
/*
|
||||
* Needed for small screens; Sometimes the divider is placed
|
||||
* incorrectly unless we explicitly set it.
|
||||
*/
|
||||
splitPane.setDividerLocation(stendhal.screenSize.height + 1);
|
||||
}
|
||||
});
|
||||
directionRelease = null;
|
||||
|
|
@ -405,7 +410,11 @@ public class j2DClient implements UserInterface {
|
|||
SlashActionRepository.register();
|
||||
|
||||
checkAndComplainAboutJavaImplementation();
|
||||
WorldObjects.addWorldListener(SoundSystemFacade.get());
|
||||
try {
|
||||
WorldObjects.addWorldListener(SoundSystemFacade.get());
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
} // constructor
|
||||
|
||||
private void checkAndComplainAboutJavaImplementation() {
|
||||
|
|
@ -467,141 +476,145 @@ public class j2DClient implements UserInterface {
|
|||
|
||||
boolean canExit = false;
|
||||
while (!canExit) {
|
||||
fps++;
|
||||
// figure out what time it is right after the screen flip then
|
||||
// later we can figure out how long we have been doing redrawing
|
||||
// / networking, then we know how long we need to sleep to make
|
||||
// the next flip happen at the right time
|
||||
try {
|
||||
fps++;
|
||||
// figure out what time it is right after the screen flip then
|
||||
// later we can figure out how long we have been doing redrawing
|
||||
// / networking, then we know how long we need to sleep to make
|
||||
// the next flip happen at the right time
|
||||
|
||||
screen.nextFrame();
|
||||
final long now = System.currentTimeMillis();
|
||||
final int delta = (int) (now - refreshTime);
|
||||
refreshTime = now;
|
||||
|
||||
logger.debug("Move objects");
|
||||
gameObjects.update(delta);
|
||||
|
||||
if (!client.isInBatchUpdate() && gameLayers.changedArea()) {
|
||||
/*
|
||||
* Update the screen
|
||||
*/
|
||||
screen.setMaxWorldSize(gameLayers.getWidth(),
|
||||
gameLayers.getHeight());
|
||||
screen.center();
|
||||
|
||||
// [Re]create the map
|
||||
screen.nextFrame();
|
||||
final long now = System.currentTimeMillis();
|
||||
final int delta = (int) (now - refreshTime);
|
||||
refreshTime = now;
|
||||
|
||||
final CollisionDetection cd = gameLayers.getCollisionDetection();
|
||||
final CollisionDetection pd = gameLayers.getProtectionDetection();
|
||||
|
||||
if (cd != null) {
|
||||
minimap.update(cd, pd,
|
||||
screen.getComponent().getGraphicsConfiguration(),
|
||||
gameLayers.getArea());
|
||||
}
|
||||
gameLayers.resetChangedArea();
|
||||
}
|
||||
|
||||
final User user = User.get();
|
||||
|
||||
if (user != null) {
|
||||
// check if the player object has changed.
|
||||
// Note: this is an exact object reference check
|
||||
if (user != lastuser) {
|
||||
character.setPlayer(user);
|
||||
keyring.setSlot(user, "keyring", gameScreen);
|
||||
inventory.setSlot(user, "bag", gameScreen);
|
||||
|
||||
lastuser = user;
|
||||
}
|
||||
}
|
||||
|
||||
if (!client.isInBatchUpdate()) {
|
||||
if (mainFrame.getMainFrame().getState() != Frame.ICONIFIED) {
|
||||
logger.debug("Draw screen");
|
||||
screen.draw();
|
||||
minimap.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if ((refreshTime - lastFpsTime) >= 1000L) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Shows a offline icon if no messages are received in 120 seconds.
|
||||
if ((refreshTime - lastMessageHandle > 120000L)
|
||||
|| !client.getConnectionState()) {
|
||||
setOffline(true);
|
||||
} else {
|
||||
setOffline(false);
|
||||
}
|
||||
|
||||
logger.debug("Start sleeping");
|
||||
// we know how long we want per screen refresh (40ms) then
|
||||
// we add the refresh time and subtract the current time
|
||||
// leaving us with the amount we still need to sleep.
|
||||
long wait = frameLength + refreshTime - System.currentTimeMillis();
|
||||
|
||||
if (wait > 0) {
|
||||
if (wait > 100L) {
|
||||
logger.info("Waiting " + wait + " ms");
|
||||
wait = 100L;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(wait);
|
||||
} catch (final InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("End sleeping");
|
||||
|
||||
if (!gameRunning) {
|
||||
logger.info("Request logout");
|
||||
try {
|
||||
logger.debug("Move objects");
|
||||
gameObjects.update(delta);
|
||||
|
||||
if (!client.isInBatchUpdate() && gameLayers.changedArea()) {
|
||||
/*
|
||||
* We request server permision to logout. Server can deny
|
||||
* it.
|
||||
* Update the screen
|
||||
*/
|
||||
if (client.logout()) {
|
||||
canExit = true;
|
||||
} else {
|
||||
logger.warn("You can't logout now.");
|
||||
gameRunning = true;
|
||||
screen.setMaxWorldSize(gameLayers.getWidth(),
|
||||
gameLayers.getHeight());
|
||||
screen.center();
|
||||
|
||||
// [Re]create the map
|
||||
|
||||
final CollisionDetection cd = gameLayers.getCollisionDetection();
|
||||
final CollisionDetection pd = gameLayers.getProtectionDetection();
|
||||
|
||||
if (cd != null) {
|
||||
minimap.update(cd, pd,
|
||||
screen.getComponent().getGraphicsConfiguration(),
|
||||
gameLayers.getArea());
|
||||
}
|
||||
gameLayers.resetChangedArea();
|
||||
}
|
||||
|
||||
final User user = User.get();
|
||||
|
||||
if (user != null) {
|
||||
// check if the player object has changed.
|
||||
// Note: this is an exact object reference check
|
||||
if (user != lastuser) {
|
||||
character.setPlayer(user);
|
||||
keyring.setSlot(user, "keyring", gameScreen);
|
||||
inventory.setSlot(user, "bag", gameScreen);
|
||||
|
||||
lastuser = user;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
/*
|
||||
* If we get a timeout exception we accept exit request.
|
||||
*/
|
||||
canExit = true;
|
||||
logger.error(e, e);
|
||||
}
|
||||
|
||||
if (!client.isInBatchUpdate()) {
|
||||
if (mainFrame.getMainFrame().getState() != Frame.ICONIFIED) {
|
||||
logger.debug("Draw screen");
|
||||
screen.draw();
|
||||
minimap.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if ((refreshTime - lastFpsTime) >= 1000L) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Shows a offline icon if no messages are received in 120 seconds.
|
||||
if ((refreshTime - lastMessageHandle > 120000L)
|
||||
|| !client.getConnectionState()) {
|
||||
setOffline(true);
|
||||
} else {
|
||||
setOffline(false);
|
||||
}
|
||||
|
||||
logger.debug("Start sleeping");
|
||||
// we know how long we want per screen refresh (40ms) then
|
||||
// we add the refresh time and subtract the current time
|
||||
// leaving us with the amount we still need to sleep.
|
||||
long wait = frameLength + refreshTime - System.currentTimeMillis();
|
||||
|
||||
if (wait > 0) {
|
||||
if (wait > 100L) {
|
||||
logger.info("Waiting " + wait + " ms");
|
||||
wait = 100L;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(wait);
|
||||
} catch (final InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("End sleeping");
|
||||
|
||||
if (!gameRunning) {
|
||||
logger.info("Request logout");
|
||||
try {
|
||||
/*
|
||||
* We request server permision to logout. Server can deny
|
||||
* it.
|
||||
*/
|
||||
if (client.logout()) {
|
||||
canExit = true;
|
||||
} else {
|
||||
logger.warn("You can't logout now.");
|
||||
gameRunning = true;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
/*
|
||||
* If we get a timeout exception we accept exit request.
|
||||
*/
|
||||
canExit = true;
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1094,7 +1107,7 @@ public class j2DClient implements UserInterface {
|
|||
* so return the divider to the maximum allowed height
|
||||
* by force.
|
||||
*/
|
||||
splitPane.setDividerLocation(stendhal.screenSize.height);
|
||||
splitPane.setDividerLocation(stendhal.screenSize.height + 1);
|
||||
} else {
|
||||
child.setSize(newSize);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import games.stendhal.client.entity.User;
|
|||
import games.stendhal.client.sprite.Sprite;
|
||||
import games.stendhal.client.sprite.SpriteStore;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -41,6 +42,8 @@ class Player2DView extends RPEntity2DView {
|
|||
* Sprite representing grumpy.
|
||||
*/
|
||||
private static Sprite grumpySprite;
|
||||
|
||||
private boolean ignored = false;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -104,6 +107,15 @@ class Player2DView extends RPEntity2DView {
|
|||
return store.getFailsafeOutfit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AlphaComposite getComposite() {
|
||||
// Check for ghostmode to avoid ignored ghostmode admins becoming visible
|
||||
if (User.isIgnoring(entity.getName()) && !((RPEntity) entity).isGhostMode()) {
|
||||
return AlphaComposite.DstOut;
|
||||
}
|
||||
return super.getComposite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine is the user can see this entity while in ghostmode.
|
||||
|
|
@ -152,6 +164,13 @@ class Player2DView extends RPEntity2DView {
|
|||
*/
|
||||
@Override
|
||||
protected void draw(final Graphics2D g2d, final int x, final int y, final int width, final int height) {
|
||||
boolean newIgnoreStatus = User.isIgnoring(entity.getName());
|
||||
if (newIgnoreStatus != ignored) {
|
||||
visibilityChanged = true;
|
||||
ignored = newIgnoreStatus;
|
||||
markChanged();
|
||||
}
|
||||
|
||||
super.draw(g2d, x, y, width, height);
|
||||
|
||||
if (((Player) entity).isAway()) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import games.stendhal.client.gui.wt.core.WtWindowManager;
|
|||
import games.stendhal.client.sound.manager.AudibleArea;
|
||||
import games.stendhal.client.sound.manager.AudibleCircleArea;
|
||||
import games.stendhal.client.sound.manager.SoundFile;
|
||||
import games.stendhal.client.sound.manager.SoundFile.Type;
|
||||
import games.stendhal.client.sound.manager.SoundManager;
|
||||
import games.stendhal.client.sound.system.Time;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
|
|
@ -26,25 +27,34 @@ import org.apache.log4j.Logger;
|
|||
*/
|
||||
public class SoundSystemFacade extends SoundManager implements WorldListener {
|
||||
private static final Logger logger = Logger.getLogger(SoundSystemFacade.class);
|
||||
|
||||
private static final SoundSystemFacade singletonInstance = new SoundSystemFacade();
|
||||
private static final Time fadingTime = new Time(100, Time.Unit.MILLI);
|
||||
private static SoundSystemFacade instance;
|
||||
private final HashMap<String, Sound> sounds = new HashMap<String, Sound>();
|
||||
private final ResourceLocator resourceLocator = ResourceManager.get();
|
||||
private boolean mute = false;
|
||||
|
||||
|
||||
public static SoundSystemFacade get() {
|
||||
return singletonInstance;
|
||||
if (instance == null) {
|
||||
instance = new SoundSystemFacade();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private SoundSystemFacade() {
|
||||
mute = !Boolean.parseBoolean(WtWindowManager.getInstance().getProperty("sound.play", "true"));
|
||||
try {
|
||||
boolean mute = !Boolean.parseBoolean(WtWindowManager.getInstance().getProperty("sound.play", "true"));
|
||||
super.mute(mute, null);
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void playerMoved() {
|
||||
float[] position = Algebra.vecf((float) User.get().getX(), (float) User.get().getY());
|
||||
setHearerPosition(position);
|
||||
update();
|
||||
try {
|
||||
float[] position = Algebra.vecf((float) User.get().getX(), (float) User.get().getY());
|
||||
super.setHearerPosition(position);
|
||||
update();
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void zoneEntered(String zoneName) {
|
||||
|
|
@ -55,91 +65,86 @@ public class SoundSystemFacade extends SoundManager implements WorldListener {
|
|||
// ignored
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
// exits the sound system
|
||||
public void stop(String soundName, Time fadeOutDuration) {
|
||||
try {
|
||||
super.stop(getSound(soundName), fadeOutDuration);
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Sound start(String soundName, double x, double y, int radius, SoundLayer layer, int volume, boolean loop) {
|
||||
if (!mute) {
|
||||
Sound sound = prepareSound(soundName);
|
||||
|
||||
AudibleArea area = new AudibleCircleArea(Algebra.vecf((float) x, (float) y), radius / 2.0f, radius);
|
||||
Time myFadingTime = new Time();
|
||||
|
||||
if (loop) {
|
||||
myFadingTime = fadingTime;
|
||||
}
|
||||
|
||||
play(sound, Numeric.intToFloat(volume, 100.0f), 0, area, loop, myFadingTime);
|
||||
return sound;
|
||||
public Sound getSound(String soundName) {
|
||||
try {
|
||||
return sounds.get(soundName);
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void stop(Sound sound) {
|
||||
stop(sound, fadingTime);
|
||||
public Sound openSound(String fileURI, SoundFile.Type fileType) {
|
||||
try {
|
||||
return super.openSound(resourceLocator.getResource(fileURI), fileType);
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Sound setSound(String soundName, Sound sound) {
|
||||
return sounds.put(soundName, sound);
|
||||
public Sound openSound(String fileURI, SoundFile.Type fileType, int numSamplesPerChunk, boolean enableStreaming) {
|
||||
try {
|
||||
return super.openSound(resourceLocator.getResource(fileURI), fileType, numSamplesPerChunk, enableStreaming);
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Sound getSound(String soundName) {
|
||||
return sounds.get(soundName);
|
||||
}
|
||||
|
||||
private Sound openSound(String fileURI, SoundFile.Type fileType) {
|
||||
return super.openSound(resourceLocator.getResource(fileURI), fileType);
|
||||
}
|
||||
|
||||
public void setMute(boolean mute) {
|
||||
this.mute = mute;
|
||||
public Sound loadSound(String name, String fileURI, SoundFile.Type fileType, boolean enableStreaming) {
|
||||
try {
|
||||
Sound sound = sounds.get(name);
|
||||
|
||||
if(sound == null) {
|
||||
sound = super.openSound(resourceLocator.getResource(fileURI), fileType, 256, enableStreaming);
|
||||
|
||||
if(sound != null) {
|
||||
sounds.put(name, sound);
|
||||
}
|
||||
}
|
||||
return sound;
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void play(final String soundName, final SoundLayer soundLayer, int volume) {
|
||||
AudibleArea area = SoundManager.INFINITE_AUDIBLE_AREA;
|
||||
playNonLoopedSound(soundName, area, soundLayer.ordinal(), volume);
|
||||
try {
|
||||
Sound sound = loadSound(soundName, "audio:/" + soundName + ".ogg", Type.OGG, false);
|
||||
super.play(sound, Numeric.intToFloat(volume, 100.0f), 0, null, false, new Time());
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void play(final String soundName, final double x, final double y, final SoundLayer soundLayer, int volume) {
|
||||
AudibleArea area = new AudibleCircleArea(Algebra.vecf((float) x, (float) y), 3, 18);
|
||||
playNonLoopedSound(soundName, area, soundLayer.ordinal(), volume);
|
||||
try {
|
||||
AudibleArea area = new AudibleCircleArea(Algebra.vecf((float) x, (float) y), 3, 20);
|
||||
Sound sound = loadSound(soundName, "audio:/" + soundName + ".ogg", Type.OGG, false);
|
||||
super.play(sound, Numeric.intToFloat(volume, 100.0f), 0, area, false, new Time());
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void play(final String soundName, final double x, final double y, int radius, final SoundLayer soundLayer, int volume) {
|
||||
AudibleArea area = new AudibleCircleArea(Algebra.vecf((float) x, (float) y), radius / 4.0f, radius);
|
||||
playNonLoopedSound(soundName, area, soundLayer.ordinal(), volume);
|
||||
try {
|
||||
AudibleArea area = new AudibleCircleArea(Algebra.vecf((float) x, (float) y), radius / 4.0f, radius);
|
||||
Sound sound = loadSound(soundName, "audio:/" + soundName + ".ogg", Type.OGG, false);
|
||||
super.play(sound, Numeric.intToFloat(volume, 100.0f), 0, area, false, new Time());
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void playNonLoopedSound(String soundName, AudibleArea area, int soundLayer, int volume) {
|
||||
if (mute) {
|
||||
return;
|
||||
}
|
||||
|
||||
SoundSystemFacade.Sound sound = prepareSound(soundName);
|
||||
play(sound, Numeric.intToFloat(volume, 100.0f), 0, area, false, new Time());
|
||||
}
|
||||
|
||||
public Sound prepareSound(String soundName) {
|
||||
if (soundName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Sound sound = getSound(soundName);
|
||||
|
||||
if (sound == null) {
|
||||
sound = openSound("audio:/" + soundName + ".ogg", SoundFile.Type.OGG);
|
||||
setSound(soundName, sound);
|
||||
}
|
||||
|
||||
// TODO: Sound objects need to be cloned here to set individual properties
|
||||
// But as long as that crashes the client because of OutOfMemoryErrors,
|
||||
// we have to live with only one sound source per file doing the right thingy.
|
||||
/*
|
||||
if (sound != null) {
|
||||
sound = sound.clone();
|
||||
}*/
|
||||
|
||||
return sound;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import games.stendhal.client.sound.system.processors.PCMStreamConverter;
|
|||
import games.stendhal.client.sound.system.SignalProcessor;
|
||||
|
||||
import games.stendhal.common.resource.Resource;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
|
|
@ -28,6 +29,7 @@ public class SoundFile extends SignalProcessor implements Cloneable
|
|||
private int mNumChannels;
|
||||
private int mSampleRate;
|
||||
private SignalProcessor mGenerator = null;
|
||||
private Recorder mRecorder = null;
|
||||
private final Resource mResource;
|
||||
private final boolean mEnableStreaming;
|
||||
private final int mOutputNumSamples;
|
||||
|
|
@ -52,7 +54,10 @@ public class SoundFile extends SignalProcessor implements Cloneable
|
|||
{
|
||||
assert resource != null;
|
||||
assert fileType != null;
|
||||
|
||||
|
||||
if(!resource.exists())
|
||||
throw new IOException("audio resource doesn't exist: " + resource.getURI());
|
||||
|
||||
mResource = resource;
|
||||
mFileType = fileType;
|
||||
mEnableStreaming = enableStreaming;
|
||||
|
|
@ -60,7 +65,7 @@ public class SoundFile extends SignalProcessor implements Cloneable
|
|||
|
||||
SignalProcessor decoder = chooseDecoder(resource, fileType, outputNumSamplesPerChannel);
|
||||
|
||||
if(decoder == null || !resource.exists())
|
||||
if(decoder == null)
|
||||
throw new IOException("could not load audio resource: " + resource.getURI());
|
||||
|
||||
if(enableStreaming)
|
||||
|
|
@ -70,47 +75,81 @@ public class SoundFile extends SignalProcessor implements Cloneable
|
|||
}
|
||||
else
|
||||
{
|
||||
Recorder recorder = new Recorder(outputNumSamplesPerChannel);
|
||||
recorder.connectTo(decoder, false);
|
||||
mRecorder = new Recorder();
|
||||
mRecorder.connectTo(decoder, false);
|
||||
|
||||
while(recorder.request())
|
||||
while(mRecorder.request())
|
||||
/* pass */;
|
||||
|
||||
recorder.disconnect();
|
||||
recorder.trim();
|
||||
recorder.connectTo(mPropagator, true);
|
||||
mGenerator = recorder;
|
||||
mRecorder.disconnect();
|
||||
mRecorder.trim();
|
||||
|
||||
Recorder.Player player = mRecorder.createPlayer(outputNumSamplesPerChannel);
|
||||
player.connectTo(mPropagator, true);
|
||||
|
||||
mGenerator = player;
|
||||
}
|
||||
}
|
||||
|
||||
private SoundFile(Recorder recorder, Resource resource, Type fileType, int outputNumSamplesPerChannel)
|
||||
{
|
||||
mResource = resource;
|
||||
mFileType = fileType;
|
||||
mOutputNumSamples = outputNumSamplesPerChannel;
|
||||
mEnableStreaming = false;
|
||||
mRecorder = recorder;
|
||||
mGenerator = recorder.createPlayer(outputNumSamplesPerChannel);
|
||||
|
||||
mGenerator.connectTo(mPropagator, true);
|
||||
}
|
||||
|
||||
public int getNumChannels() { return mNumChannels; }
|
||||
public int getSampleRate () { return mSampleRate; }
|
||||
|
||||
public void close()
|
||||
{
|
||||
if(mGenerator instanceof Recorder.Player)
|
||||
{
|
||||
mRecorder.clear();
|
||||
}
|
||||
else if(mGenerator instanceof OggVorbisDecoder)
|
||||
{
|
||||
OggVorbisDecoder decoder = (OggVorbisDecoder)mGenerator;
|
||||
decoder.close();
|
||||
}
|
||||
else if(mGenerator instanceof PCMStreamConverter)
|
||||
{
|
||||
PCMStreamConverter converter = (PCMStreamConverter)mGenerator;
|
||||
converter.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoundFile clone()
|
||||
{
|
||||
SoundFile file = null;
|
||||
|
||||
try
|
||||
{
|
||||
file = new SoundFile(mResource, mFileType, mOutputNumSamples, mEnableStreaming);
|
||||
}
|
||||
catch(IOException exception)
|
||||
{
|
||||
assert false: "catched exception - " + exception;
|
||||
}
|
||||
|
||||
if(mGenerator instanceof Recorder.Player)
|
||||
{
|
||||
file = new SoundFile(mRecorder, mResource, mFileType, mOutputNumSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
file = new SoundFile(mResource, mFileType, mOutputNumSamples, mEnableStreaming);
|
||||
}
|
||||
catch(IOException exception)
|
||||
{
|
||||
assert false: "catched exception - " + exception;
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public void restart()
|
||||
{
|
||||
if(mGenerator.getClass() == Recorder.class)
|
||||
{
|
||||
Recorder recorder = (Recorder)mGenerator;
|
||||
recorder.restart();
|
||||
}
|
||||
else if(mGenerator.getClass() == OggVorbisDecoder.class)
|
||||
if(mGenerator instanceof OggVorbisDecoder)
|
||||
{
|
||||
OggVorbisDecoder decoder = (OggVorbisDecoder)mGenerator;
|
||||
decoder.close();
|
||||
|
|
@ -122,7 +161,7 @@ public class SoundFile extends SignalProcessor implements Cloneable
|
|||
catch(IOException exception) { }
|
||||
|
||||
}
|
||||
else if(mGenerator.getClass() == PCMStreamConverter.class)
|
||||
else if(mGenerator instanceof PCMStreamConverter)
|
||||
{
|
||||
PCMStreamConverter converter = (PCMStreamConverter)mGenerator;
|
||||
converter.close();
|
||||
|
|
@ -135,6 +174,11 @@ public class SoundFile extends SignalProcessor implements Cloneable
|
|||
catch(IOException exception) { }
|
||||
catch(UnsupportedAudioFileException exception) { }
|
||||
}
|
||||
else if(mGenerator instanceof Recorder.Player)
|
||||
{
|
||||
Recorder.Player player = (Recorder.Player)mGenerator;
|
||||
player.restart();
|
||||
}
|
||||
}
|
||||
|
||||
private SignalProcessor chooseDecoder(Resource resource, Type fileType, int outputNumSamplesPerChannel)
|
||||
|
|
@ -163,7 +207,7 @@ public class SoundFile extends SignalProcessor implements Cloneable
|
|||
case WAV:
|
||||
try
|
||||
{
|
||||
AudioInputStream stream = AudioSystem.getAudioInputStream(resource.getInputStream());
|
||||
AudioInputStream stream = AudioSystem.getAudioInputStream(new BufferedInputStream(resource.getInputStream()));
|
||||
decoder = new PCMStreamConverter(stream, stream.getFormat(), outputNumSamplesPerChannel);
|
||||
|
||||
mNumChannels = stream.getFormat().getChannels();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ package games.stendhal.client.sound.manager;
|
|||
|
||||
import games.stendhal.client.sound.system.SignalProcessor;
|
||||
import games.stendhal.client.sound.system.SoundSystem;
|
||||
import games.stendhal.client.sound.system.SoundSystemException;
|
||||
import games.stendhal.client.sound.system.Time;
|
||||
import games.stendhal.client.sound.system.processors.DirectedSound;
|
||||
import games.stendhal.client.sound.system.processors.Interruptor;
|
||||
|
|
@ -17,6 +16,7 @@ import games.stendhal.common.math.Algebra;
|
|||
import games.stendhal.common.resource.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
|
@ -31,14 +31,14 @@ import org.apache.log4j.Logger;
|
|||
*/
|
||||
public class SoundManager
|
||||
{
|
||||
private final static Logger logger = Logger.getLogger(SoundManager.class);
|
||||
|
||||
private final static Logger logger = Logger.getLogger(SoundManager.class);
|
||||
private final static int OUTPUT_NUM_SAMPLES = 256;
|
||||
private final static int USE_NUM_MIXER_LINES = 15;
|
||||
private final static int SOUND_CHANNEL_LIMIT = 50;
|
||||
private final static int USE_NUM_MIXER_LINES = 0;
|
||||
private final static int DIMENSION = 2;
|
||||
private final static float[] HEARER_LOOKONG_DIRECTION = { 0.0f, 1.0f };
|
||||
private final static AudioFormat AUDIO_FORMAT = new AudioFormat(44100, 16, 2, true, false);
|
||||
public final static InfiniteAudibleArea INFINITE_AUDIBLE_AREA = new InfiniteAudibleArea();
|
||||
private final static InfiniteAudibleArea INFINITE_AUDIBLE_AREA = new InfiniteAudibleArea();
|
||||
public final static Time ZERO_DURATION = new Time();
|
||||
|
||||
public final static class Sound implements Cloneable
|
||||
|
|
@ -51,13 +51,12 @@ public class SoundManager
|
|||
{
|
||||
Sound sound = new Sound();
|
||||
sound.file.set(file.get().clone());
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
public boolean isActive() { return channel.get() != null && channel.get().isActive(); }
|
||||
}
|
||||
|
||||
|
||||
private final class SoundChannel extends SignalProcessor
|
||||
{
|
||||
final float[] mSoundPosition = new float[DIMENSION];
|
||||
|
|
@ -67,9 +66,9 @@ public class SoundManager
|
|||
final Interruptor mInterruptor = new Interruptor();
|
||||
final DirectedSound mDirectedSound = new DirectedSound();
|
||||
final VolumeAdjustor mGlobalVolume = new VolumeAdjustor();
|
||||
SoundLayers.VolumeAdjustor mLayerVolume = null;
|
||||
SoundSystem.Output mOutput = null;
|
||||
Sound mSound = null;
|
||||
final SoundLayers.VolumeAdjustor mLayerVolume;
|
||||
final SoundSystem.Output mOutput;
|
||||
Sound mSound = null;
|
||||
|
||||
SoundChannel()
|
||||
{
|
||||
|
|
@ -84,10 +83,18 @@ public class SoundManager
|
|||
void setVolume (float volume) { mGlobalVolume.setVolume(volume); }
|
||||
void startFading (float volume, Time duration) { mGlobalVolume.startFading(volume, duration); }
|
||||
void setLayer (int level) { mLayerVolume.setLayer(level); }
|
||||
void setAudibleArea(AudibleArea area) { mAudibleArea.set(area); }
|
||||
void resumePlayback() { mInterruptor.play(); }
|
||||
void close () { mSoundSystem.closeOutput(mOutput); }
|
||||
|
||||
void playSound(Sound newSound, Time time)
|
||||
void setAudibleArea(AudibleArea area)
|
||||
{
|
||||
if(area == null)
|
||||
area = INFINITE_AUDIBLE_AREA;
|
||||
|
||||
mAudibleArea.set(area);
|
||||
}
|
||||
|
||||
void playSound(Sound newSound, float volume, Time time)
|
||||
{
|
||||
if(mSound != null)
|
||||
{
|
||||
|
|
@ -104,7 +111,7 @@ public class SoundManager
|
|||
|
||||
mInterruptor.play();
|
||||
mGlobalVolume.setVolume(0.0f);
|
||||
mGlobalVolume.startFading(1.0f, time);
|
||||
mGlobalVolume.startFading(volume, time);
|
||||
newSound.channel.set(this);
|
||||
newSound.file.get().connectTo(mInterruptor, true);
|
||||
}
|
||||
|
|
@ -115,6 +122,9 @@ public class SoundManager
|
|||
|
||||
void stopPlayback(Time time)
|
||||
{
|
||||
if(time == null)
|
||||
time = ZERO_DURATION;
|
||||
|
||||
mAutoRepeat.set(false);
|
||||
mGlobalVolume.startFading(0.0f, time);
|
||||
mInterruptor.stop(time);
|
||||
|
|
@ -137,7 +147,7 @@ public class SoundManager
|
|||
}
|
||||
else
|
||||
{
|
||||
playSound(null, null);
|
||||
playSound(null, 0, null);
|
||||
super.quit();
|
||||
}
|
||||
}
|
||||
|
|
@ -146,44 +156,40 @@ public class SoundManager
|
|||
private final LinkedList<SoundChannel> mChannels = new LinkedList<SoundChannel>();
|
||||
private final float[] mHearerPosition = new float[DIMENSION];
|
||||
private final SoundLayers mSoundLayers = new SoundLayers();
|
||||
private SoundSystem mSoundSystem = null;
|
||||
private boolean mMute = false;
|
||||
private SoundSystem mSoundSystem;
|
||||
|
||||
protected SoundManager()
|
||||
{
|
||||
Algebra.mov_Vecf(mHearerPosition, 0.0f);
|
||||
protected SoundManager()
|
||||
{
|
||||
try {
|
||||
Algebra.mov_Vecf(mHearerPosition, 0.0f);
|
||||
|
||||
try
|
||||
{
|
||||
mSoundSystem = new SoundSystem(AUDIO_FORMAT, new Time(70, Time.Unit.MILLI), USE_NUM_MIXER_LINES);
|
||||
mSoundSystem.setDaemon(true);
|
||||
mSoundSystem.start();
|
||||
}
|
||||
catch(SoundSystemException exception)
|
||||
{
|
||||
mSoundSystem = null;
|
||||
// TODO: write error message into logger
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSoundSystemRunnig()
|
||||
{
|
||||
return mSoundSystem != null;
|
||||
}
|
||||
mSoundSystem = new SoundSystem(AUDIO_FORMAT, new Time(15, Time.Unit.MILLI), USE_NUM_MIXER_LINES);
|
||||
mSoundSystem.setDaemon(true);
|
||||
mSoundSystem.start();
|
||||
} catch (RuntimeException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Sound openSound(Resource resource, SoundFile.Type fileType)
|
||||
{
|
||||
return openSound(resource, fileType, OUTPUT_NUM_SAMPLES, true);
|
||||
}
|
||||
|
||||
public Sound openSound(Resource resource, SoundFile.Type fileType, int numSamplesPerChunk, boolean enableStreaming)
|
||||
{
|
||||
Sound sound = null;
|
||||
|
||||
try
|
||||
{
|
||||
SoundFile file = new SoundFile(resource, fileType, OUTPUT_NUM_SAMPLES, true);
|
||||
|
||||
SoundFile file = new SoundFile(resource, fileType, numSamplesPerChunk, enableStreaming);
|
||||
sound = new Sound();
|
||||
sound.file.set(file);
|
||||
}
|
||||
catch(IOException exception)
|
||||
{
|
||||
assert false: exception;
|
||||
logger.warn(exception);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +212,7 @@ public class SoundManager
|
|||
|
||||
public void play(Sound sound, float volume, int layerLevel, AudibleArea area, boolean autoRepeat, Time fadeInDuration)
|
||||
{
|
||||
if(sound == null)
|
||||
if(sound == null || mMute)
|
||||
return;
|
||||
|
||||
if(sound.isActive())
|
||||
|
|
@ -222,13 +228,14 @@ public class SoundManager
|
|||
}
|
||||
else
|
||||
{
|
||||
SoundChannel channel = findInactiveChannel();
|
||||
SoundChannel channel = getInactiveChannel();
|
||||
channel.setAutoRepeat(autoRepeat);
|
||||
channel.setVolume(volume);
|
||||
channel.setLayer(layerLevel);
|
||||
channel.setAudibleArea(area);
|
||||
channel.playSound(sound, fadeInDuration);
|
||||
channel.playSound(sound, volume, fadeInDuration);
|
||||
channel.update();
|
||||
|
||||
closeInactiveChannels(SOUND_CHANNEL_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -255,22 +262,63 @@ public class SoundManager
|
|||
if(sound != null && sound.isActive())
|
||||
sound.channel.get().setAudibleArea(area);
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
if(isSoundSystemRunnig())
|
||||
{
|
||||
mSoundSystem.close();
|
||||
|
||||
try
|
||||
{
|
||||
mSoundSystem.join();
|
||||
}
|
||||
catch(InterruptedException exception) { }
|
||||
}
|
||||
public void mute(boolean turnOffSound, Time turnOffDuration)
|
||||
{
|
||||
if(turnOffSound && !mMute)
|
||||
{
|
||||
logger.info("turning off audio");
|
||||
|
||||
for(SoundChannel channel: mChannels)
|
||||
channel.stopPlayback(turnOffDuration);
|
||||
}
|
||||
|
||||
if(!turnOffSound && mMute)
|
||||
logger.info("turning on audio");
|
||||
|
||||
mMute = turnOffSound;
|
||||
}
|
||||
|
||||
public void exit()
|
||||
{
|
||||
mSoundSystem.exit();
|
||||
|
||||
try
|
||||
{
|
||||
mSoundSystem.join();
|
||||
}
|
||||
catch(InterruptedException exception)
|
||||
{
|
||||
logger.warn("joining sound system thread was interrupted: " + exception);
|
||||
}
|
||||
}
|
||||
|
||||
private SoundChannel findInactiveChannel()
|
||||
private void closeInactiveChannels(int leaveNumChannelsOpen)
|
||||
{
|
||||
int numChannels = mChannels.size();
|
||||
Iterator<SoundChannel> iChannel = mChannels.iterator();
|
||||
|
||||
while(iChannel.hasNext())
|
||||
{
|
||||
if(mChannels.size() <= leaveNumChannelsOpen)
|
||||
break;
|
||||
|
||||
SoundChannel currChannel = iChannel.next();
|
||||
|
||||
if(!currChannel.isActive())
|
||||
{
|
||||
currChannel.close();
|
||||
iChannel.remove();
|
||||
}
|
||||
}
|
||||
|
||||
numChannels -= mChannels.size();
|
||||
|
||||
if(numChannels > 0)
|
||||
logger.debug("close " + numChannels + " inactive sound channels");
|
||||
}
|
||||
|
||||
private SoundChannel getInactiveChannel()
|
||||
{
|
||||
SoundChannel foundChannel = null;
|
||||
|
||||
|
|
@ -285,8 +333,10 @@ public class SoundManager
|
|||
|
||||
if(foundChannel == null)
|
||||
{
|
||||
mChannels.add(new SoundChannel());
|
||||
foundChannel = mChannels.getLast();
|
||||
foundChannel = new SoundChannel();
|
||||
mChannels.add(foundChannel);
|
||||
|
||||
logger.debug("open new sound channel (number " + mChannels.size() + ")");
|
||||
}
|
||||
|
||||
return foundChannel;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
package games.stendhal.client.sound.system;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import games.stendhal.common.memory.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
|
|
@ -20,7 +20,6 @@ import javax.sound.sampled.Line;
|
|||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.Mixer;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
|
|
@ -29,137 +28,279 @@ import org.apache.log4j.Logger;
|
|||
*/
|
||||
public class SoundSystem extends Thread
|
||||
{
|
||||
public final static class Output extends SignalProcessor
|
||||
{
|
||||
private SourceDataLine mLine = null; // the line we will write the PCM data to
|
||||
private AudioFormat mAudioFormat = null; // the audio format the line was opened with
|
||||
private float[] mUniformPCM = null; // the interleaved uniform PCM data. every samples value ranges form -1.0 to 1.0
|
||||
private byte[] mPCM = null; // the uniform PCM data converted to the format defined in "mAudioFormat"
|
||||
private int mNumBytesWritten = 0; // number of bytes of "mPCM" we have written to "mLine"
|
||||
private int mNumSamplesMixed = 0; // number of samples we have mixed into the line of "mMixedOutput"
|
||||
private int mNumChannels = 0;
|
||||
private int mNumSamples = 0;
|
||||
private int mSampleRate = 0;
|
||||
private boolean mPCMWasConverted = false;
|
||||
public static abstract class Output extends SignalProcessor { }
|
||||
|
||||
private int mRemainingBytesToWritePerCycle = 0;
|
||||
private static class DummyOutput extends Output { }
|
||||
|
||||
private int getRemainingBytesToWrite() { return mPCM.length - mNumBytesWritten; }
|
||||
private int getRemainingSamplesToMix() { return mNumSamples - mNumSamplesMixed; }
|
||||
private boolean receivedData () { return mUniformPCM != null; }
|
||||
private boolean wasPCMConverted () { return mPCMWasConverted; }
|
||||
private boolean isLineUsable () { return mLine != null; }
|
||||
private boolean wasAllDataWritten () { return mNumBytesWritten >= mPCM.length; }
|
||||
private boolean whereAllSamplesMixed () { return mNumSamplesMixed >= mNumSamples; }
|
||||
private static class SystemOutput extends Output
|
||||
{
|
||||
final SourceDataLine mLine; // the line we will write the PCM data to
|
||||
float[] mAudioBuffer = null; // the interleaved uniform PCM data
|
||||
int mNumSamples = 0; //
|
||||
byte[] mPCMBuffer = null; // the uniform PCM data converted to the format defined in "mAudioFormat"
|
||||
int mPCMBufferSize = 0; //
|
||||
int mNumBytesWritten = 0; // number of bytes from "mPCMBuffer" we have written to "mLine"
|
||||
int mNumBytesToWrite = 0;
|
||||
|
||||
private int writeToLine(int numBytes)
|
||||
SystemOutput(SourceDataLine line)
|
||||
{
|
||||
assert line != null;
|
||||
assert line.isOpen();
|
||||
mLine = line;
|
||||
mLine.start();
|
||||
}
|
||||
|
||||
boolean isOpen () { return mLine.isOpen(); }
|
||||
boolean receivedData () { return mNumSamples > 0; }
|
||||
boolean isConverted () { return mPCMBufferSize > 0; }
|
||||
AudioFormat getAudioFormat () { return mLine.getFormat(); }
|
||||
float[] getBuffer () { return mAudioBuffer; }
|
||||
int getNumSamples () { return mNumSamples; }
|
||||
int getNumChannels () { return mLine.getFormat().getChannels(); }
|
||||
int getSampleRate () { return (int)mLine.getFormat().getSampleRate(); }
|
||||
int getNumBytesPerSample() { return mLine.getFormat().getSampleSizeInBits() / 8; }
|
||||
int available () { return mLine.available(); }
|
||||
int getNumBytesToWrite () { return mNumBytesToWrite; }
|
||||
|
||||
void close()
|
||||
{
|
||||
mLine.close();
|
||||
mAudioBuffer = null;
|
||||
mPCMBuffer = null;
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
mNumBytesWritten = 0;
|
||||
mNumSamples = 0;
|
||||
mPCMBufferSize = 0;
|
||||
}
|
||||
|
||||
void setBuffer(float[] buffer, int numSamples)
|
||||
{
|
||||
assert numSamples <= buffer.length;
|
||||
assert (numSamples % getNumChannels()) == 0;
|
||||
|
||||
mAudioBuffer = buffer;
|
||||
mNumSamples = numSamples;
|
||||
}
|
||||
|
||||
void setNumBytesToWrite(int numBytesToWrite)
|
||||
{
|
||||
int frameSize = getNumBytesPerSample() * getNumChannels();
|
||||
|
||||
mNumBytesToWrite = numBytesToWrite;
|
||||
mNumBytesToWrite /= frameSize;
|
||||
mNumBytesToWrite *= frameSize;
|
||||
}
|
||||
|
||||
void convert()
|
||||
{
|
||||
assert mAudioFormat.getChannels() == mNumChannels;
|
||||
assert (int)mAudioFormat.getSampleRate() == mSampleRate;
|
||||
assert (mLine.getFormat().getSampleSizeInBits() % 8) == 0;
|
||||
|
||||
int written = mLine.write(mPCM, mNumBytesWritten, numBytes);
|
||||
mNumBytesWritten += written;
|
||||
return written;
|
||||
int numBytesPerSample = getNumBytesPerSample();
|
||||
int numBytes = numBytesPerSample * mNumSamples;
|
||||
|
||||
mPCMBuffer = Field.expand(mPCMBuffer, numBytes, false);
|
||||
mPCMBuffer = SoundSystem.convertUniformPCM(mPCMBuffer, mAudioBuffer, mNumSamples, numBytesPerSample);
|
||||
mPCMBufferSize = numBytes;
|
||||
}
|
||||
|
||||
private int mixToOutput(Output output, int offsetInSamples, int numSamplesToMix)
|
||||
boolean write(int numBytes)
|
||||
{
|
||||
assert output.mAudioFormat.getChannels() == mNumChannels;
|
||||
assert (int)output.mAudioFormat.getSampleRate() == mSampleRate;
|
||||
int frameSize = getNumBytesPerSample() * getNumChannels();
|
||||
int numRemainingBytesInBuffer = mPCMBufferSize - mNumBytesWritten;
|
||||
|
||||
SoundSystem.mixUniformPCM(output.mUniformPCM, offsetInSamples*mNumChannels, mUniformPCM, mNumSamplesMixed*mNumChannels, numSamplesToMix*mNumChannels);
|
||||
mNumSamplesMixed += numSamplesToMix;
|
||||
return numSamplesToMix;
|
||||
numBytes = Math.min(numBytes, numRemainingBytesInBuffer);
|
||||
numBytes = Math.min(numBytes, mNumBytesToWrite );
|
||||
numBytes = Math.min(numBytes, mLine.available() );
|
||||
numBytes /= frameSize;
|
||||
numBytes *= frameSize;
|
||||
numBytes = mLine.write(mPCMBuffer, mNumBytesWritten, numBytes);
|
||||
|
||||
mNumBytesWritten += numBytes;
|
||||
mNumBytesToWrite -= numBytes;
|
||||
numRemainingBytesInBuffer -= numBytes;
|
||||
|
||||
if(numRemainingBytesInBuffer < frameSize)
|
||||
reset();
|
||||
|
||||
if(mNumBytesToWrite < frameSize)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void convertPCM()
|
||||
@Override
|
||||
protected void modify(float[] buffer, int frames, int channels, int rate)
|
||||
{
|
||||
assert (mAudioFormat.getSampleSizeInBits() % 8) == 0;
|
||||
|
||||
int numBytesPerSample = mAudioFormat.getSampleSizeInBits() / 8;
|
||||
mPCM = SoundSystem.convertUniformPCM(mPCM, mUniformPCM, mNumSamples * mNumChannels, numBytesPerSample);
|
||||
mPCMWasConverted = true;
|
||||
}
|
||||
|
||||
private void clearData()
|
||||
{
|
||||
mNumBytesWritten = 0;
|
||||
mNumSamplesMixed = 0;
|
||||
mUniformPCM = null;
|
||||
mPCMWasConverted = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modify(float[] buffer, int samples, int channels, int rate)
|
||||
{
|
||||
if(buffer != null && samples > 0 && channels > 0 && rate > 0)
|
||||
if(buffer != null && frames > 0 && channels > 0 && rate > 0)
|
||||
{
|
||||
assert samples <= buffer.length;
|
||||
assert (frames * channels) <= buffer.length;
|
||||
buffer = SoundSystem.convertChannels(buffer, frames, channels, getNumChannels());
|
||||
|
||||
mUniformPCM = buffer;
|
||||
mNumChannels = channels;
|
||||
mNumSamples = samples;
|
||||
mSampleRate = rate;
|
||||
|
||||
if(!SoundSystem.convertChannels(this))
|
||||
if(buffer != null)
|
||||
{
|
||||
assert false: "could not convert channels";
|
||||
|
||||
mUniformPCM = null;
|
||||
mNumChannels = 0;
|
||||
mNumSamples = 0;
|
||||
mSampleRate = 0;
|
||||
setBuffer(buffer, (frames * getNumChannels()));
|
||||
}
|
||||
else
|
||||
{
|
||||
setBuffer(null, 0);
|
||||
//assert false: "could not convert channels";
|
||||
}
|
||||
|
||||
if(!SoundSystem.convertSampleRate(this))
|
||||
{
|
||||
assert false: "could not convert sample rate";
|
||||
buffer = SoundSystem.convertSampleRate(buffer, (frames * channels), channels, rate, getSampleRate());
|
||||
|
||||
mUniformPCM = null;
|
||||
mNumChannels = 0;
|
||||
mNumSamples = 0;
|
||||
mSampleRate = 0;
|
||||
}
|
||||
if(buffer != null)
|
||||
{
|
||||
float ratio = (float)frames / (float)rate;
|
||||
setBuffer(buffer, (int)(ratio * getSampleRate() * channels));
|
||||
}
|
||||
else
|
||||
{
|
||||
setBuffer(null, 0);
|
||||
//assert false: "could not convert sample rate";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final static Logger logger = Logger.getLogger(SoundSystem.class);
|
||||
private final ArrayList<Output> mOutputs = new ArrayList<Output>();
|
||||
private final Output mMixOutput = new Output();
|
||||
private Mixer mMixer = null;
|
||||
private Time mBufferDuration = null;
|
||||
private final AtomicBoolean mSystemIsRunning = new AtomicBoolean(false);
|
||||
private final AtomicBoolean mUseDynamicLoadScaling = new AtomicBoolean(false);
|
||||
private final int mMaxNumLines;
|
||||
private static class MixerOutput extends Output
|
||||
{
|
||||
AudioFormat mAudioFormat;
|
||||
float[] mAudioBuffer = null; // the interleaved uniform PCM data
|
||||
int mNumSamples = 0; //
|
||||
int mNumSamplesMixed = 0;
|
||||
|
||||
MixerOutput(AudioFormat format)
|
||||
{
|
||||
assert format != null;
|
||||
mAudioFormat = format;
|
||||
}
|
||||
|
||||
boolean receivedData () { return mNumSamples > 0; }
|
||||
int getNumChannels() { return mAudioFormat.getChannels(); }
|
||||
int getSampleRate () { return (int)mAudioFormat.getSampleRate(); }
|
||||
|
||||
void reset()
|
||||
{
|
||||
mNumSamples = 0;
|
||||
mNumSamplesMixed = 0;
|
||||
}
|
||||
|
||||
void setBuffer(float[] buffer, int numSamples)
|
||||
{
|
||||
assert numSamples <= buffer.length;
|
||||
assert (numSamples % getNumChannels()) == 0;
|
||||
|
||||
mAudioBuffer = buffer;
|
||||
mNumSamples = numSamples;
|
||||
}
|
||||
|
||||
boolean mix(float[] buffer, int size)
|
||||
{
|
||||
int offset = 0;
|
||||
int numSamplesToMix = size;
|
||||
|
||||
while(numSamplesToMix > 0)
|
||||
{
|
||||
if(!receivedData())
|
||||
request();
|
||||
|
||||
if(!receivedData())
|
||||
return false;
|
||||
|
||||
int numSamples = mNumSamples - mNumSamplesMixed;
|
||||
numSamples = Math.min(numSamples, numSamplesToMix);
|
||||
|
||||
SoundSystem.mixUniformPCM(buffer, offset, mAudioBuffer, mNumSamplesMixed, numSamples);
|
||||
|
||||
offset += numSamples;
|
||||
mNumSamplesMixed += numSamples;
|
||||
numSamplesToMix -= numSamples;
|
||||
|
||||
if(mNumSamples == mNumSamplesMixed)
|
||||
reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modify(float[] buffer, int frames, int channels, int rate)
|
||||
{
|
||||
if(buffer != null && frames > 0 && channels > 0 && rate > 0)
|
||||
{
|
||||
assert (frames * channels) <= buffer.length;
|
||||
buffer = SoundSystem.convertChannels(buffer, frames, channels, getNumChannels());
|
||||
|
||||
if(buffer != null)
|
||||
{
|
||||
setBuffer(buffer, (frames * getNumChannels()));
|
||||
}
|
||||
else
|
||||
{
|
||||
setBuffer(null, 0);
|
||||
//assert false: "could not convert channels";
|
||||
}
|
||||
|
||||
buffer = SoundSystem.convertSampleRate(buffer, (frames * channels), channels, rate, getSampleRate());
|
||||
|
||||
if(buffer != null)
|
||||
{
|
||||
float ratio = (float)frames / (float)rate;
|
||||
setBuffer(buffer, (int)(ratio * getSampleRate() * channels));
|
||||
}
|
||||
else
|
||||
{
|
||||
setBuffer(null, 0);
|
||||
//assert false: "could not convert sample rate";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final static Logger logger = Logger.getLogger(SoundSystem.class);
|
||||
private final LinkedList<SystemOutput> mSystemOutputs = new LinkedList<SystemOutput>();
|
||||
private final LinkedList<MixerOutput> mMixerOutputs = new LinkedList<MixerOutput>();
|
||||
private SystemOutput mMixSystemOutput = null;
|
||||
private Mixer mSystemMixer = null;
|
||||
private Time mBufferDuration = null;
|
||||
private final AtomicBoolean mSystemIsRunning = new AtomicBoolean(false);
|
||||
private final AtomicBoolean mUseDynamicLoadScaling = new AtomicBoolean(false);
|
||||
float[] mMixBuffer = null;
|
||||
private final int mMaxNumLines;
|
||||
|
||||
public SoundSystem(AudioFormat audioFormat, Time bufferDuration, int useMaxMixerLines) throws SoundSystemException
|
||||
public SoundSystem(AudioFormat audioFormat, Time bufferDuration, int useMaxMixerLines)
|
||||
{
|
||||
assert audioFormat != null;
|
||||
assert bufferDuration != null;
|
||||
if(audioFormat == null)
|
||||
throw new IllegalArgumentException("audioFormat argument must not be null");
|
||||
if(bufferDuration == null)
|
||||
throw new IllegalArgumentException("bufferDuration argument must not be null");
|
||||
|
||||
mMixer = tryToFindMixer(audioFormat);
|
||||
logger.info("opening sound system and trying to find optimal system mixer device / output line");
|
||||
|
||||
mSystemMixer = tryToFindMixer(audioFormat);
|
||||
mBufferDuration = bufferDuration;
|
||||
mMaxNumLines = useMaxMixerLines;
|
||||
|
||||
if(mMixer == null)
|
||||
if(mSystemMixer == null)
|
||||
{
|
||||
logger.warn("cannot find a system mixer device. manual mixing will be the only option");
|
||||
|
||||
DataLine.Info datalineInfo = new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);
|
||||
|
||||
try
|
||||
{
|
||||
mMixOutput.mLine = (SourceDataLine)AudioSystem.getLine(datalineInfo);
|
||||
|
||||
if(!AudioSystem.isLineSupported(datalineInfo))
|
||||
throw new SoundSystemException("line is not supported - " + audioFormat.toString());
|
||||
|
||||
mMixOutput.mAudioFormat = audioFormat;
|
||||
mMixOutput.mLine.open();
|
||||
SourceDataLine line = (SourceDataLine)AudioSystem.getLine(datalineInfo);
|
||||
line.open();
|
||||
|
||||
mMixSystemOutput = new SystemOutput(line);
|
||||
}
|
||||
catch(LineUnavailableException exception)
|
||||
catch(LineUnavailableException e)
|
||||
{
|
||||
mMixOutput.mLine.close();
|
||||
throw new SoundSystemException("line is unavailable - " + exception.toString());
|
||||
logger.warn("cannot open output line for manual mixing of audio data: \"" + e + "\"");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -168,26 +309,30 @@ public class SoundSystem extends Thread
|
|||
|
||||
try
|
||||
{
|
||||
mMixOutput.mLine = (SourceDataLine)mMixer.getLine(info);
|
||||
mMixOutput.mAudioFormat = audioFormat;
|
||||
mMixOutput.mLine.open();
|
||||
SourceDataLine line = (SourceDataLine)mSystemMixer.getLine(info);
|
||||
line.open();
|
||||
|
||||
mMixSystemOutput = new SystemOutput(line);
|
||||
}
|
||||
catch(LineUnavailableException e)
|
||||
{
|
||||
mMixOutput.mLine.close();
|
||||
mMixOutput.mLine = null;
|
||||
mMixOutput.mAudioFormat = null;
|
||||
}
|
||||
{
|
||||
logger.warn("cannot open output line for manual mixing of audio data: \"" + e + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SoundSystem(Mixer mixer, AudioFormat audioFormat, Time bufferDuration, int useMaxMixerLines)
|
||||
{
|
||||
assert audioFormat != null;
|
||||
assert mixer != null;
|
||||
assert bufferDuration != null;
|
||||
if(mixer == null)
|
||||
throw new IllegalArgumentException("mixer argument must not be null");
|
||||
if(audioFormat == null)
|
||||
throw new IllegalArgumentException("audioFormat argument must not be null");
|
||||
if(bufferDuration == null)
|
||||
throw new IllegalArgumentException("bufferDuration argument must not be null");
|
||||
|
||||
mMixer = mixer;
|
||||
logger.info("opening sound system using specified system mixer device");
|
||||
|
||||
mSystemMixer = mixer;
|
||||
mBufferDuration = bufferDuration;
|
||||
mMaxNumLines = useMaxMixerLines;
|
||||
|
||||
|
|
@ -195,23 +340,29 @@ public class SoundSystem extends Thread
|
|||
|
||||
try
|
||||
{
|
||||
mMixOutput.mLine = (SourceDataLine)mMixer.getLine(info);
|
||||
mMixOutput.mAudioFormat = audioFormat;
|
||||
mMixOutput.mLine.open();
|
||||
SourceDataLine line = (SourceDataLine)mSystemMixer.getLine(info);
|
||||
line.open();
|
||||
|
||||
mMixSystemOutput = new SystemOutput(line);
|
||||
}
|
||||
catch(LineUnavailableException e)
|
||||
{
|
||||
mMixOutput.mLine.close();
|
||||
mMixOutput.mLine = null;
|
||||
mMixOutput.mAudioFormat = null;
|
||||
logger.warn("cannot open output line for manual mixing of audio data: \"" + e + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
public SoundSystem(SourceDataLine outputLine, Time bufferDuration) throws SoundSystemException
|
||||
public SoundSystem(SourceDataLine outputLine, Time bufferDuration)
|
||||
{
|
||||
assert outputLine != null;
|
||||
assert bufferDuration != null;
|
||||
if(outputLine == null)
|
||||
throw new IllegalArgumentException("outputLine argument must not be null");
|
||||
if(bufferDuration == null)
|
||||
throw new IllegalArgumentException("bufferDuration argument must not be null");
|
||||
|
||||
logger.info("opening sound system");
|
||||
|
||||
mMaxNumLines = 0;
|
||||
mBufferDuration = bufferDuration;
|
||||
|
||||
if(!outputLine.isOpen())
|
||||
{
|
||||
try
|
||||
|
|
@ -220,97 +371,97 @@ public class SoundSystem extends Thread
|
|||
}
|
||||
catch(LineUnavailableException e)
|
||||
{
|
||||
throw new SoundSystemException(e.toString());
|
||||
logger.warn("cannot open output line for manual mixing of audio data: \"" + e + "\"");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mMaxNumLines = 0;
|
||||
mMixOutput.mLine = outputLine;
|
||||
mMixOutput.mAudioFormat = outputLine.getFormat();
|
||||
mMixOutput.mNumChannels = mMixOutput.mAudioFormat.getChannels();
|
||||
mMixOutput.mSampleRate = (int)mMixOutput.mAudioFormat.getSampleRate();
|
||||
mBufferDuration = bufferDuration;
|
||||
mMixSystemOutput = new SystemOutput(outputLine);
|
||||
}
|
||||
|
||||
public Output openOutput(AudioFormat audioFormat)
|
||||
public Output openOutput(AudioFormat audioFormat)
|
||||
{
|
||||
Output output = new Output();
|
||||
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);
|
||||
boolean lineUnavailable = false;
|
||||
|
||||
if(mMixer != null && mMixer.isLineSupported(info) && mOutputs.size() < mMaxNumLines)
|
||||
if(audioFormat == null)
|
||||
throw new IllegalArgumentException("audioFormat argument must not be null");
|
||||
|
||||
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);
|
||||
|
||||
if(mSystemMixer != null && mSystemMixer.isLineSupported(info) && mSystemOutputs.size() < mMaxNumLines)
|
||||
{
|
||||
try
|
||||
{
|
||||
output.mLine = (SourceDataLine)mMixer.getLine(info);
|
||||
output.mAudioFormat = audioFormat;
|
||||
output.mLine.open();
|
||||
SourceDataLine line = (SourceDataLine)mSystemMixer.getLine(info);
|
||||
line.open();
|
||||
|
||||
SystemOutput output = new SystemOutput(line);
|
||||
|
||||
synchronized(mSystemOutputs)
|
||||
{
|
||||
mSystemOutputs.add(output);
|
||||
}
|
||||
|
||||
logger.debug("opening a system output (using a system mixer device)");
|
||||
return output;
|
||||
}
|
||||
catch(LineUnavailableException e)
|
||||
{
|
||||
output.mLine.close();
|
||||
output.mLine = null;
|
||||
output.mAudioFormat = null;
|
||||
lineUnavailable = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lineUnavailable = true;
|
||||
catch(LineUnavailableException e) { }
|
||||
}
|
||||
|
||||
if(lineUnavailable && mMixOutput.isLineUsable())
|
||||
output.mAudioFormat = mMixOutput.mAudioFormat;
|
||||
if(mMixSystemOutput != null)
|
||||
{
|
||||
MixerOutput output = new MixerOutput(mMixSystemOutput.getAudioFormat());
|
||||
|
||||
synchronized(this)
|
||||
{
|
||||
mOutputs.add(output);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
synchronized(mMixerOutputs)
|
||||
{
|
||||
mMixerOutputs.add(output);
|
||||
}
|
||||
|
||||
logger.debug("opening a mixer output (using manual mixing)");
|
||||
return output;
|
||||
}
|
||||
|
||||
logger.debug("opening a dummy output (no output line could be received)");
|
||||
return new DummyOutput();
|
||||
}
|
||||
|
||||
public synchronized void closeOutput(Output output)
|
||||
{
|
||||
if(output.isLineUsable())
|
||||
{
|
||||
output.mLine.close();
|
||||
output.mLine = null;
|
||||
}
|
||||
if(output != null)
|
||||
{
|
||||
output.disconnect();
|
||||
|
||||
output.clearData();
|
||||
output.disconnect();
|
||||
if(output instanceof SystemOutput)
|
||||
{
|
||||
logger.debug("closing a system output");
|
||||
|
||||
mOutputs.remove(output);
|
||||
SystemOutput systemOutput = (SystemOutput)output;
|
||||
systemOutput.close();
|
||||
mSystemOutputs.remove(systemOutput);
|
||||
}
|
||||
|
||||
if(output instanceof MixerOutput)
|
||||
{
|
||||
logger.debug("closing a mixer output");
|
||||
|
||||
MixerOutput mixerOutput = (MixerOutput)output;
|
||||
mMixerOutputs.remove(mixerOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void closeAllOutputs()
|
||||
{
|
||||
for(Output output: mOutputs)
|
||||
{
|
||||
if(output.isLineUsable())
|
||||
{
|
||||
output.mLine.close();
|
||||
output.mLine = null;
|
||||
}
|
||||
logger.debug("closing all outputs excluding the output for manual mixing");
|
||||
|
||||
for(SystemOutput output: mSystemOutputs)
|
||||
output.close();
|
||||
|
||||
output.clearData();
|
||||
output.disconnect();
|
||||
}
|
||||
|
||||
mOutputs.clear();
|
||||
|
||||
if(mMixOutput.isLineUsable())
|
||||
{
|
||||
mMixOutput.mLine.stop();
|
||||
mMixOutput.mLine.close();
|
||||
mMixOutput.mLine = null;
|
||||
mMixOutput.clearData();
|
||||
}
|
||||
mSystemOutputs.clear();
|
||||
mMixerOutputs.clear();
|
||||
}
|
||||
|
||||
public void close()
|
||||
public void exit()
|
||||
{
|
||||
logger.info("stopping sound system");
|
||||
mSystemIsRunning.set(false);
|
||||
}
|
||||
|
||||
|
|
@ -320,21 +471,14 @@ public class SoundSystem extends Thread
|
|||
mSystemIsRunning.set(true);
|
||||
|
||||
double averageTimeToProcessSound = mBufferDuration.getInNanoSeconds();
|
||||
double multiplicator = 0.995;
|
||||
double multiplicator = 0.990;
|
||||
|
||||
while(mSystemIsRunning.get())
|
||||
{
|
||||
long duration = System.nanoTime();
|
||||
|
||||
try
|
||||
{
|
||||
processOutputs();
|
||||
}
|
||||
catch(RuntimeException e)
|
||||
{
|
||||
logger.warn(e, e);
|
||||
}
|
||||
|
||||
processOutputs();
|
||||
|
||||
duration = System.nanoTime() - duration;
|
||||
averageTimeToProcessSound = (averageTimeToProcessSound + duration) / 2.0;
|
||||
|
||||
|
|
@ -349,134 +493,82 @@ public class SoundSystem extends Thread
|
|||
}
|
||||
|
||||
closeAllOutputs();
|
||||
closeOutput(mMixSystemOutput);
|
||||
}
|
||||
|
||||
private int min(int a, int b, int c)
|
||||
{
|
||||
return Math.min(a, Math.min(b,c));
|
||||
}
|
||||
|
||||
private void prepareManualMixing()
|
||||
{
|
||||
int numBytesPerSample = mMixOutput.mAudioFormat.getSampleSizeInBits() / 8;
|
||||
int numChannels = mMixOutput.mAudioFormat.getChannels();
|
||||
int sampleRate = (int)mMixOutput.mAudioFormat.getSampleRate();
|
||||
int numSamples = (int)mBufferDuration.getInSamples(sampleRate);
|
||||
@SuppressWarnings("unchecked")
|
||||
private void processOutputs()
|
||||
{
|
||||
LinkedList<SystemOutput> sysOutputs;
|
||||
LinkedList<MixerOutput> mixOutputs;
|
||||
|
||||
if(mMixOutput.mUniformPCM == null || mMixOutput.mUniformPCM.length < (numSamples * numChannels))
|
||||
mMixOutput.mUniformPCM = new float[numSamples * numChannels];
|
||||
synchronized(mSystemOutputs)
|
||||
{
|
||||
sysOutputs = (LinkedList<SystemOutput>)mSystemOutputs.clone();
|
||||
}
|
||||
|
||||
mMixOutput.mNumSamples = numSamples;
|
||||
mMixOutput.mRemainingBytesToWritePerCycle = numSamples * numChannels * numBytesPerSample;
|
||||
Arrays.fill(mMixOutput.mUniformPCM, 0, numSamples, 0.0f);
|
||||
}
|
||||
|
||||
private void processOutputs()
|
||||
{
|
||||
LinkedList<Output> outputsWithUsableLines = new LinkedList<Output>();
|
||||
synchronized(mMixerOutputs)
|
||||
{
|
||||
mixOutputs = (LinkedList<MixerOutput>)mMixerOutputs.clone();
|
||||
}
|
||||
|
||||
if(mMixOutput.isLineUsable())
|
||||
{
|
||||
prepareManualMixing();
|
||||
outputsWithUsableLines.add(mMixOutput);
|
||||
}
|
||||
if(mMixSystemOutput != null)
|
||||
sysOutputs.add(mMixSystemOutput);
|
||||
|
||||
synchronized(this)
|
||||
{
|
||||
for(Output output: mOutputs)
|
||||
{
|
||||
/* happens for example if the audio device is in use by an other applicatin
|
||||
if(output.mAudioFormat == null)
|
||||
continue; //*/
|
||||
for(SystemOutput output: sysOutputs)
|
||||
{
|
||||
int sampleRate = output.getSampleRate();
|
||||
int numBytesPerSample = output.getAudioFormat().getSampleSizeInBits() / 8;
|
||||
int numChannels = output.getNumChannels();
|
||||
int numBytesToWrite = (int)(mBufferDuration.getInSamples(sampleRate) * numChannels * numBytesPerSample);
|
||||
|
||||
int numBytesPerSample = output.mAudioFormat.getSampleSizeInBits() / 8;
|
||||
int numChannels = output.mAudioFormat.getChannels();
|
||||
int sampleRate = (int)output.mAudioFormat.getSampleRate();
|
||||
output.setNumBytesToWrite(numBytesToWrite);
|
||||
}
|
||||
|
||||
if(output.isLineUsable())
|
||||
{
|
||||
output.mRemainingBytesToWritePerCycle =
|
||||
(int)mBufferDuration.getInSamples(sampleRate) * numChannels * numBytesPerSample;
|
||||
if(mMixSystemOutput != null)
|
||||
{
|
||||
int numSamples = mMixSystemOutput.getNumBytesToWrite() / mMixSystemOutput.getNumBytesPerSample();
|
||||
mMixBuffer = Field.expand(mMixBuffer, numSamples, false);
|
||||
Arrays.fill(mMixBuffer, 0, numSamples, 0.0f);
|
||||
|
||||
outputsWithUsableLines.add(output);
|
||||
}
|
||||
else if(mMixOutput.isLineUsable())
|
||||
{
|
||||
int remainingSamplesToMix = mMixOutput.mNumSamples;
|
||||
int numSmplesMixed = 0;
|
||||
for(MixerOutput output: mixOutputs)
|
||||
output.mix(mMixBuffer, numSamples);
|
||||
|
||||
while(remainingSamplesToMix > 0)
|
||||
{
|
||||
if(!output.receivedData())
|
||||
output.request(); // if we got no sound data we request it
|
||||
mMixSystemOutput.setBuffer(mMixBuffer, numSamples);
|
||||
}
|
||||
|
||||
while(!sysOutputs.isEmpty())
|
||||
{
|
||||
boolean buffersAreFull = true;
|
||||
|
||||
if(output.receivedData())
|
||||
{
|
||||
int numSamplesToMix = Math.min(output.getRemainingSamplesToMix(), remainingSamplesToMix);
|
||||
int count = output.mixToOutput(mMixOutput, numSmplesMixed, numSamplesToMix);
|
||||
for(Iterator<SystemOutput> iOutput = sysOutputs.iterator(); iOutput.hasNext(); )
|
||||
{
|
||||
SystemOutput output = iOutput.next();
|
||||
|
||||
remainingSamplesToMix -= count;
|
||||
numSmplesMixed += count;
|
||||
if(output.isOpen())
|
||||
{
|
||||
if(!output.receivedData())
|
||||
output.request(); // if we got no sound data we request it
|
||||
|
||||
if(output.whereAllSamplesMixed())
|
||||
output.clearData();
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(output.receivedData())
|
||||
{
|
||||
if(!output.isConverted())
|
||||
output.convert();
|
||||
|
||||
while(!outputsWithUsableLines.isEmpty())
|
||||
{
|
||||
boolean lineBuffersAreFull = true;
|
||||
buffersAreFull = buffersAreFull && (output.available() == 0);
|
||||
|
||||
for(Iterator<Output> iOutput = outputsWithUsableLines.iterator(); iOutput.hasNext(); )
|
||||
{
|
||||
Output output = iOutput.next();
|
||||
if(output.write(256))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(!output.receivedData())
|
||||
output.request(); // if we got no sound data we request it
|
||||
iOutput.remove();
|
||||
}
|
||||
|
||||
if(output.receivedData())
|
||||
{
|
||||
if(!output.mLine.isRunning())
|
||||
output.mLine.start();
|
||||
|
||||
if(!output.wasPCMConverted())
|
||||
output.convertPCM();
|
||||
|
||||
assert output.getRemainingBytesToWrite() != 0;
|
||||
assert output.mRemainingBytesToWritePerCycle != 0;
|
||||
|
||||
lineBuffersAreFull = lineBuffersAreFull && (output.mLine.available() == 0);
|
||||
|
||||
int numBytesWritable =
|
||||
min(output.mLine.available(), output.getRemainingBytesToWrite(), output.mRemainingBytesToWritePerCycle);
|
||||
|
||||
output.mRemainingBytesToWritePerCycle -= output.writeToLine(numBytesWritable);
|
||||
|
||||
if(output.wasAllDataWritten())
|
||||
output.clearData();
|
||||
|
||||
if(output.mRemainingBytesToWritePerCycle <= 0)
|
||||
iOutput.remove();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if after we requested data we still don't have any, we quit processing of this output
|
||||
iOutput.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(lineBuffersAreFull)
|
||||
{
|
||||
if(!mUseDynamicLoadScaling.get())
|
||||
suspendThread(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(buffersAreFull && !mUseDynamicLoadScaling.get())
|
||||
suspendThread(50);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
|
|
@ -567,11 +659,9 @@ public class SoundSystem extends Thread
|
|||
private static byte[] convertUniformPCM(byte[] pcmBuffer, float[] uniformSamples, int numUniformSamples, int numBytesPerSample)
|
||||
{
|
||||
int numBitsPerSample = numBytesPerSample * 8;
|
||||
long maxValue = (long)((Math.pow(2, numBitsPerSample) - 1.0) / 2.0);
|
||||
|
||||
if(pcmBuffer == null || pcmBuffer.length < (numBytesPerSample * numUniformSamples))
|
||||
pcmBuffer = new byte[numBytesPerSample * numUniformSamples];
|
||||
|
||||
long maxValue = (long)((Math.pow(2, numBitsPerSample) - 1.0) / 2.0);
|
||||
pcmBuffer = Field.expand(pcmBuffer, (numBytesPerSample * numUniformSamples), false);
|
||||
|
||||
for(int i=0; i<numUniformSamples; ++i)
|
||||
{
|
||||
|
|
@ -586,7 +676,7 @@ public class SoundSystem extends Thread
|
|||
}
|
||||
|
||||
return pcmBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
private static void mixUniformPCM(float[] result, int rBegin, float[] data, int dBegin, int numSamples)
|
||||
{
|
||||
|
|
@ -599,7 +689,7 @@ public class SoundSystem extends Thread
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean convertChannels(Output output)
|
||||
private static float[] convertChannels(float[] buffer, int numFrames, int numChannels, int numRequiredChannels)
|
||||
{
|
||||
/* Assignments for audio channels
|
||||
* channel 0: Front left
|
||||
|
|
@ -612,51 +702,49 @@ public class SoundSystem extends Thread
|
|||
* channel 7: Surround back right - ## NEEDS CONFIRMATION ##
|
||||
*/
|
||||
|
||||
if(!output.receivedData() || output.mNumChannels == output.mAudioFormat.getChannels())
|
||||
return true;
|
||||
|
||||
int numRequiredChannels = output.mAudioFormat.getChannels();
|
||||
if(numChannels == numRequiredChannels)
|
||||
return buffer;
|
||||
|
||||
// we have to reduce the number of channels
|
||||
if(output.mNumChannels > numRequiredChannels)
|
||||
if(numChannels > numRequiredChannels)
|
||||
{
|
||||
// stereo/multichannel to mono - maybe this won't work properly for more than 2 channels
|
||||
// - ## NEEDS CONFIRMATION ##
|
||||
if(numRequiredChannels == 1)
|
||||
{
|
||||
for(int i=0; i<output.mNumSamples; ++i)
|
||||
for(int i=0; i<numFrames; ++i)
|
||||
{
|
||||
int index = i * output.mNumChannels;
|
||||
int index = i * numChannels;
|
||||
float value = 0.0f;
|
||||
|
||||
for(int c=0; c<output.mNumChannels; ++c)
|
||||
value += output.mUniformPCM[index + c];
|
||||
for(int c=0; c<numChannels; ++c)
|
||||
value += buffer[index + c];
|
||||
|
||||
output.mUniformPCM[i] = value / (float)output.mNumChannels;
|
||||
buffer[i] = value / (float)numChannels;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not implemented yet
|
||||
return false;
|
||||
buffer = null;
|
||||
}
|
||||
}
|
||||
else // we have to increase the number of channels
|
||||
{
|
||||
// mono to stereo/multichannel
|
||||
if(output.mNumChannels == 1)
|
||||
if(numChannels == 1)
|
||||
{
|
||||
float[] newUniformPCM = new float[output.mNumSamples * numRequiredChannels];
|
||||
float[] newUniformPCM = new float[numFrames * numRequiredChannels];
|
||||
|
||||
for(int i=0; i<output.mNumSamples; ++i)
|
||||
for(int i=0; i<numFrames; ++i)
|
||||
{
|
||||
int index = i * numRequiredChannels;
|
||||
|
||||
for(int c=0; c<numRequiredChannels; ++c)
|
||||
newUniformPCM[index + c] = output.mUniformPCM[i];
|
||||
newUniformPCM[index + c] = buffer[i];
|
||||
}
|
||||
|
||||
output.mUniformPCM = newUniformPCM;
|
||||
buffer = newUniformPCM;
|
||||
}
|
||||
else // stereo/multichannel to multichannel
|
||||
{
|
||||
|
|
@ -669,20 +757,19 @@ public class SoundSystem extends Thread
|
|||
*/
|
||||
|
||||
// not implemented yet
|
||||
return false;
|
||||
buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
output.mNumChannels = numRequiredChannels;
|
||||
return true;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static boolean convertSampleRate(Output output)
|
||||
private static float[] convertSampleRate(float[] buffer, int numSamples, int numChannels, int sampleRate, int toSampleRate)
|
||||
{
|
||||
if(!output.receivedData() || output.mSampleRate == (int)output.mAudioFormat.getSampleRate())
|
||||
return true;
|
||||
if(sampleRate == toSampleRate)
|
||||
return buffer;
|
||||
|
||||
// not implemented yet
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package games.stendhal.client.sound.system.processors;
|
|||
|
||||
import games.stendhal.common.math.Algebra;
|
||||
import games.stendhal.client.sound.system.SignalProcessor;
|
||||
import games.stendhal.common.memory.Field;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -14,10 +15,11 @@ import games.stendhal.client.sound.system.SignalProcessor;
|
|||
*/
|
||||
public class DirectedSound extends SignalProcessor
|
||||
{
|
||||
private final float[] mUpVector = { 0.0f, 1.0f, 0.0f };
|
||||
private float mIntensity = 1.0f;
|
||||
private float mLVolume = 1.0f;
|
||||
private float mRVolume = 1.0f;
|
||||
private final float[] mUpVector = { 0.0f, 1.0f, 0.0f };
|
||||
private float mIntensity = 1.0f;
|
||||
private float mLVolume = 1.0f;
|
||||
private float mRVolume = 1.0f;
|
||||
private float[] mOutputBuffer = null;
|
||||
|
||||
public DirectedSound() { }
|
||||
|
||||
|
|
@ -95,15 +97,15 @@ public class DirectedSound extends SignalProcessor
|
|||
|
||||
if(channels == 1)
|
||||
{
|
||||
float[] newData = new float[samples * 2];
|
||||
mOutputBuffer = Field.expand(mOutputBuffer, (samples * 2), false);
|
||||
|
||||
for(int i=0; i<samples; ++i)
|
||||
{
|
||||
newData[i*2 + 0] = data[i] * mLVolume; // multiply with the positional volume
|
||||
newData[i*2 + 1] = data[i] * mRVolume; // do the same for the right channel
|
||||
mOutputBuffer[i*2 + 0] = data[i] * mLVolume; // multiply with the positional volume
|
||||
mOutputBuffer[i*2 + 1] = data[i] * mRVolume; // do the same for the right channel
|
||||
}
|
||||
|
||||
data = newData;
|
||||
data = mOutputBuffer;
|
||||
channels = 2;
|
||||
}
|
||||
else // if(channels == 2)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import java.io.InputStream;
|
|||
import java.io.IOException;
|
||||
import com.jcraft.jogg.*;
|
||||
import com.jcraft.jorbis.*;
|
||||
import games.stendhal.common.memory.Field;
|
||||
/**
|
||||
*
|
||||
* @author silvio
|
||||
|
|
@ -23,7 +24,7 @@ public class OggVorbisDecoder extends SignalProcessor
|
|||
private Comment mVorbisComment = null;
|
||||
private Info mVorbisInfo = null;
|
||||
|
||||
private float[][][] mUniformPCMData = null;
|
||||
private float[][][] mUniformPCMData = new float[1][][];
|
||||
int[] mPCMIndex = null;
|
||||
private float[] mOutputBuffer = null;
|
||||
private byte[] mInputBuffer = null;
|
||||
|
|
@ -57,9 +58,8 @@ public class OggVorbisDecoder extends SignalProcessor
|
|||
|
||||
// IMPORTANT: read the header first before setting up the buffers
|
||||
// or else getNumChannels() will not return the right number
|
||||
mUniformPCMData = new float[1][][];
|
||||
mPCMIndex = new int[getNumChannels()];
|
||||
mOutputBuffer = new float[outputNumSamplesPerChannel * getNumChannels()];
|
||||
mPCMIndex = Field.expand(mPCMIndex , getNumChannels() , false);
|
||||
mOutputBuffer = Field.expand(mOutputBuffer, (outputNumSamplesPerChannel * getNumChannels()), false);
|
||||
mDecoderIsOpened = true;
|
||||
}
|
||||
|
||||
|
|
@ -287,7 +287,6 @@ public class OggVorbisDecoder extends SignalProcessor
|
|||
assert false: exception.toString();
|
||||
}
|
||||
|
||||
mOutputBuffer = null;
|
||||
mIStream = null;
|
||||
mDecoderIsOpened = false;
|
||||
mEndOfStream = true;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
package games.stendhal.client.sound.system.processors;
|
||||
|
||||
import games.stendhal.client.sound.system.SignalProcessor;
|
||||
import games.stendhal.common.memory.Field;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
|
|
@ -18,8 +19,8 @@ public class PCMStreamConverter extends SignalProcessor
|
|||
{
|
||||
private InputStream mInputStream;
|
||||
private AudioFormat mAudioFormat;
|
||||
private float[] mOutputBuffer;
|
||||
private byte[] mInputBuffer;
|
||||
private float[] mOutputBuffer = null;
|
||||
private byte[] mInputBuffer = null;
|
||||
private int mNumSamplesToBuffer;
|
||||
private boolean mEndOfStream;
|
||||
private boolean mConverterIsOpened;
|
||||
|
|
@ -45,18 +46,19 @@ public class PCMStreamConverter extends SignalProcessor
|
|||
mEndOfStream = true;
|
||||
mConverterIsOpened = false;
|
||||
|
||||
try
|
||||
{
|
||||
if (mInputStream != null) {
|
||||
mInputStream.close();
|
||||
}
|
||||
}
|
||||
catch(IOException exception)
|
||||
{
|
||||
assert false: exception.toString();
|
||||
}
|
||||
if(mInputStream != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mInputStream.close();
|
||||
}
|
||||
catch(IOException exception)
|
||||
{
|
||||
assert false: exception.toString();
|
||||
}
|
||||
|
||||
mInputStream = null;
|
||||
mInputStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void init(InputStream stream, AudioFormat format, int outputNumSamplesPerChannel)
|
||||
|
|
@ -69,12 +71,8 @@ public class PCMStreamConverter extends SignalProcessor
|
|||
int outputBufferSize = outputNumSamplesPerChannel * format.getChannels();
|
||||
int inputBufferSize = outputNumSamplesPerChannel * format.getFrameSize();
|
||||
|
||||
if(mInputBuffer == null || mInputBuffer.length < inputBufferSize)
|
||||
mInputBuffer = new byte[inputBufferSize];
|
||||
|
||||
if(mOutputBuffer == null || mOutputBuffer.length < outputBufferSize)
|
||||
mOutputBuffer = new float[outputBufferSize];
|
||||
|
||||
mInputBuffer = Field.expand(mInputBuffer , inputBufferSize , false);
|
||||
mOutputBuffer = Field.expand(mOutputBuffer, outputBufferSize, false);
|
||||
mInputStream = stream;
|
||||
mAudioFormat = format;
|
||||
mNumSamplesToBuffer = outputNumSamplesPerChannel;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
package games.stendhal.client.sound.system.processors;
|
||||
|
||||
import games.stendhal.client.sound.system.SignalProcessor;
|
||||
import games.stendhal.common.memory.Field;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -13,33 +14,62 @@ import games.stendhal.client.sound.system.SignalProcessor;
|
|||
*/
|
||||
public class Recorder extends SignalProcessor
|
||||
{
|
||||
public class Player extends SignalProcessor
|
||||
{
|
||||
private float[] mOutputBuffer;
|
||||
private int mNumSamplesRead = 0;
|
||||
|
||||
private Player(int outputNumSamples)
|
||||
{
|
||||
mOutputBuffer = new float[outputNumSamples * mNumChannels];
|
||||
}
|
||||
|
||||
public synchronized void restart () { mNumSamplesRead = 0; }
|
||||
public synchronized boolean reachedEndOfStream() { return mNumSamplesRead >= mNumSamplesBuffered; }
|
||||
public synchronized int getNumChannels () { return mNumChannels; }
|
||||
public synchronized int getSampleRate () { return mSampleRate; }
|
||||
public synchronized int getNumSamples () { return mNumSamplesBuffered / mNumChannels; }
|
||||
|
||||
@Override
|
||||
protected synchronized boolean generate()
|
||||
{
|
||||
if(reachedEndOfStream())
|
||||
{
|
||||
super.quit();
|
||||
return false;
|
||||
}
|
||||
|
||||
int outputBufferSize = mOutputBuffer.length;
|
||||
int numSamplesAvailable = mNumSamplesBuffered - mNumSamplesRead;
|
||||
int numSamples = Math.min(outputBufferSize, numSamplesAvailable);
|
||||
|
||||
System.arraycopy(mData, mNumSamplesRead, mOutputBuffer, 0, numSamples);
|
||||
mNumSamplesRead += numSamples;
|
||||
|
||||
super.propagate(mOutputBuffer, (numSamples / mNumChannels), mNumChannels, mSampleRate);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private float[] mData = null;
|
||||
private float[] mOutputBuffer = null;
|
||||
private int mNumChannels = 1;
|
||||
private int mSampleRate = 0;
|
||||
private int mNumSamplesBuffered = 0;
|
||||
private int mNumSamplesRead = 0;
|
||||
private int mOutputNumSamples;
|
||||
|
||||
public Recorder(int outputNumSamplesPerChannel)
|
||||
{
|
||||
mOutputNumSamples = outputNumSamplesPerChannel;
|
||||
}
|
||||
public Player createPlayer(int outputNumSamplesPerChannel)
|
||||
{
|
||||
return new Player(outputNumSamplesPerChannel);
|
||||
}
|
||||
|
||||
public synchronized void clear()
|
||||
{
|
||||
mNumSamplesBuffered = 0;
|
||||
mNumSamplesRead = 0;
|
||||
mData = null;
|
||||
mOutputBuffer = null;
|
||||
}
|
||||
|
||||
public synchronized void restart () { mNumSamplesRead = 0; }
|
||||
public synchronized boolean reachedEndOfStream() { return mNumSamplesRead >= mNumSamplesBuffered; }
|
||||
public synchronized int getNumChannels () { return mNumChannels; }
|
||||
public synchronized int getSampleRate () { return mSampleRate; }
|
||||
public synchronized int getNumSamples () { return mNumSamplesBuffered / mNumChannels; }
|
||||
|
||||
public synchronized int getNumChannels() { return mNumChannels; }
|
||||
public synchronized int getSampleRate () { return mSampleRate; }
|
||||
public synchronized int getNumSamples () { return mNumSamplesBuffered / mNumChannels; }
|
||||
|
||||
public synchronized void trim()
|
||||
{
|
||||
|
|
@ -78,38 +108,11 @@ public class Recorder extends SignalProcessor
|
|||
|
||||
// if the buffer is to small to hold the whole data, we expand it
|
||||
if(remainingBufferSpace < requiredBufferSpace)
|
||||
{
|
||||
float[] newData = new float[(mNumSamplesBuffered + requiredBufferSpace) * 2];
|
||||
System.arraycopy(mData, 0, newData, 0, mNumSamplesBuffered);
|
||||
mData = newData;
|
||||
}
|
||||
mData = Field.expand(mData, (mNumSamplesBuffered + requiredBufferSpace) * 2, true);
|
||||
|
||||
System.arraycopy(data, 0, mData, mNumSamplesBuffered, requiredBufferSpace);
|
||||
mNumSamplesBuffered += requiredBufferSpace;
|
||||
|
||||
super.propagate(data, samples, channels, rate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean generate()
|
||||
{
|
||||
if(reachedEndOfStream())
|
||||
{
|
||||
super.quit();
|
||||
return false;
|
||||
}
|
||||
|
||||
int numSamplesToRead = mOutputNumSamples * mNumChannels;
|
||||
int numSamplesAvailable = mNumSamplesBuffered - mNumSamplesRead;
|
||||
int numSamples = Math.min(numSamplesToRead, numSamplesAvailable);
|
||||
|
||||
if(mOutputBuffer == null)
|
||||
mOutputBuffer = new float[numSamplesToRead];
|
||||
|
||||
System.arraycopy(mData, mNumSamplesRead, mOutputBuffer, 0, numSamples);
|
||||
mNumSamplesRead += numSamples;
|
||||
|
||||
super.propagate(mOutputBuffer, (numSamples / mNumChannels), mNumChannels, mSampleRate);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ public class SpriteStore {
|
|||
// avoid infinite loop and stack overflow in case of missing
|
||||
// failsafe icon
|
||||
if (ref.equals(FAILSAFE_ICON_REF)) {
|
||||
logger.error("Cannot find FAILSAFE_ICON_REF: " + FAILSAFE_ICON_REF);
|
||||
return null;
|
||||
} else {
|
||||
return getFailsafe();
|
||||
|
|
@ -215,8 +216,14 @@ public class SpriteStore {
|
|||
// use ImageIO to read the image in
|
||||
sourceImage = ImageIO.read(url);
|
||||
} catch (final IOException e) {
|
||||
logger.error("Failed to load: " + ref, e);
|
||||
return null;
|
||||
logger.error("Failed to read: " + ref, e);
|
||||
|
||||
if (ref.equals(FAILSAFE_ICON_REF)) {
|
||||
logger.error("Cannot read FAILSAFE_ICON_REF: " + FAILSAFE_ICON_REF);
|
||||
return null;
|
||||
} else {
|
||||
return getFailsafe();
|
||||
}
|
||||
}
|
||||
|
||||
// create an accelerated image of the right size to store our sprite in
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public class stendhal {
|
|||
logger.info("Java-VM: " + System.getProperty("java.vm.vendor") + " "
|
||||
+ System.getProperty("java.vm.name") + " "
|
||||
+ System.getProperty("java.vm.version"));
|
||||
|
||||
LogUncaughtExceptionHandler.setup();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ package games.stendhal.client.update;
|
|||
public class Version {
|
||||
|
||||
/** Version Number. */
|
||||
public static final String VERSION = "0.80.5";
|
||||
public static final String VERSION = "0.81.3";
|
||||
|
||||
/**
|
||||
* Extract the specified number of parts from a version-string.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public interface Debug {
|
|||
|
||||
/** server version. */
|
||||
// Note: This line is updated by build.xml using a regexp so be sure to adjust it in case you modify this line.
|
||||
String VERSION = "0.80.5";
|
||||
String VERSION = "0.81.3";
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
94
src/games/stendhal/common/memory/Field.java
Normal file
94
src/games/stendhal/common/memory/Field.java
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package games.stendhal.common.memory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author silvio
|
||||
*/
|
||||
public class Field
|
||||
{
|
||||
/**
|
||||
* This method will return a new allocated array of size newSize if newSize
|
||||
* is greater than array.length.
|
||||
* Tt will return the same array otherwise
|
||||
* Only if keepData is set to true, the data of the array will be copied to the
|
||||
* new allocated array
|
||||
*
|
||||
* @param array
|
||||
* @param newSize
|
||||
* @param keepData
|
||||
* @return - new or old array
|
||||
*/
|
||||
public static byte[] expand(byte[] array, int newSize, boolean keepData)
|
||||
{
|
||||
if(array == null || array.length < newSize)
|
||||
{
|
||||
byte[] newArray = new byte[newSize];
|
||||
|
||||
if(keepData && array != null)
|
||||
System.arraycopy(array, 0, newArray, 0, array.length);
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return a new allocated array of size newSize if newSize
|
||||
* is greater than array.length.
|
||||
* Tt will return the same array otherwise
|
||||
* Only if keepData is set to true, the data of the array will be copied to the
|
||||
* new allocated array
|
||||
*
|
||||
* @param array
|
||||
* @param newSize
|
||||
* @param keepData
|
||||
* @return - new or old array
|
||||
*/
|
||||
public static int[] expand(int[] array, int newSize, boolean keepData)
|
||||
{
|
||||
if(array == null || array.length < newSize)
|
||||
{
|
||||
int[] newArray = new int[newSize];
|
||||
|
||||
if(keepData && array != null)
|
||||
System.arraycopy(array, 0, newArray, 0, array.length);
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return a new allocated array of size newSize if newSize
|
||||
* is greater than array.length.
|
||||
* Tt will return the same array otherwise
|
||||
* Only if keepData is set to true, the data of the array will be copied to the
|
||||
* new allocated array
|
||||
*
|
||||
* @param array
|
||||
* @param newSize
|
||||
* @param keepData
|
||||
* @return - new or old array
|
||||
*/
|
||||
public static float[] expand(float[] array, int newSize, boolean keepData)
|
||||
{
|
||||
if(array == null || array.length < newSize)
|
||||
{
|
||||
float[] newArray = new float[newSize];
|
||||
|
||||
if(keepData && array != null)
|
||||
System.arraycopy(array, 0, newArray, 0, array.length);
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
|
@ -55,11 +55,18 @@ public class ResourceManager extends ResourceLocator
|
|||
}
|
||||
}
|
||||
|
||||
private class CPSchemeLocator implements ResourceLocator.Locator
|
||||
private interface SchemeLocator
|
||||
{
|
||||
void addSearchPath(String path);
|
||||
InputStream locate (URI uri);
|
||||
boolean isLocatable (URI uri);
|
||||
}
|
||||
|
||||
private class CPSchemeLocator implements ResourceLocator.Locator, SchemeLocator
|
||||
{
|
||||
final ArrayList<String> mPaths = new ArrayList<String>();
|
||||
|
||||
void addSearchPath(String path)
|
||||
public void addSearchPath(String path)
|
||||
{
|
||||
if(path.endsWith("/"))
|
||||
path = path.substring(0, path.length() - 1);
|
||||
|
|
@ -87,25 +94,27 @@ public class ResourceManager extends ResourceLocator
|
|||
{
|
||||
InputStream stream = locate(uri);
|
||||
|
||||
try
|
||||
if(stream != null)
|
||||
{
|
||||
stream.close();
|
||||
try
|
||||
{
|
||||
stream.close();
|
||||
}
|
||||
catch(IOException exception)
|
||||
{
|
||||
assert false: exception;
|
||||
stream = null;
|
||||
}
|
||||
}
|
||||
catch(IOException exception)
|
||||
{
|
||||
assert false: exception;
|
||||
stream = null;
|
||||
}
|
||||
|
||||
return stream != null;
|
||||
}
|
||||
}
|
||||
|
||||
private class SchemeLocator implements ResourceLocator.Locator
|
||||
private class FSSchemeLocator implements ResourceLocator.Locator, SchemeLocator
|
||||
{
|
||||
final ArrayList<File> mDirectories = new ArrayList<File>();
|
||||
|
||||
void addSearchPath(String path)
|
||||
public void addSearchPath(String path)
|
||||
{
|
||||
File directory = new File(mRootDirectory, path);
|
||||
|
||||
|
|
@ -113,7 +122,7 @@ public class ResourceManager extends ResourceLocator
|
|||
mDirectories.add(directory);
|
||||
}
|
||||
|
||||
File locateFile(URI uri)
|
||||
public File locateFile(URI uri)
|
||||
{
|
||||
for(File directory: mDirectories)
|
||||
{
|
||||
|
|
@ -146,7 +155,7 @@ public class ResourceManager extends ResourceLocator
|
|||
}
|
||||
}
|
||||
|
||||
private final HashMap<String,CPSchemeLocator> mSchemeLocators = new HashMap<String,CPSchemeLocator>();
|
||||
private final HashMap<String,SchemeLocator> mSchemeLocators = new HashMap<String,SchemeLocator>();
|
||||
private File mRootDirectory = null;
|
||||
|
||||
public void addScheme(String name, String ...searchPaths)
|
||||
|
|
@ -156,10 +165,10 @@ public class ResourceManager extends ResourceLocator
|
|||
|
||||
if(!mSchemeLocators.containsKey(name))
|
||||
{
|
||||
CPSchemeLocator locator = new CPSchemeLocator();
|
||||
SchemeLocator locator = new CPSchemeLocator();
|
||||
|
||||
mSchemeLocators.put(name, locator);
|
||||
setLocator(name, locator);
|
||||
setLocator(name, (Locator)locator);
|
||||
|
||||
for(String path: searchPaths)
|
||||
locator.addSearchPath(path);
|
||||
|
|
@ -168,7 +177,7 @@ public class ResourceManager extends ResourceLocator
|
|||
|
||||
public void addSearchPath(String scheme, String path)
|
||||
{
|
||||
CPSchemeLocator locator = mSchemeLocators.get(scheme);
|
||||
SchemeLocator locator = mSchemeLocators.get(scheme);
|
||||
|
||||
assert locator != null;
|
||||
assert path != null;
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ public class GenerateINI {
|
|||
out.println();
|
||||
out.println("server_typeGame=" + gameName);
|
||||
out.println("server_name=" + gameName + " Marauroa server");
|
||||
out.println("server_version=0.80.5");
|
||||
out.println("server_version=0.81.3");
|
||||
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.");
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import java.io.InputStream;
|
|||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -210,15 +211,18 @@ public class StendhalRPRuleProcessor implements IRPRuleProcessor {
|
|||
*
|
||||
* @param entity
|
||||
* The entity to check.
|
||||
* @return true if the given entity has been killed this turn.
|
||||
* @return pair of killed, killer if the specified entity did logout while dying, <code>null</code> otherwise.
|
||||
*/
|
||||
private boolean wasKilled(final RPEntity entity) {
|
||||
for (final Pair<RPEntity, Entity> entry : entityToKill) {
|
||||
private Pair<RPEntity, Entity> removedKilled(final RPEntity entity) {
|
||||
Iterator<Pair<RPEntity, Entity>> itr = entityToKill.iterator();
|
||||
while (itr.hasNext()) {
|
||||
Pair<RPEntity, Entity> entry = itr.next();
|
||||
if (entity.equals(entry.first())) {
|
||||
return true;
|
||||
itr.remove();
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -384,14 +388,22 @@ public class StendhalRPRuleProcessor implements IRPRuleProcessor {
|
|||
if (adminNames == null) {
|
||||
adminNames = new LinkedList<String>();
|
||||
|
||||
final String adminFilename = "data/conf/admins.list";
|
||||
|
||||
String adminFilename = "data/conf/admins.txt";
|
||||
final String adminFilenameAlt = "data/conf/admins.list";
|
||||
|
||||
try {
|
||||
final InputStream is = player.getClass().getClassLoader().getResourceAsStream(
|
||||
InputStream is = player.getClass().getClassLoader().getResourceAsStream(
|
||||
adminFilename);
|
||||
|
||||
if (is == null) {
|
||||
logger.info("data/conf/admins.list does not exist.");
|
||||
// backwards compatibility for those who used to have admins.list
|
||||
is = player.getClass().getClassLoader().getResourceAsStream(
|
||||
adminFilenameAlt);
|
||||
if (is == null) {
|
||||
logger.info("Neither " + adminFilename + " nor " + adminFilenameAlt + " exist.");
|
||||
}
|
||||
// update filename for any error messages
|
||||
adminFilename = adminFilenameAlt;
|
||||
} else {
|
||||
|
||||
final BufferedReader in = new BufferedReader(
|
||||
|
|
@ -495,10 +507,13 @@ public class StendhalRPRuleProcessor implements IRPRuleProcessor {
|
|||
public synchronized boolean onExit(final RPObject object) {
|
||||
try {
|
||||
final Player player = (Player) object;
|
||||
if (wasKilled(player)) {
|
||||
logger.info("Logged out shortly before death: Killing it now :)");
|
||||
player.onDead(killerOf(player));
|
||||
|
||||
Pair<RPEntity, Entity> entry = removedKilled(player);
|
||||
if (entry != null) {
|
||||
logger.info(object.get("name") + " logged out shortly before death: Killing it now :)");
|
||||
entry.first().onDead(entry.second());
|
||||
}
|
||||
|
||||
if (!player.isGhost()) {
|
||||
notifyOnlineStatus(false, player.getName());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package games.stendhal.server.entity.npc.behaviour.adder;
|
||||
|
||||
import games.stendhal.common.Grammar;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
import games.stendhal.server.entity.npc.ChatAction;
|
||||
import games.stendhal.server.entity.npc.ChatCondition;
|
||||
import games.stendhal.server.entity.npc.ConversationPhrases;
|
||||
|
|
@ -126,7 +127,7 @@ public class BuyerAdder {
|
|||
|
||||
boolean success = behaviour.transactAgreedDeal(engine, player);
|
||||
if (success) {
|
||||
engine.addEvent(new SoundEvent("coins-1"));
|
||||
engine.addEvent(new SoundEvent("coins-1", SoundLayer.CREATURE_NOISE));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package games.stendhal.server.entity.npc.behaviour.adder;
|
||||
|
||||
import games.stendhal.common.Grammar;
|
||||
import games.stendhal.common.constants.SoundLayer;
|
||||
import games.stendhal.server.entity.npc.ChatAction;
|
||||
import games.stendhal.server.entity.npc.ChatCondition;
|
||||
import games.stendhal.server.entity.npc.ConversationPhrases;
|
||||
|
|
@ -125,7 +126,7 @@ public class SellerAdder {
|
|||
|
||||
boolean success = behaviour.transactAgreedDeal(engine, player);
|
||||
if (success) {
|
||||
engine.addEvent(new SoundEvent("coins-1"));
|
||||
engine.addEvent(new SoundEvent("coins-1", SoundLayer.CREATURE_NOISE));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ import java.util.List;
|
|||
import marauroa.common.game.RPObject;
|
||||
import marauroa.common.game.RPSlot;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public abstract class UpdateConverter {
|
||||
private static Logger logger = Logger.getLogger(UpdateConverter.class);
|
||||
|
||||
private static final List<String> ITEM_NAMES_OLD = Arrays.asList(
|
||||
"flail_+2", "leather_armor_+1", "leather_cuirass_+1",
|
||||
|
|
@ -212,6 +215,12 @@ public abstract class UpdateConverter {
|
|||
|
||||
// port to 0.66
|
||||
transformKillSlot(object);
|
||||
|
||||
// port to 0.81 because of a bug in 0.80 which allowed 0 hp by double killing on logout during dying
|
||||
if (object.getInt("hp") <= 0) {
|
||||
logger.warn("Setting hp to 1 for player " + object);
|
||||
object.put("hp", 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -30,9 +30,10 @@ public class SoundEvent extends RPEvent {
|
|||
*
|
||||
* @param sound name of sound to play
|
||||
*/
|
||||
public SoundEvent(final String sound) {
|
||||
public SoundEvent(final String sound, SoundLayer layer) {
|
||||
super(Events.SOUND);
|
||||
put("sound", sound);
|
||||
put("layer", layer.ordinal());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.80.5.jar" download="eager" main="true" />
|
||||
<jar href="http://arianne.sourceforge.net/jws/stendhal-starter-0.81.3.jar" download="eager" main="true" />
|
||||
</resources>
|
||||
<application-desc/>
|
||||
</jnlp>
|
||||
|
|
|
|||
|
|
@ -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="64" height="64" tilewidth="32" tileheight="32">
|
||||
<?xml version="1.0"?>
|
||||
<map version="0.99b" orientation="orthogonal" width="64" height="64" tilewidth="32" tileheight="32">
|
||||
<tileset name="../../tileset/ground/ground.png" firstgid="1" tilewidth="32" tileheight="32">
|
||||
<image source="../../tileset/ground/ground.png" trans="ffffff"/>
|
||||
</tileset>
|
||||
|
|
@ -191,12 +190,12 @@
|
|||
</layer>
|
||||
<layer name="1_terrain" width="64" height="64">
|
||||
<data encoding="base64" compression="gzip">
|
||||
H4sIAAAAAAAAAO2ZOW8UMRTHPVvTAzUQwhUIElcJEiUtEpf4BOHIbsiWoYCKK2EhSQECRIWAGvgqnAXkEgXiLnnW+mm85j37ecazu0n2SX/N5fHzz34+xqNUWmvVlLpfS5xpSdNluhdRrislfWk/Z1R5pbJY/rK+Bvyri79MvFO+UvOXLd+g/Tv5JxLmTfnqd/4qrd/4J9T64qfGivXEz5VpwN9d/lOqHPu5xGVKwT9TCwv97Ib0e0poBDQt8CctUwr+lpWXT9rPDki/k9BQ1nk9nNHpdkX4k6os/1wtLPSVst0kfiVlqrr/67mn39pMqe6PfwP+7vFLvo16Mf/ZDPNGHOOsEcc/BvleAF20dAl02fjUc+1p1Tl/nlX5HNoL/hbBz41PIf69kO8+lc8des7Q16PGZ+h5r/nR9yEo1wFHBzN/n8EyN03+4wH/+Lxp3QvxF1mr6dg6CjoG2gIMW0Hbsvb1cUXzw2N1OOtUi6grV1Wuf3R9Yfyg3DjiYssXdxQ/+j+RtXWe4S8738fM/2484ZqTqveYtqDi2PVP8es0oRiPtVD8j1tKOXdR/A3wcTLL5eP/APpYUJ8i+O16mC0QV1L+/cB7xNFIxvOvOOVbMnLvKee+Pv9agP8t6F1A71UeK1xduex3jbi+Mm3E8btsFL97zfGPWfdx/TBpjosqr2NOG7LOOqDkjmfIz8WGhN/ldG3JScfx22P9qJOHhF9r2VMOij+2n3Dxb7NS57Zx/E0nnb1+roo/Zhyx+X+D/hTUX4bfZ1J+7AMSfsmas9fzH8aAlN8XAyH+unNURPpu86NVyW/z1o2w3vuF/wfop1C/mDx87V9n3ukX/hQW0/+xPih+/d9K+l0W2iv38dvzNmV6TZQZcWMeWkOlHf+ke+gjwvxD/FQ92GOCb96z8wv1f658Lv8QaLtAwyrfI7LjAveIYviV6qyDVPw+4/jxH4L7z4D6VyD5Fpfwu+2k1yChMU/CT7W/r/9T+WpbzPJ79jnapElrr+vc9b+OE3v/UKdvKv84ILWq1j/63jLw3jL5rZjzZacO6irncfOn1v92zEvYQ3ux1Po3Zq8k1P534PlC9v85Go7BXPtTRvV7bRNEmo3gb1PW7mf6OGyOm7N2+pTfP1QZlyxet+21YRxPWvd8/FhfXB34jCtnlfwhw/in1tfS+a+sUvBXUR4q7xT/aUN7n7GqsjypyzpQp1Lv3fdaMWz6H99aMj2GVvHt2EiXVeXm8rv7h2vdUrf/amp7bd3cOyliVddnSv5+bfsp0FXmWQp+irtoXcS+12DO0aZAN0A3mfer4u8XewKa8zwvwx/ijqkXtx1j8vbF30vQU08+sfzUPg8Xd1yfk7xfJA1lbwLPOX7Xn8+/u2aYUv4+J7Ey40BM+hC/Pl4DXQ/4wGdTKtznqPek91NZiN+2adCMatcBvsvFvGYP9bmylrLOpP3/IegR6DFoHnTb0gNzH9lDfS5kUo4UMRLaO0N7BnoOeuERskvL9Rn0hdFCPEohC7W/zfIK9Nqj2Hb/5nn2PTIvtNiYkIz/MXNBjKXi95XnH6yyEbMAQAAA
|
||||
H4sIAAAAAAAAAO2ZOW8UMRTHPVvTAzUQwhUIElcJEiUtEpf4BOHIbsiWSwEVV8JCkgIEiAoBNfBVOAvIJQrEXfKsmafxWu/ZzzOe3UnYJ/01l8fPP/v5GI9Sca3bUOp+I3KmJU2X6V5Aua6U9KX9nFHlFctC+cv6GvKvLf4y8U75is1ftnzD9u/ln4qYN+Wr7vxVWt34p9T/xU+NFf8TP1emIX9/+U+pcuznIpcpBv9swy/0sxvS7ymhMdCMwJ+0TDH4u0ZeLmk/OyD9TkIjSe/1aEKn2xXgT6qy/PMNv9BXzHaT+JWUqer+r+eeurWZUv0f/4b8/eOXfBsNYv4zGRYycYxzmTj+Ccj3AuiioUugy5lPPdeeVr3z51mVz6GD4O8S/Nz45OPfC/nuU/ncoecMfT2e+fQ9HzQ/+j4E5Tpg6WDi7jNY5naW/6THPz5vG/d8/EXWajq2joKOgbYAw1bQtiS9Pq5ofnisDie96hJ1ZavK9Y+uL4wflB1HXGy54o7iR/8nklTnGf6y833I/G/HE645qXoPaQsqjm3/FL9O44vxUPPF/6ShmHMXxd8CHyeTXC7+D6CPBfUpgN+sh7kCcSXl3w+8RyyNJTz/qlW+5Uz2PWXd1+dfC/C/Bb3z6L3KY4WrK5v9biaur8xk4vhtNorfvub4J4z7uH6Yzo5LKq9jThuS3jqgZI9nyM/FhoTf5rRt2UrH8Ztj/biVh4Rfa8VRDoo/tJ9w8W+yUuemcfxtK525fq6KP2QcMfl/g/4U1F+G32VSfuwDEn7JmnPQ8x/GgJTfFQM+/qZ1VET6fvOjVclv8jYzYb3Xhf8H6KdQv5g8XO3fZN6pC38MC+n/WB8Uv/5vJf0u8+2Vu/jNeZsyvSZKMnFjHlpLxR3/pHvoY8L8ffxUPZhjgmveM/Pz9X+ufDb/CGi7QKMq3yMy4wL3iEL4leqtg1j8LuP48R+C/c+A+lcg+RaX8NvtpNcgvjFPwk+1v6v/U/miLSXpsUWkm87Smus6e/2v48TcP9Tp28o9DkitqvUP3lsB9luNlH01O19J8nRNlfPY+VPrfzPmJey+vVhq/RuyVyJp/zuQZjFJ6wDP0XAM5tqfMqrfa5si0mwEX5uStJ/p42h23Jyk6WN+/3D8ywav2fbaMI6njXsufqwvrg5cxpWzan6XYfxT62vp/FdWMfirKA+Vd4z/tL69z1BVWZ7YZR2qV7H37getEDb9j289mR5Dq/h2pNZtdTWb394/XO8Wu/3XUttr6+feSRGruj5j8te17Tugq8yzGPwUd9G6CH2vxZyjdUA3QDeZ96vir4s9Ac07npfh93GH1IvdjiF5u+LvJeipI59Qfmqfh4s7rs9J3i+ShrI3nuccv+3P5d9eM3SUu89JrMw4EJLex6+P10DXPT7wWUf5+xz1nvR+LPPxmzYDmlVpHeC7XMxrdl+fK2sx60za/x+CHoEegxZAtw09yO4ju6/P+UzKESNGfHtnaM9Az0EvHEJ2abk+g74wWgxHKWS+9jdZXoFeOxTa7t8cz74H5oUWGhOS8T9kLgixWPyu8vwDR965HgBAAAA=
|
||||
</data>
|
||||
</layer>
|
||||
<layer name="2_object" width="64" height="64">
|
||||
<data encoding="base64" compression="gzip">
|
||||
H4sIAAAAAAAAAO1aW2sUMRTujLSI1xfvPqpUUbEFUaw+KP4BFVRon91W1N11rZeWIkXpw/48tS9esOqbTwpeEJ88hyb0bEgyZ5LMJG7ng4/Zmckm5zs5yUmyOzIyfGhlsS0YLqTmzxD2FNVxh9lGTN+sZIPcbFD1+/rA1ufqu1TGxGaPgSrg4s864yF0W2pspxZPscaa6gNqBzc/hAS2b/JFzPmoyrZTmWddMO3BEEjRd0fApqOExyLZyPGNr/8+wvfXgJ9EPfNwfQwcJ3wCfJrpy8dAHTHTI6wbc6DvboLjgoO+IRfZYCtnyydlbJKMiWvA64Q3DOWk3lD5OxX9N4G3CG9byrYc4qgIVc8bbUHMGfKzimnxnubP2P0SCjR30JzBfS9xBp5PCU4E8I1pr7A196+7DMrEH5ZVx/+MoLoOk8/lugXXKzNKfVeBf7P1qw2XNDZeTCQ+50XMqHG0JlgUW+MMHaegzCO4HoDYOJhvPPtf8Q1s/y7sl2uZ3BL3Uv8hKHO4QH8bnt/z5H3giYT8e1pjy0mDfZ0AdmMdnPjqQpmHCfkJ4asf52Ou/h7GZcE6sW50NDnFpQ6dflw7dDXlpc4FS7tLhnehfUT1n1XmlHNwf16wqA6dfjnXI1S7n8H98xJ7i5ahHl8U9T/6QL5/YZlDOPFPbV/O1vv/FfA18A25riq++QGffwL/ZPXr55zPcvTr7EaNb4HvgO/J9UM2ODZ+weffDN2632mKzpjnAuQ/3OdPOvYLxsEi+e6i0G6bG2z6Xd7VAbkOlnGg5sK+EvPLSnmO/S76q8o5dFyvatrQ5UKdLVXrr+r3BTqucTzPknbomHvgON508In/us7KVeDciXnlM7ADuaYL/OLYJ6hRt25YYMzhmKvpeTDeyz2ZaY8fAlL/ZZwPQHsPeMVDP0I39jjjn+69bWgr9AHqfyna++rp5xWRN9Sxt2Tpf6lf9j/ur49ng/t0vI6RNaDuDGjM8RyG6ufCNGdLjdvAlu3AHflGHnXJf7Qdes6k+4+G6zmUbv/lmpPkOmcn2LILuDsffG5D2XMtznfp+t8E2/5TPQ9LBWr/63IoPusz/C7163zIPWutCtImdV7ixgpnbREy/in2QOzvZYzJCSgzWfMZMsVsRX26DzTtD6DrAtQxlfPvG8THsPwW1cCMURhzWzw5qhm33PJl22rAQ4z/qjRo0ICH2OeosZHaf7ZV1GVXivpT7peUkKqf6sr9qepv0KDB5kaz/xlO/ANyH73mAEAAAA==
|
||||
H4sIAAAAAAAAAO1aW2sUMRTujLSI1xfvPqpUUbEFUaw+KP4BFVRon91W1N11rZeWIkXpw/48tS9esOqbTwpeEJ88hyb0bEgyZ5LMJG7ng4/Zmckm5zs5yUmyOzIyfGhlsS0YLqTmzxD2FNVxh9lGTN+sZIPcbFD1+/rA1ufqu1TGxGaPgSrg4s864yF0W2pspxZPscaa6gNqBzc/hAS2b/JFzPmoyrZTmWddMO3BEEjRd0fApqOExyLZyPGNr/8+wvfXgJ9EPfNwfQwcJ3wCfJrpy8dAHTHTI6wbc6DvboLjgoO+IRfZYCtnyydlbJKMiWvA64Q3DOWk3lD5OxX9N4G3CG9byrYc4qgIVc8bbUHMGfKzimnxnubP2P0SCjR30JzBfS9xBp5PCU4E8I1pr7A196+7DMrEH5ZVx/+MoLoOk8/lugXXKzNKfVeBf7P1qw2XNDZeTCQ+50XMqHG0JlgUW+MMHaegzCO4HoDYOJhvPPtf8Q1s/y7sl2uZ3BL3Uv8hKHO4QH8bnt/z5H3giYT8e1pjy0mDfZ0AdmMdnPjqQpmHCfkJ4asf52Ou/h7GZcE6sW50NDnFpQ6dflw7dDXlpc4FS7tLhnehfUT1n1XmlHNwf16wqA6dfjnXI1S7n8H98xJ7i5ahHl8U9T/6QL5/YZlDOPFPbV/O1vv/FfA18A25riq++QGffwL/ZPXr55zPcvTr7EaNb4HvgO/J9UM2ODZ+weffDN2632mKzpjnAuQ/3OdPOvYLxsEi+e6i0G6bG2z6Xd7VAbkOlnGg5sK+EvPLSnmO/S76q8o5dFyvatrQ5UKdLVXrr+r3BTqucTzPknbomHvgON508In/us7KVeDciXnlM7ADuaYL/OLYJ6hRt25YYMzhmKvpeTDeyz2ZaY8fAlL/ZZwPQHsPeMVDP0I39jjjn+69bWgr9AHqfyna++rp5xWRN9Sxt2Tpf6lf9j/ur49ng/t0vI6RNaDuDGjM8RyG6ufCNGdLjdvAlu3AHflGHi2b/9Q26DmT7j8arudQuv2Xa06S65ydYMsu4O588LkNZc+1ON+l638TbPtP9TwsFaj9r8uh+KzP8LvUr/Mh96y1bnBjhbO2CBn/FHsg9vcyxuQElJms+QyZYraiPt0HmvYH0HUB6pjK+fcN4mNYfotqYMYojLktnhzVjFtu+bJtNeAhxn9VGjRowEPsc9TYSO0/2yrqsitF/Sn3S0pI1U915f5U9Tdo0GBzo9n/DCf+AaC8eNwAQAAA
|
||||
</data>
|
||||
</layer>
|
||||
<layer name="3_roof" width="64" height="64">
|
||||
|
|
@ -209,7 +208,7 @@
|
|||
H4sIAAAAAAAAAO3ZSwrCMBSFYc1WVFDQFfjW+q5Dceb+tyA48IZS6MBIU01Ik/+DMxPqIaE3xk4HrmTKHG2rzIlB1rBHbP3vFn1uka3/TrI35GDoGVP/o+RkyJn+H9Hf7/d0Rfe/SHJDrgn0T3n+A0Bb5JW00T/mxlQVCVF1broyU0VQn8/zyrPr71m2mt53hGYuPRaSZSR9bK2k91qyqfTvyb7rSwYB7z+grlBnvC/MeAAIn+n3RSznbXxns/7lZ9kbadLr/1Du17/8nybVc3TZn3M0kB59H/ZyeB82lPfKKOF3y1i6TxLuDwAAAAAAAAD4zRs+ikUJAEAAAA==
|
||||
</data>
|
||||
</layer>
|
||||
<layer name="objects" width="64" height="64" visible="0">
|
||||
<layer name="objects" width="64" height="64">
|
||||
<data encoding="base64" compression="gzip">
|
||||
H4sIAAAAAAAAAO3ZQQ6CMBAFUMJFdaEmHsY7uzCEDSlBpmacvpd0SzrDp03pNAH/6jp/BuP2YcnAqPX3Mmo/ZQmgj/u8DgCIYE8hwlaOWtmSO8ijwv+bvRpeBWpsqfAOz9irv3p/WvX5RwsQ62JNJaEj5+vMZ/Fb4rnRT8V1NfN3BsA5FfctvveQByCZ5S4g651A1nn9wtZdzTOoH1HPyWL0nACwcgbv5w0mQTIgAEAAAA==
|
||||
</data>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue