Merged changes from original repo and modified some web port releated files
@@ -1,12 +1,5 @@
|
||||
# Shattered Pixel Dungeon
|
||||
|
||||
This is a fork of Shattered Pixel Dungeon that can also compile for the web. Inspired by [this](https://github.com/gnojus/pixel-dungeon-gdx) web port for the original Pixel Dungeon.
|
||||
|
||||
## Libraries Used
|
||||
This project uses the following libraries:
|
||||
- [TeaVM](https://teavm.org), licensed under the Apache License, Version 2.0.
|
||||
- [gdx-teavm](https://github.com/xpenatan/gdx-teavm), licensed under the Apache License, Version 2.0.
|
||||
|
||||
[Shattered Pixel Dungeon](https://shatteredpixel.com/shatteredpd/) is an open-source traditional roguelike dungeon crawler with randomized levels and enemies, and hundreds of items to collect and use. It's based on the [source code of Pixel Dungeon](https://github.com/00-Evan/pixel-dungeon-gradle), by [Watabou](https://watabou.itch.io/).
|
||||
|
||||
Shattered Pixel Dungeon currently compiles for Android, iOS, and Desktop platforms. You can find official releases of the game on:
|
||||
|
||||
@@ -24,8 +24,6 @@ package com.watabou.glwrap;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.watabou.utils.DeviceCompat;
|
||||
|
||||
public class Attribute {
|
||||
|
||||
@@ -38,22 +36,20 @@ public class Attribute {
|
||||
public int location() {
|
||||
return location;
|
||||
}
|
||||
|
||||
private static final GL20 activeGL = (DeviceCompat.isWeb()) ? Gdx.gl30 : Gdx.gl;
|
||||
|
||||
public void enable() {
|
||||
activeGL.glEnableVertexAttribArray( location );
|
||||
Gdx.gl.glEnableVertexAttribArray( location );
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
activeGL.glDisableVertexAttribArray( location );
|
||||
Gdx.gl.glDisableVertexAttribArray( location );
|
||||
}
|
||||
|
||||
public void vertexPointer( int size, int stride, FloatBuffer ptr ) {
|
||||
activeGL.glVertexAttribPointer( location, size, GL20.GL_FLOAT, false, stride * 4, ptr );
|
||||
Gdx.gl.glVertexAttribPointer( location, size, Gdx.gl.GL_FLOAT, false, stride * 4, ptr );
|
||||
}
|
||||
|
||||
public void vertexBuffer( int size, int stride, int offset) {
|
||||
activeGL.glVertexAttribPointer( location, size, GL20.GL_FLOAT, false, stride * 4, offset * 4 );
|
||||
Gdx.gl.glVertexAttribPointer(location, size, Gdx.gl.GL_FLOAT, false, stride * 4, offset * 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ package com.watabou.noosa;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.watabou.utils.DeviceCompat;
|
||||
import com.watabou.glscripts.Script;
|
||||
import com.watabou.glwrap.Attribute;
|
||||
import com.watabou.glwrap.Quad;
|
||||
@@ -36,9 +35,9 @@ import java.nio.FloatBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
public class NoosaScript extends Script {
|
||||
|
||||
private static final GL20 activeGL = (DeviceCompat.isWeb()) ? Gdx.gl30 : Gdx.gl20;
|
||||
|
||||
private static final GL20 activeGL = (DeviceCompat.isWeb()) ? Gdx.gl30 : Gdx.gl20;
|
||||
|
||||
public Uniform uCamera;
|
||||
public Uniform uModel;
|
||||
public Uniform uTex;
|
||||
@@ -65,13 +64,14 @@ public class NoosaScript extends Script {
|
||||
|
||||
Quad.setupIndices();
|
||||
Quad.bindIndices();
|
||||
|
||||
|
||||
if (DeviceCompat.isWeb()) {
|
||||
vertexBufferId = Gdx.gl30.glGenBuffer();
|
||||
Gdx.gl30.glBindBuffer(Gdx.gl30.GL_ARRAY_BUFFER, vertexBufferId);
|
||||
Gdx.gl30.glBufferData(Gdx.gl30.GL_ARRAY_BUFFER, 16384, null, Gdx.gl30.GL_DYNAMIC_DRAW);
|
||||
Gdx.gl30.glBindBuffer(Gdx.gl30.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,6 +85,7 @@ public class NoosaScript extends Script {
|
||||
}
|
||||
|
||||
public void drawElements( FloatBuffer vertices, ShortBuffer indices, int size ) {
|
||||
|
||||
if (DeviceCompat.isWeb()) {
|
||||
activeGL.glBindBuffer(GL20.GL_ARRAY_BUFFER, vertexBufferId);
|
||||
((Buffer)vertices).position( 0 );
|
||||
@@ -111,6 +112,7 @@ public class NoosaScript extends Script {
|
||||
}
|
||||
|
||||
public void drawQuad( FloatBuffer vertices ) {
|
||||
|
||||
if (DeviceCompat.isWeb()) {
|
||||
activeGL.glBindBuffer(GL20.GL_ARRAY_BUFFER, vertexBufferId);
|
||||
((Buffer)vertices).position( 0 );
|
||||
@@ -126,7 +128,7 @@ public class NoosaScript extends Script {
|
||||
|
||||
((Buffer)vertices).position( 2 );
|
||||
aUV.vertexPointer( 2, 4, vertices );
|
||||
|
||||
|
||||
Gdx.gl20.glDrawElements( Gdx.gl20.GL_TRIANGLES, Quad.SIZE, Gdx.gl20.GL_UNSIGNED_SHORT, 0 );
|
||||
}
|
||||
}
|
||||
@@ -142,7 +144,7 @@ public class NoosaScript extends Script {
|
||||
|
||||
buffer.release();
|
||||
|
||||
activeGL.glDrawElements( GL20.GL_TRIANGLES, Quad.SIZE, GL20.GL_UNSIGNED_SHORT, 0 );
|
||||
Gdx.gl20.glDrawElements( Gdx.gl20.GL_TRIANGLES, Quad.SIZE, Gdx.gl20.GL_UNSIGNED_SHORT, 0 );
|
||||
}
|
||||
|
||||
public void drawQuadSet( FloatBuffer vertices, int size ) {
|
||||
@@ -168,9 +170,10 @@ public class NoosaScript extends Script {
|
||||
|
||||
((Buffer)vertices).position( 2 );
|
||||
aUV.vertexPointer( 2, 4, vertices );
|
||||
|
||||
|
||||
Gdx.gl20.glDrawElements( Gdx.gl20.GL_TRIANGLES, Quad.SIZE * size, Gdx.gl20.GL_UNSIGNED_SHORT, 0 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void drawQuadSet( Vertexbuffer buffer, int length, int offset ){
|
||||
@@ -188,7 +191,7 @@ public class NoosaScript extends Script {
|
||||
|
||||
buffer.release();
|
||||
|
||||
activeGL.glDrawElements( GL20.GL_TRIANGLES, Quad.SIZE * length, GL20.GL_UNSIGNED_SHORT, Quad.SIZE * Short.SIZE/8 * offset );
|
||||
Gdx.gl20.glDrawElements( Gdx.gl20.GL_TRIANGLES, Quad.SIZE * length, Gdx.gl20.GL_UNSIGNED_SHORT, Quad.SIZE * Short.SIZE/8 * offset );
|
||||
}
|
||||
|
||||
public void lighting( float rm, float gm, float bm, float am, float ra, float ga, float ba, float aa ) {
|
||||
@@ -209,7 +212,7 @@ public class NoosaScript extends Script {
|
||||
uCamera.valueM4( camera.matrix );
|
||||
|
||||
if (!camera.fullScreen) {
|
||||
activeGL.glEnable( GL20.GL_SCISSOR_TEST );
|
||||
Gdx.gl20.glEnable( Gdx.gl20.GL_SCISSOR_TEST );
|
||||
|
||||
//This fixes pixel scaling issues on some hidpi displays (mainly on macOS)
|
||||
// because for some reason all other openGL operations work on virtual pixels
|
||||
@@ -217,13 +220,13 @@ public class NoosaScript extends Script {
|
||||
float xScale = DeviceCompat.getRealPixelScaleX();
|
||||
float yScale = DeviceCompat.getRealPixelScaleY();
|
||||
|
||||
activeGL.glScissor(
|
||||
Gdx.gl20.glScissor(
|
||||
Math.round(camera.x * xScale),
|
||||
Math.round((Game.height - camera.screenHeight - camera.y) * yScale),
|
||||
Math.round(camera.screenWidth * xScale),
|
||||
Math.round(camera.screenHeight * yScale));
|
||||
} else {
|
||||
activeGL.glDisable( GL20.GL_SCISSOR_TEST );
|
||||
Gdx.gl20.glDisable( Gdx.gl20.GL_SCISSOR_TEST );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,41 +240,41 @@ public class NoosaScript extends Script {
|
||||
return SHADER;
|
||||
}
|
||||
|
||||
private static String SHADER;
|
||||
private static final String SHADER;
|
||||
static {
|
||||
if (DeviceCompat.isWeb()) {
|
||||
SHADER =
|
||||
//vertex shader
|
||||
"#version 300 es\n" +
|
||||
"uniform mat4 uCamera;\n" +
|
||||
"uniform mat4 uModel;\n" +
|
||||
"in vec4 aXYZW;\n" +
|
||||
"in vec2 aUV;\n" +
|
||||
"out vec2 vUV;\n" +
|
||||
"void main() {\n" +
|
||||
" gl_Position = uCamera * uModel * aXYZW;\n" +
|
||||
" vUV = aUV;\n" +
|
||||
"}\n" +
|
||||
if (DeviceCompat.isWeb()) {
|
||||
SHADER =
|
||||
//vertex shader
|
||||
"#version 300 es\n" +
|
||||
"uniform mat4 uCamera;\n" +
|
||||
"uniform mat4 uModel;\n" +
|
||||
"in vec4 aXYZW;\n" +
|
||||
"in vec2 aUV;\n" +
|
||||
"out vec2 vUV;\n" +
|
||||
"void main() {\n" +
|
||||
" gl_Position = uCamera * uModel * aXYZW;\n" +
|
||||
" vUV = aUV;\n" +
|
||||
"}\n" +
|
||||
|
||||
//this symbol separates the vertex and fragment shaders (see Script.compile)
|
||||
"//\n" +
|
||||
|
||||
//fragment shader
|
||||
//preprocessor directives let us define precision on GLES platforms, and ignore it elsewhere
|
||||
//this symbol separates the vertex and fragment shaders (see Script.compile)
|
||||
"//\n" +
|
||||
|
||||
//fragment shader
|
||||
//preprocessor directives let us define precision on GLES platforms, and ignore it elsewhere
|
||||
"#version 300 es\n" +
|
||||
"#ifdef GL_ES\n" +
|
||||
" precision mediump float;\n" +
|
||||
"#endif\n" +
|
||||
"in vec2 vUV;\n" +
|
||||
"uniform sampler2D uTex;\n" +
|
||||
"uniform vec4 uColorM;\n" +
|
||||
"uniform vec4 uColorA;\n" +
|
||||
"#ifdef GL_ES\n" +
|
||||
" precision mediump float;\n" +
|
||||
"#endif\n" +
|
||||
"in vec2 vUV;\n" +
|
||||
"uniform sampler2D uTex;\n" +
|
||||
"uniform vec4 uColorM;\n" +
|
||||
"uniform vec4 uColorA;\n" +
|
||||
"out vec4 fragColor;\n" +
|
||||
"void main() {\n" +
|
||||
" fragColor = texture( uTex, vUV ) * uColorM + uColorA;\n" +
|
||||
"}\n";
|
||||
} else {
|
||||
SHADER =
|
||||
"void main() {\n" +
|
||||
" fragColor = texture( uTex, vUV ) * uColorM + uColorA;\n" +
|
||||
"}\n";
|
||||
} else {
|
||||
SHADER =
|
||||
//vertex shader
|
||||
"uniform mat4 uCamera;\n" +
|
||||
"uniform mat4 uModel;\n" +
|
||||
@@ -298,6 +301,6 @@ public class NoosaScript extends Script {
|
||||
"void main() {\n" +
|
||||
" gl_FragColor = texture2D( uTex, vUV ) * uColorM + uColorA;\n" +
|
||||
"}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,40 +45,40 @@ public class NoosaScriptNoLighting extends NoosaScript {
|
||||
return SHADER;
|
||||
}
|
||||
|
||||
private static String SHADER;
|
||||
|
||||
private static final String SHADER;
|
||||
|
||||
static {
|
||||
if (DeviceCompat.isWeb()) {
|
||||
SHADER =
|
||||
//vertex shader
|
||||
if (DeviceCompat.isWeb()) {
|
||||
SHADER =
|
||||
//vertex shader
|
||||
"#version 300 es\n" +
|
||||
"uniform mat4 uCamera;\n" +
|
||||
"uniform mat4 uModel;\n" +
|
||||
"in vec4 aXYZW;\n" +
|
||||
"in vec2 aUV;\n" +
|
||||
"out vec2 vUV;\n" +
|
||||
"void main() {\n" +
|
||||
" gl_Position = uCamera * uModel * aXYZW;\n" +
|
||||
" vUV = aUV;\n" +
|
||||
"}\n" +
|
||||
"uniform mat4 uCamera;\n" +
|
||||
"uniform mat4 uModel;\n" +
|
||||
"in vec4 aXYZW;\n" +
|
||||
"in vec2 aUV;\n" +
|
||||
"out vec2 vUV;\n" +
|
||||
"void main() {\n" +
|
||||
" gl_Position = uCamera * uModel * aXYZW;\n" +
|
||||
" vUV = aUV;\n" +
|
||||
"}\n" +
|
||||
|
||||
//this symbol separates the vertex and fragment shaders (see Script.compile)
|
||||
"//\n" +
|
||||
//this symbol separates the vertex and fragment shaders (see Script.compile)
|
||||
"//\n" +
|
||||
|
||||
//fragment shader
|
||||
//preprocessor directives let us define precision on GLES platforms, and ignore it elsewhere
|
||||
//fragment shader
|
||||
//preprocessor directives let us define precision on GLES platforms, and ignore $
|
||||
"#version 300 es\n" +
|
||||
"#ifdef GL_ES\n" +
|
||||
" precision mediump float;\n" +
|
||||
"#endif\n" +
|
||||
"in vec2 vUV;\n" +
|
||||
"uniform sampler2D uTex;\n" +
|
||||
"#ifdef GL_ES\n" +
|
||||
" precision mediump float;\n" +
|
||||
"#endif\n" +
|
||||
"in vec2 vUV;\n" +
|
||||
"uniform sampler2D uTex;\n" +
|
||||
"out vec4 fragColor;\n" +
|
||||
"void main() {\n" +
|
||||
" fragColor = texture( uTex, vUV );\n" +
|
||||
"}\n";
|
||||
} else {
|
||||
SHADER =
|
||||
"void main() {\n" +
|
||||
" fragColor = texture( uTex, vUV );\n" +
|
||||
"}\n";
|
||||
} else {
|
||||
SHADER =
|
||||
//vertex shader
|
||||
"uniform mat4 uCamera;\n" +
|
||||
"uniform mat4 uModel;\n" +
|
||||
|
||||
@@ -79,7 +79,7 @@ public class RenderedText extends Image {
|
||||
private static final ArrayList<Character> alreadyReported = new ArrayList<>();
|
||||
|
||||
private synchronized void measure(){
|
||||
|
||||
|
||||
if (!DeviceCompat.isWeb()) {
|
||||
if (Thread.currentThread().getName().equals("SHPD Actor Thread")){
|
||||
throw new RuntimeException("Text measured from the actor thread!");
|
||||
|
||||
@@ -198,6 +198,7 @@ public class Tilemap extends Visual {
|
||||
public void draw() {
|
||||
|
||||
super.draw();
|
||||
|
||||
if (DeviceCompat.isWeb()) {
|
||||
fullUpdate = true;
|
||||
}
|
||||
|
||||
@@ -487,13 +487,13 @@ public class Bundle {
|
||||
//useful to turn this off for save data debugging.
|
||||
private static final boolean compressByDefault;
|
||||
|
||||
static {
|
||||
if (DeviceCompat.isWeb()) {
|
||||
compressByDefault = false;
|
||||
} else {
|
||||
compressByDefault = true;
|
||||
}
|
||||
}
|
||||
static {
|
||||
if (DeviceCompat.isWeb()) {
|
||||
compressByDefault = false;
|
||||
} else {
|
||||
compressByDefault = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int GZIP_BUFFER = 1024*4; //4 kb
|
||||
|
||||
|
||||
@@ -50,9 +50,9 @@ public class DeviceCompat {
|
||||
Gdx.app.getType() == Application.ApplicationType.WebGL;
|
||||
}
|
||||
|
||||
public static boolean isWeb(){
|
||||
public static boolean isWeb(){
|
||||
return Gdx.app.getType() == Application.ApplicationType.WebGL;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasHardKeyboard(){
|
||||
return Gdx.input.isPeripheralAvailable(Input.Peripheral.HardwareKeyboard);
|
||||
|
||||
@@ -53,9 +53,9 @@ public abstract class PlatformSupport {
|
||||
);
|
||||
}
|
||||
|
||||
//returns a display cutout (if one is present) in device pixels, or null is none is present
|
||||
//returns a display cutout (if one is present) in device pixels, or empty if none is present
|
||||
public RectF getDisplayCutout(){
|
||||
return null;
|
||||
return new RectF();
|
||||
}
|
||||
|
||||
public abstract void updateSystemUI();
|
||||
|
||||
@@ -66,6 +66,35 @@ public class AndroidPlatformSupport extends PlatformSupport {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RectF getDisplayCutout() {
|
||||
RectF cutoutRect = new RectF();
|
||||
|
||||
//some extra logic here is because cutouts can apparently be returned inverted
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
DisplayCutout cutout = AndroidLauncher.instance.getApplicationWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
|
||||
|
||||
Rect largest = null;
|
||||
if (cutout != null) {
|
||||
for (Rect r : cutout.getBoundingRects()) {
|
||||
if (largest == null
|
||||
|| Math.abs(r.height() * r.width()) > Math.abs(largest.height() * largest.width())) {
|
||||
largest = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (largest != null){
|
||||
cutoutRect.left = Math.min(largest.left, largest.right);
|
||||
cutoutRect.right = Math.max(largest.left, largest.right);
|
||||
cutoutRect.top = Math.min(largest.top, largest.bottom);
|
||||
cutoutRect.bottom = Math.max(largest.top, largest.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
return cutoutRect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RectF getSafeInsets( int level ) {
|
||||
RectF insets = new RectF();
|
||||
@@ -90,9 +119,12 @@ public class AndroidPlatformSupport extends PlatformSupport {
|
||||
boolean largeCutout = false;
|
||||
int screenSize = Game.width * Game.height;
|
||||
for (Rect r : cutout.getBoundingRects()){
|
||||
//use abs as some cutouts can apparently be returned inverted
|
||||
int cutoutSize = Math.abs(r.height() * r.width());
|
||||
//display cutouts are considered large if they take up more than 0.5% of the screen
|
||||
if (cutoutSize*200 >= screenSize){
|
||||
//display cutouts are considered large if they take up more than 0.667% of the screen
|
||||
//in reality we want less than about 0.5%, but some cutouts over-report their size
|
||||
//Pixel devices especially =S
|
||||
if (cutoutSize*150 >= screenSize){
|
||||
largeCutout = true;
|
||||
}
|
||||
}
|
||||
|
||||
10
build.gradle
@@ -14,8 +14,8 @@ allprojects {
|
||||
appName = 'Shattered Pixel Dungeon'
|
||||
appPackageName = 'com.shatteredpixel.shatteredpixeldungeon'
|
||||
|
||||
appVersionCode = 868
|
||||
appVersionName = '3.2.3'
|
||||
appVersionCode = 875
|
||||
appVersionName = '3.2.4'
|
||||
|
||||
appJavaCompatibility = JavaVersion.VERSION_11
|
||||
|
||||
@@ -23,12 +23,12 @@ allprojects {
|
||||
appAndroidMinSDK = 21 //Android 5.0
|
||||
appAndroidTargetSDK = 35 //Android 15
|
||||
|
||||
gdxVersion = '1.13.5'
|
||||
gdxVersion = '1.13.6-SNAPSHOT' //using snapshot as 1.13.5 has issues with Android R8
|
||||
gdxControllersVersion = '2.2.4'
|
||||
robovmVersion = '2.3.23'
|
||||
|
||||
gdxTeaVMVersion = '1.2.4'
|
||||
teaVMVersion = '0.12.0'
|
||||
teaVMVersion = '0.12.3'
|
||||
}
|
||||
version = appVersionName
|
||||
|
||||
@@ -38,4 +38,4 @@ allprojects {
|
||||
maven { url 'https://central.sonatype.com/repository/maven-snapshots/' }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 152 B After Width: | Height: | Size: 242 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 5.2 KiB |
@@ -353,7 +353,7 @@ actors.buffs.physicalempower.name=fysiek versterkt
|
||||
actors.buffs.physicalempower.desc=Je slagen zijn versterkt, waardoor je meer schade kunt aanrichten met fysieke aanvallen.\n\nBonusschade: %1$d.\nResterende treffers: %2$d.
|
||||
|
||||
actors.buffs.pincushion.name=speldenkussen
|
||||
actors.buffs.pincushion.desc=De werpwapens die je naar dit personage heb gegooid zijn op het moment aan hen vastgeplakt en valt op de grond nadat het karakter is verslagen.\n\nDe volgende voorwerpen zitten op het moment vast:
|
||||
actors.buffs.pincushion.desc=De werpwapens die je naar dit personage heb gegooid zijn op het moment aan hen vastgeplakt en vallen op de grond nadat het karakter is verslagen.\n\nDe volgende voorwerpen zitten op het moment vast:
|
||||
|
||||
actors.buffs.poison.name=vergiftigd
|
||||
actors.buffs.poison.heromsg=Je bent vergiftigd!
|
||||
|
||||
@@ -422,21 +422,21 @@ actors.buffs.terror.desc=Terör, hedefini kontrol dışı bir paniğe sokan mani
|
||||
actors.buffs.toxicimbue.name=zehirle yoğrulmuş
|
||||
actors.buffs.toxicimbue.desc=Zehirli enerjiyle dolusun!\n\nEtrafınızda hareket ettikçe zehirli gaz sürekli olarak sizden yayılacak ve düşmanlarınıza zarar verecek. Etki süresi boyunca ve sonrasında birkaç tur boyunca zehirli gaza ve zehre karşı bağışıklığınız vardır.\n\nKalan zehir enerjisi tur sayısı: %s.
|
||||
|
||||
actors.buffs.corrosion.name=aşındırma
|
||||
actors.buffs.corrosion.name=Aşınma
|
||||
actors.buffs.corrosion.heromsg=Eriyorsun!
|
||||
actors.buffs.corrosion.ondeath=Eriyip gittin...
|
||||
actors.buffs.corrosion.rankings_desc=Eridi
|
||||
actors.buffs.corrosion.desc=Güçlü asit eti, metali ve kemiği endişelendirici bir hızda eritir.\n\nAşındırma hasarı hedef erimeye devam ettikçe zamanla artar.\n\nAşındırmanın geçmesine kalan sıra: %1$s.\nŞu anki aşınma hasarı:%2$d.
|
||||
|
||||
actors.buffs.vertigo.name=baş dönmesi
|
||||
actors.buffs.vertigo.name=Baş Dönmesi
|
||||
actors.buffs.vertigo.desc=Düz bir yolda yürümek tüm dünya dönerken zor olabilir.\n\nBaş dönmesinin etkisi altında olan bir karakter gitmeye çalıştığı yer yerine rastgele başka bir yere gidecektir.\n\nBaş dönmesinin geçmesine kalan sıra: %s.
|
||||
|
||||
actors.buffs.wandempower.name=asalar güçlendi
|
||||
actors.buffs.wandempower.name=Asalsr Güçlendi
|
||||
actors.buffs.wandempower.desc=Hasar veren asaların güçlendirildi, bir kaç vuruş için verilen hasar miktarı arttı.\n\nEk hasar:%1$d.\nKalan vuruşlar:%2$d.
|
||||
|
||||
actors.buffs.weakness.name=Güçsüzleşmiş
|
||||
actors.buffs.weakness.heromsg=Zayıflamış hissediyorsun!
|
||||
actors.buffs.weakness.desc=Her şey bir anda daha ağır hissettirmeye başladı.\n\nZayıflatma büyüsü karakterin fiziksel gücünü azaltır, ve 33%% daha az hasar vermelerine sebep olur.\n\nzayıflamanın geçmesine kalan tur.%s.
|
||||
actors.buffs.weakness.desc=Her şey bir anda daha ağır hissettirmeye başladı.\n\nGüçsüzleşme büyüsü karakterin fiziksel gücünü azaltır, ve 33%% daha az hasar vermelerine sebep olur.\n\nGüçsüzleşmenin geçmesine kalan tur.%s.
|
||||
|
||||
actors.buffs.wellfed.name=tok
|
||||
actors.buffs.wellfed.desc=Gayet tatmin olmuş ve tok hissediyorsun.\n\nİyi beslenmişken açlığın artmaz, ve bunun yanında ekstra bir miktar sağlığa sahip olursun.\n\nKalan sıra: %d.
|
||||
@@ -464,7 +464,7 @@ actors.hero.abilities.warrior.endure$enduretracker.desc=Savaşçı şu an katlan
|
||||
actors.hero.abilities.warrior.endure.short_desc=Savaşçı _Katlanırken_, birkaç tur atlar ama fazladan hasar direnci kazanır. Sonra katlandığı hasara göre fazladan hasar verir.
|
||||
actors.hero.abilities.warrior.endure.desc=Savaşçı, tüm kaynaklardan yarı hasar alarak 3 tur katlanır. Bu azalma, zırh gibi hasara dayanıklı etkilerden önce uygulanır.\n\nKatlandıktan sonra, savaşçının 10 sıra içindeki bir sonraki vuruşu ilave hasar kazanır. Bu fazlandan hasar, herhangi bir hasar azaltma etkisinden önce, ona verilen tüm hasarın yarısına eşittir!\n\nSavaşçının herhangi bir kombosu varsa, bu yeteneği kullanmak kombonun kalan süresini 3 tur artırır.
|
||||
|
||||
actors.hero.abilities.mage.elementalblast.name=elemental patlama
|
||||
actors.hero.abilities.mage.elementalblast.name=Elemental Patlama
|
||||
actors.hero.abilities.mage.elementalblast.no_staff=Bu yeteneği asanız olmadan kullanamazsınız.
|
||||
actors.hero.abilities.mage.elementalblast.short_desc=Büyücü asasından bir _Elemental Patlama_ yayar, değneğindeki asaya bağlı olarak değişen bir etkiyle etrafındaki geniş bir alanı kaplar.
|
||||
actors.hero.abilities.mage.elementalblast.desc=Büyücü, en fazla 4 kare uzağa kadar dairesel bir bölgeyi kaplayan ve 15-25'e kadar hasar veren bir büyü patlaması yayar. Elemental patlamanın özel etkisi, Büyücünün değneğine yerleştirilen asaya göre değişecektir.
|
||||
@@ -473,36 +473,36 @@ actors.hero.abilities.mage.wildmagic.name=vahşi büyü
|
||||
actors.hero.abilities.mage.wildmagic.no_wands=Saldıracak bir asanız yok!
|
||||
actors.hero.abilities.mage.wildmagic.short_desc=Büyücü, asalarında bulunan _Vahşi Büyü_'yü serbest bırakır ve bunları tek bir tur boyunca seçilen bir hedefe gelişigüzel olarak birden çok kez ateşler.
|
||||
actors.hero.abilities.mage.wildmagic.desc=Büyücü asalarındaki gücü açığa çıkarır ve tek bir turda 4 defaya kadar gelişigüzel ateşler. Büyücünün asası bu etkiye dahil değildir.\n\nAyrıca her asanın seviyesi 2 artırılarak +3'e çıkarılır. Bu yetenek her bir vuruş için yarım asa şarjı kullanır ve hiçbir asa 2 kezden fazla ateşlenemez.
|
||||
actors.hero.abilities.mage.warpbeacon.name=ışınlanma feneri
|
||||
actors.hero.abilities.mage.warpbeacon.depths=Farklı katlar arasında ışınlanamazsın!
|
||||
actors.hero.abilities.mage.warpbeacon.name=Işınlanma Feneri
|
||||
actors.hero.abilities.mage.warpbeacon.depths=Katlar arası ışınlanamazsın!
|
||||
actors.hero.abilities.mage.warpbeacon.locked_floor=Kilitli bir katı terk edemezsin!
|
||||
actors.hero.abilities.mage.warpbeacon.too_far=O konum çok uzakta!
|
||||
actors.hero.abilities.mage.warpbeacon.invalid_beacon=Oraya bir fener koyamazsın!
|
||||
actors.hero.abilities.mage.warpbeacon.window_desc=Fenerin şu anda kat %d de yerleştirilmiş.
|
||||
actors.hero.abilities.mage.warpbeacon.window_tele=fenere ışınlan
|
||||
actors.hero.abilities.mage.warpbeacon.window_clear=feneri sıfırla
|
||||
actors.hero.abilities.mage.warpbeacon.window_cancel=iptal et
|
||||
actors.hero.abilities.mage.warpbeacon.window_desc=Fenerin şu anda kat %d'de yerleştirilmiş.
|
||||
actors.hero.abilities.mage.warpbeacon.window_tele=Fenere Işınlan
|
||||
actors.hero.abilities.mage.warpbeacon.window_clear=Feneri Sıfırla
|
||||
actors.hero.abilities.mage.warpbeacon.window_cancel=İptal Et
|
||||
actors.hero.abilities.mage.warpbeacon.short_desc=Büyücü mevcut konumuna bir _Işınlanma Feneri_ koyar, daha sonra ona anında ışınlanabilir.
|
||||
actors.hero.abilities.mage.warpbeacon.desc=Büyücü, istediği zaman ışınlanabileceği bir fener yerleştirir. Feneri yerleştirmek 1 tur alır, ama ışınlanmak anında gerçekleşir.\n\nBüyücü varsayılan olarak katlar arası ışınlanamaz, veya feneri ulaşılamaz yerlere, mesela kilitli odalara, ulaşmak için kullanamaz. Büyücü düşmanların içine ışınlanabilir, bu, düşmanları yana itecektir.
|
||||
|
||||
actors.hero.abilities.rogue.smokebomb.name=sis bombası
|
||||
actors.hero.abilities.rogue.smokebomb.name=Sis Bombası
|
||||
actors.hero.abilities.rogue.smokebomb.fov=Sadece görüş alanındaki boş bir bölgeye atlayabilirsin.
|
||||
actors.hero.abilities.rogue.smokebomb.prompt=Atlayacağın yeri seç
|
||||
actors.hero.abilities.rogue.smokebomb$ninjalog.name=tahta kukla
|
||||
actors.hero.abilities.rogue.smokebomb$ninjalog.name=Tahta Kukla
|
||||
actors.hero.abilities.rogue.smokebomb$ninjalog.desc=Her nasılsa, düşmanlar bu tahta kuklanın gerçek Haydut olduğuna kolayca ikna oluyor!
|
||||
actors.hero.abilities.rogue.smokebomb$ninjalog.discover_hint=Bu karakteri, belirli bir kahraman zırh yeteneğiyle bulabilirsin.
|
||||
actors.hero.abilities.rogue.smokebomb.short_desc=Haydut, bir _Sis Bombası_ atar ve bu sırada anında uzağa atılır. Eski yerine yakın düşmanları kör eder.
|
||||
actors.hero.abilities.rogue.smokebomb.desc=Haydut bir sis bombası atar ve 6 kare uzaklığa kadar anında atılır. Tehlikelerin ve düşmanların içinden atılabilir, ama duvar gibi sağlam arazinin içinden geçemez\n\nHaydut'un eski konumuna yakın olan düşmanlar 5 tur boyunca kör olur.
|
||||
actors.hero.abilities.rogue.deathmark.name=ölüm işareti
|
||||
actors.hero.abilities.rogue.deathmark.ally_target=Sadece düşmanları işaretleyebilirsin
|
||||
actors.hero.abilities.rogue.deathmark.name=Ölüm Damgası
|
||||
actors.hero.abilities.rogue.deathmark.ally_target=Sadece düşmanları damgalayabilirsin
|
||||
actors.hero.abilities.rogue.deathmark.short_desc=Haydut, seçilen bir düşmanın üzerine bir _Ölüm İşareti_ koyar. İşaretlenen düşmanlar ilave hasar alır, ama işaret sonlanana kadar ölemezler.
|
||||
actors.hero.abilities.rogue.deathmark.desc=Haydut, seçilen bir düşmanın üzerine bir işaret koyar, onun %25 ilave hasar almasını sağlar. İşaret anında uygulanır ve 5 tur boyunca sürer.\n\nİşaretlenen düşmanlar ilave hasar alır ama işaret sona erene kadar ölemezler. Eğer bir düşman 0 HP iken işaret sona ererse, düşman anında ölür.
|
||||
actors.hero.abilities.rogue.deathmark$deathmarktracker.name=ölümle işaretlenmiş
|
||||
actors.hero.abilities.rogue.deathmark$deathmarktracker.desc=Bu düşman işaretlenmiş, düşmanın 25%% ilave hasar almasına sebep olur, ama işaret sona erene kadar ölemez.\n\nKalan tur: %s
|
||||
actors.hero.abilities.rogue.shadowclone.name=gölge klon
|
||||
actors.hero.abilities.rogue.deathmark.desc=Haydut, seçilen bir düşmanın üzerine bir damga koyar, onun %25 ilave hasar almasını sağlar. damga anında koyulur ve 5 tur sürer.\n\nDamgalanan düşmanlar ilave hasar alır ancak damga sona erene kadar ölemezler. Eğer bir düşman 0 SP'de iken damga sona ererse anında ölür.
|
||||
actors.hero.abilities.rogue.deathmark$deathmarktracker.name=Ölüm Damgalı
|
||||
actors.hero.abilities.rogue.deathmark$deathmarktracker.desc=Bu düşman damgalı. Bu, düşmanın 25%% ilave hasar almasına sebep olur, ama damga sona erene kadar ölemez.\n\nKalan tur: %s
|
||||
actors.hero.abilities.rogue.shadowclone.name=Gölge Klon
|
||||
actors.hero.abilities.rogue.shadowclone.short_desc=Haydut, savaşta kendisine yardım etmesi için yönlendirilebilen bir _Gölge Klon_ çağırır.
|
||||
actors.hero.abilities.rogue.shadowclone.desc=Haydut, savaşta kendisine yardım etmesi için yönlendirilebilen, kendisinin gölgeli bir taklidini çağırır. Gölge klonu yönlendirmek yük gerektirmez\n\nKlonun 80 HP'si vardır, zırhı yoktur ve 10-20 hasar verir. Tüm bu özellikler yeteneklerle geliştirilebilir, böylece klon kahramanın sahip olduğu zırh ve silahlardan faydalanabilir.
|
||||
actors.hero.abilities.rogue.shadowclone$shadowally.name=gölge haydut
|
||||
actors.hero.abilities.rogue.shadowclone$shadowally.name=Gölge Haydudu
|
||||
actors.hero.abilities.rogue.shadowclone$shadowally.direct_defend=Klonun bu konuma gidiyor.
|
||||
actors.hero.abilities.rogue.shadowclone$shadowally.direct_follow=Klonun seni takip ediyor.
|
||||
actors.hero.abilities.rogue.shadowclone$shadowally.direct_attack=Klonun saldırmak için hareket ediyor!
|
||||
|
||||
@@ -1416,7 +1416,7 @@ items.trinkets.eyeofnewt.typical_stats_desc=Typically this trinket will reduce y
|
||||
items.trinkets.eyeofnewt.stats_desc=At its current level this trinket will reduce your vision range by _%1$s%%_, but will also grant you mind vision on enemies within _%2$d_ tiles.
|
||||
|
||||
items.trinkets.ferrettuft.name=ferret tuft
|
||||
items.trinkets.ferrettuft.desc=A tuft of silky white ferret fur, held together by a lime green ribbon tied into a bow. Ferrets are known for their agility, mischievousness, and lack of integrity. Some of that power seems to radiate from the trinket, enhancing the evasion of anything nearby.
|
||||
items.trinkets.ferrettuft.desc=A tuft of silky white ferret fur, held together by a Lime green ribbon tied into a bow. Ferrets are known for their agility, mischievousness, and lack of integrity. Some of that power seems to radiate from the trinket, enhancing the evasion of anything nearby.
|
||||
items.trinkets.ferrettuft.typical_stats_desc=Typically this trinket will increase the evasion of all characters by _%1$s%%_.
|
||||
items.trinkets.ferrettuft.stats_desc=At its current level this trinket will increase the evasion of all characters by _%1$s%%_.
|
||||
|
||||
|
||||
@@ -2201,11 +2201,11 @@ items.weapon.missiles.shuriken$shurikeninstanttracker.name=Rechargement des shur
|
||||
items.weapon.missiles.shuriken$shurikeninstanttracker.desc=Vous avez récemment lancé instantanément un shuriken, et devez attendre avant de pouvoir recommencer. Vous pouvez toujours lancer des shurikens, mais à une vitesse normale.\n\nTours restants : %s.
|
||||
|
||||
items.weapon.missiles.throwingclub.name=bâton de lancer
|
||||
items.weapon.missiles.throwingclub.stats_desc=
|
||||
items.weapon.missiles.throwingclub.stats_desc=Cette arme ne s'accroche pas aux ennemis et peut être ramassée instantanément.
|
||||
items.weapon.missiles.throwingclub.desc=Une arme de jet assez simple mais robuste, grossièrement une pierre attachée à un bâton.
|
||||
|
||||
items.weapon.missiles.throwinghammer.name=marteau de lancer
|
||||
items.weapon.missiles.throwinghammer.stats_desc=
|
||||
items.weapon.missiles.throwinghammer.stats_desc=Cette arme ne s'accroche pas aux ennemis et peut être ramassée instantanément.
|
||||
items.weapon.missiles.throwinghammer.desc=Ces lourds marteaux ont été créés pour être lancés sur les ennemis. Leur corps lisse entièrement métallique leur confère une grande durabilité.
|
||||
|
||||
items.weapon.missiles.throwingknife.name=couteau de lancer
|
||||
|
||||
@@ -558,16 +558,16 @@ items.food.blandfruit.name=flauwfruit
|
||||
items.food.blandfruit.cooked=gekookt flauwfruit
|
||||
items.food.blandfruit.sunfruit=zonnefruit
|
||||
items.food.blandfruit.rotfruit=rotfruit
|
||||
items.food.blandfruit.earthfruit=aardefruit
|
||||
items.food.blandfruit.earthfruit=aardfruit
|
||||
items.food.blandfruit.blindfruit=blindfruit
|
||||
items.food.blandfruit.firefruit=vuurfruit
|
||||
items.food.blandfruit.icefruit=ijsfruit
|
||||
items.food.blandfruit.fadefruit=verdwijnfruit
|
||||
items.food.blandfruit.sorrowfruit=zieligfruit
|
||||
items.food.blandfruit.fadefruit=vaagfruit
|
||||
items.food.blandfruit.sorrowfruit=treurfruit
|
||||
items.food.blandfruit.stormfruit=stormfruit
|
||||
items.food.blandfruit.dreamfruit=magefruit
|
||||
items.food.blandfruit.starfruit=sterrenfruit
|
||||
items.food.blandfruit.swiftfruit=snelfruit
|
||||
items.food.blandfruit.swiftfruit=fluksfruit
|
||||
items.food.blandfruit.raw=Je durft het niet rauw te eten.
|
||||
items.food.blandfruit.desc=Het is nu zo droog en onbeduidend, dat het misschien zou verbeteren door het te stoven met een ander ingrediënt.
|
||||
items.food.blandfruit.desc_cooked=De vrucht is tijdens het koken in de ketel opgezwollen en heeft zelfs de eigenschappen van het zaad waarmee hij gekookt was geabsorbeerd. Het heeft het effect van de toverdrank waarmee het zaad overeenkwam.
|
||||
@@ -2201,11 +2201,11 @@ items.weapon.missiles.shuriken$shurikeninstanttracker.name=Werpster afkoelperiod
|
||||
items.weapon.missiles.shuriken$shurikeninstanttracker.desc=Je hebt onlangs direct een werpster gegooid en moet wachten voordat je dit opnieuw doet. Werpsterren kunnen nog steeds worden gegooid, maar dan met normale wapensnelheid. \n\nResterende beurten: %s.
|
||||
|
||||
items.weapon.missiles.throwingclub.name=werpknuppel
|
||||
items.weapon.missiles.throwingclub.stats_desc=
|
||||
items.weapon.missiles.throwingclub.stats_desc=Dit wapen blijft niet aan vijanden vastzitten en kan direct verzameld worden.
|
||||
items.weapon.missiles.throwingclub.desc=Een vrij eenvoudig maar duurzaam werpwapen, in feite een grote steen bevestigd aan een stok.
|
||||
|
||||
items.weapon.missiles.throwinghammer.name=werphamer
|
||||
items.weapon.missiles.throwinghammer.stats_desc=
|
||||
items.weapon.missiles.throwinghammer.stats_desc=Dit wapen blijft niet aan vijanden vastzitten en kan direct verzameld worden.
|
||||
items.weapon.missiles.throwinghammer.desc=Deze forse hamers zijn ontworpen om naar een vijand te gooien. Dankzij hun soepele, volledig metalen constructie zijn ze zeer duurzaam.
|
||||
|
||||
items.weapon.missiles.throwingknife.name=werpmes
|
||||
|
||||
@@ -199,7 +199,7 @@ items.artifacts.cloakofshadows.name=áo choàng bóng tối
|
||||
items.artifacts.cloakofshadows.ac_stealth=TÀNG HÌNH
|
||||
items.artifacts.cloakofshadows.cooldown=Áo choàng của bạn cần %d lượt nữa để nạp lại năng lượng.
|
||||
items.artifacts.cloakofshadows.cursed=Bạn không thể sử dụng một cái áo choàng bị nguyền rủa.
|
||||
items.artifacts.cloakofshadows.no_charge=Áo choàng của bạn chưa sạc lại đủ để trở nên dùng được.
|
||||
items.artifacts.cloakofshadows.no_charge=Áo choàng của bạn chưa hồi đủ năng lượng để có thể sử dụng được.
|
||||
items.artifacts.cloakofshadows.desc=Một chiếc áo choàng thần kì vô giá, được lấy trộm từ kho vũ khí hoàng gia nhiều năm trước bởi Kẻ cướp. Khi được mặc, nó có thể được dùng để trở nên hoàn toàn vô hình trong một khoảng thời gian ngắn.\n\nCái áo choàng này càng được sử dụng nhiều, nó sẽ càng mạnh hơn, cho phép Kẻ cướp trở nên vô hình thường xuyên hơn và lâu hơn.
|
||||
items.artifacts.cloakofshadows.discover_hint=Một trong các người hùng bắt đầu với vật phẩm này.
|
||||
items.artifacts.cloakofshadows$cloakstealth.no_charge=Áo choàng của bạn đã hết năng lượng.
|
||||
@@ -440,7 +440,7 @@ items.artifacts.talismanofforesight$foresight.desc=Bạn cảm thấy rất lo n
|
||||
items.artifacts.timekeepershourglass.name=đồng hồ cát của kẻ giữ thời gian
|
||||
items.artifacts.timekeepershourglass.ac_activate=KÍCH HOẠT
|
||||
items.artifacts.timekeepershourglass.deactivate=Bạn hủy kích hoạt "đóng băng thời gian".
|
||||
items.artifacts.timekeepershourglass.no_charge=Cái đòng hồ cát của bạn vẫn chưa sạc lại đủ để có thể sử dụng được.
|
||||
items.artifacts.timekeepershourglass.no_charge=Đồng hồ cát của bạn chưa hồi đủ năng lượng để có thể sử dụng được.
|
||||
items.artifacts.timekeepershourglass.cursed=Bạn không thể sử dụng một chiếc đồng hồ cát bị nguyền rủa.
|
||||
items.artifacts.timekeepershourglass.onstasis=Thế giới dường như thay đổi xung quanh bạn ngay lập tức.
|
||||
items.artifacts.timekeepershourglass.onfreeze=Mọi thứ xung bạn bỗng nhiên đóng băng.
|
||||
@@ -491,7 +491,7 @@ items.bags.velvetpouch.desc=Cái túi nhung nhỏ này có thể chứa nhiều
|
||||
items.bags.velvetpouch.discover_hint=Một trong các người hùng bắt đầu với vật phẩm này.
|
||||
|
||||
items.bags.magicalholster.name=bao da thần kì
|
||||
items.bags.magicalholster.desc=Cái bao da mảnh mai này được làm từ một loại da động vật lạ, nó sở hữu một phép thuật thần kì giúp nó chứa một lượng vũ khí tầm xa khổng lồ.\n\nBạn có thể dễ dàng thò tay vao trong bao và sẽ luôn lấy được vật phẩm mà bạn tìm.\n\nNhờ phép thuật của cái bao da, các cây đũa phép sẽ sạc lại nhanh hơn một chút và các vũ khí ném sẽ dùng được lâu hơn một chút bên trong nó.
|
||||
items.bags.magicalholster.desc=Cái bao da mảnh mai này được làm từ một loại da động vật lạ, nó sở hữu một phép thuật thần kì giúp nó chứa một lượng vũ khí tầm xa khổng lồ.\n\nBạn có thể dễ dàng thò tay vao trong bao và sẽ luôn lấy được vật phẩm mà bạn tìm.\n\nNhờ phép thuật của cái bao da, các cây đũa phép sẽ hồi năng lượng nhanh hơn một chút và các vũ khí ném sẽ dùng được lâu hơn một chút bên trong nó.
|
||||
|
||||
|
||||
|
||||
@@ -1129,7 +1129,7 @@ items.scrolls.scrollofrage.name=cuộn giấy thịnh nộ
|
||||
items.scrolls.scrollofrage.roar=Cuộn giấy này phát ra tiếng gầm giận dữ vang vọng khắp hầm ngục!
|
||||
items.scrolls.scrollofrage.desc=Khi được đọc thành tiếng, cuộn giấy này sẽ giải phóng một tiếng gầm lớn, kéo tất cả kẻ địch đến chỗ người đọc, và làm nổi giận những kẻ địch ở gần.
|
||||
|
||||
items.scrolls.scrollofrecharging.name=cuộn giấy sạc lại
|
||||
items.scrolls.scrollofrecharging.name=cuộn giấy hồi năng
|
||||
items.scrolls.scrollofrecharging.surge=Một nguồn năng lượng chạy qua cơ thể bạn, tiếp thêm năng lượng cho các cây đũa phép của bạn!
|
||||
items.scrolls.scrollofrecharging.desc=Sức mạnh phép thuật thô được lưu giữ trong tấm da này sẽ hồi năng lượng cho các cây đũa phép của người sử dụng theo thời gian khi được giải phóng.
|
||||
|
||||
@@ -1317,7 +1317,7 @@ items.spells.unstablespell.name=thần chú bất ổn
|
||||
items.spells.unstablespell.desc=Tinh thể nhỏ màu đen hình vuông này có những biểu tượng rune biến đổi ở trên mỗi mặt của nó.\n\nKhi được sử dụng, nó sẽ kích hoạt hiệu ứng của một cuộn giấy ngẫu nhiên. Hiệu ứng cuộn giấy sẽ luôn là một hiệu ứng thiên về chiến đấu nếu có kẻ địch trong tầm nhìn của bạn, còn nếu không thì sẽ luôn thiên về không chiến đấu.
|
||||
|
||||
items.spells.wildenergy.name=năng lượng hỗn loạn
|
||||
items.spells.wildenergy.desc=Thần chứ này chứa một phần năng lượng bị nguyền, thứ đã giúp DM-300 chạy. Khi sử dụng, nó sẽ sạc lại các đũa phép và cổ vật đang được trang bị của bạn và đồng thời kích hoạt một hiệu ứng đũa phép bị nguyền ngẫu nhiên. Tuy nhiên, bạn sẽ được định hướng nơi mà phép thuật bị nguyền này bắn vào.
|
||||
items.spells.wildenergy.desc=Thần chứ này chứa một phần năng lượng bị nguyền, thứ đã giúp DM-300 chạy. Khi sử dụng, nó sẽ hồi năng lượng cho các đũa phép và cổ vật đang được trang bị của bạn và đồng thời kích hoạt một hiệu ứng đũa phép bị nguyền ngẫu nhiên. Tuy nhiên, bạn sẽ được định hướng nơi mà phép thuật bị nguyền này bắn vào.
|
||||
|
||||
|
||||
###runestones
|
||||
|
||||
@@ -1402,8 +1402,8 @@ items.trinkets.chaoticcenser.stats_desc=在当前等级下,这件饰物会每
|
||||
items.trinkets.dimensionalsundial.name=位面日晷
|
||||
items.trinkets.dimensionalsundial.warning=你的日晷不再显影,这使你倍感不安。
|
||||
items.trinkets.dimensionalsundial.desc=不知为何,这块小型手持式日晷能在地牢深处显影,甚至你不将其摆正也是如此。更奇怪的是,晷影的方位似乎与这个世界的太阳无关。当晷影不再显现时,日晷似乎会招致危险。
|
||||
items.trinkets.dimensionalsundial.typical_stats_desc=这件饰物通常会在设备日间(8:00~20:00)降低_%1$d%%_的敌人生成速率并在设备夜间(20:00~8:00)提升_%2$d%%_的敌人生成速率。
|
||||
items.trinkets.dimensionalsundial.stats_desc=在当前等级下,这件饰物会在设备日间(8:00~20:00)降低_%1$d%%_的敌人生成速率并在设备夜间(20:00~8:00)提升_%2$d%%_的敌人生成速率。
|
||||
items.trinkets.dimensionalsundial.typical_stats_desc=这件饰物通常会在昼间(8:00~20:00)降低_%1$d%%_的敌人生成速率并在夜间(20:00~8:00)提升_%2$d%%_的敌人生成速率。
|
||||
items.trinkets.dimensionalsundial.stats_desc=在当前等级下,这件饰物会在昼间(8:00~20:00)降低_%1$d%%_的敌人生成速率并在夜间(20:00~8:00)提升_%2$d%%_的敌人生成速率。
|
||||
|
||||
items.trinkets.exoticcrystals.name=奇异能晶
|
||||
items.trinkets.exoticcrystals.desc=这些小型粉色晶体有着和炼金能量晶体一样的几何外形。尽管它们不能直接为炼金实验供能,但不知为何似乎能影响你找到的药剂和卷轴。
|
||||
|
||||
@@ -45,7 +45,7 @@ journal.document.adventurers_guide.identifying.body=В каждом подзем
|
||||
journal.document.adventurers_guide.food.title=Береги запасы еды!
|
||||
journal.document.adventurers_guide.food.body=Рационировать еду — один из лучших способов повысить шансы на выживание, ведь необязательно принимать пищу сразу, как только проголодался!\n\nПока вы не голодаете, ваше здоровье постепенно восстанавливается, так что этот эффект будет потрачен впустую, если вы уже полностью здоровы.\n\nЕсли вы будете принимать пищу осознанно, учитывая состояние здоровья, ваших запасов хватит дольше.
|
||||
journal.document.adventurers_guide.alchemy.title=Алхимия и Аксессуары
|
||||
journal.document.adventurers_guide.alchemy.body=Если вы не находите какой-то предмет полезным, можно использовать его в качестве ингредиента в алхимическом котле или превращены в алхимическую энергию для любых задач. Расщепление зелий и свитков опознаёт их!\n\nАксессуары — это уникальное снаряжение, которое можно изготовить и улучшить только с помощью алхимии. Они могут быть изготовлены из небольшого количества алхимической энергии и магического катализатора.\n\n(Первый алхимический котёл можно найти на 3-м или 4-м этаже подземелья. Рядом с котлами можно найти страницы из книги рецептов по алхимии.)
|
||||
journal.document.adventurers_guide.alchemy.body=Если вы не находите какой-то предмет полезным, можно использовать его в качестве ингредиента в алхимическом котле или превратить в алхимическую энергию для любых задач. Расщепление зелий и свитков опознаёт их!\n\nАксессуары — это уникальное снаряжение, которое можно изготовить и улучшить только с помощью алхимии. Они могут быть изготовлены из небольшого количества алхимической энергии и магического катализатора.\n\n(Первый алхимический котёл можно найти на 3-м или 4-м этаже подземелья. Рядом с котлами можно найти страницы из книги рецептов по алхимии.)
|
||||
journal.document.adventurers_guide.dieing.title=Принятие поражения
|
||||
journal.document.adventurers_guide.dieing.body=Приключения в подземелье сопряжены с немалым риском, и большинство искателей приключений в итоге настигнет погибель.\n\nУдача важна, но самые опытные искатели приключений это те, которые используют любую уловку, чтобы увеличить свои шансы на выживание.\n\n(Не отчаивайтесь, если вы постоянно погибаете, это сложная игра! Сосредоточьтесь на изучении игры и улучшении своих навыков, не настраивайтесь на быструю победу.)
|
||||
journal.document.adventurers_guide.searching.title=Обыскивай каждый уголок!
|
||||
|
||||
@@ -208,7 +208,7 @@ badges$badge.many_buffs.title=狂乱的鸡尾酒
|
||||
badges$badge.many_buffs.desc=同时拥有至少10种增益/减益
|
||||
badges$badge.pacifist_ascent.title=光荣凯旋
|
||||
badges$badge.pacifist_ascent.desc=将其诅咒未曾减弱的Yendor护符带出地牢
|
||||
badges$badge.taking_the_mick.title=洋相百出
|
||||
badges$badge.taking_the_mick.title=弄巧成拙
|
||||
badges$badge.taking_the_mick.desc=以一把至少20级的镐子对最终boss打出致命一击
|
||||
|
||||
challenges.no_food=缩餐节食
|
||||
|
||||
@@ -285,8 +285,8 @@ windows.wndscorebreakdown.old_score_desc=Games started prior to v1.3 have fewer
|
||||
|
||||
windows.wndsettings$displaytab.title=Display Settings
|
||||
windows.wndsettings$displaytab.fullscreen=Fullscreen
|
||||
windows.wndsettings$displaytab.saver=Power Saver
|
||||
windows.wndsettings$displaytab.saver_desc=Power Saver mode draws the game at a reduced size and scales it up to fit your screen.\n\nThis will make graphics less crisp and enlarge the UI slightly, but will also improve performance and battery life.\n\nYou may need to restart the game for changes to take effect.
|
||||
windows.wndsettings$displaytab.hide_navigation=Hide Navigation Bar
|
||||
windows.wndsettings$displaytab.hide_gesture=Hide Gesture Bar
|
||||
windows.wndsettings$displaytab.okay=Okay
|
||||
windows.wndsettings$displaytab.cancel=Cancel
|
||||
windows.wndsettings$displaytab.landscape=Force landscape
|
||||
|
||||
BIN
core/src/main/assets/splashes/title/archs.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
core/src/main/assets/splashes/title/back_clusters.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
core/src/main/assets/splashes/title/front_small.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
core/src/main/assets/splashes/title/mid_mixed.png
Normal file
|
After Width: | Height: | Size: 209 KiB |
@@ -249,6 +249,13 @@ public class Assets {
|
||||
public static final String CAVES = "splashes/caves.jpg";
|
||||
public static final String CITY = "splashes/city.jpg";
|
||||
public static final String HALLS = "splashes/halls.jpg";
|
||||
|
||||
public static class Title {
|
||||
public static final String ARCHS = "splashes/title/archs.png";
|
||||
public static final String BACK_CLUSTERS = "splashes/title/back_clusters.png";
|
||||
public static final String MID_MIXED = "splashes/title/mid_mixed.png";
|
||||
public static final String FRONT_SMALL = "splashes/title/front_small.png";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Sprites {
|
||||
|
||||
@@ -66,12 +66,12 @@ public enum Rankings {
|
||||
public static final int TABLE_SIZE = 11;
|
||||
|
||||
public static final String RANKINGS_FILE = "rankings.dat";
|
||||
|
||||
private static String generateUUID() {
|
||||
Random random = new Random();
|
||||
return Long.toHexString(random.nextLong()) + "-" + Long.toHexString(random.nextLong());
|
||||
}
|
||||
|
||||
private static String generateUUID() {
|
||||
Random random = new Random();
|
||||
return Long.toHexString(random.nextLong()) + "-" + Long.toHexString(random.nextLong());
|
||||
}
|
||||
|
||||
public ArrayList<Record> records;
|
||||
public int lastRecord;
|
||||
public int totalNumber;
|
||||
@@ -544,7 +544,7 @@ public enum Rankings {
|
||||
|
||||
if (bundle.contains(DATA)) gameData = bundle.getBundle(DATA);
|
||||
if (bundle.contains(ID)) gameID = bundle.getString(ID);
|
||||
|
||||
|
||||
if (DeviceCompat.isWeb()) {
|
||||
if (gameID == null) gameID = generateUUID();
|
||||
} else {
|
||||
|
||||
@@ -49,7 +49,7 @@ public class SPDSettings extends GameSettings {
|
||||
|
||||
//Display
|
||||
|
||||
public static final String KEY_FULLSCREEN = "fullscreen";
|
||||
public static final String KEY_FULLSCREEN = "fullscreen"; //used to hide navbars on mobile
|
||||
public static final String KEY_LANDSCAPE = "force_landscape";
|
||||
public static final String KEY_ZOOM = "zoom";
|
||||
public static final String KEY_BRIGHTNESS = "brightness";
|
||||
|
||||
@@ -23,9 +23,9 @@ package com.shatteredpixel.shatteredpixeldungeon.scenes;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Flare;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
|
||||
@@ -52,12 +52,11 @@ public class AboutScene extends PixelScene {
|
||||
|
||||
RectF insets = getCommonInsets();
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.setSize( w, h );
|
||||
add( archs );
|
||||
TitleBackground BG = new TitleBackground( w, h );
|
||||
add( BG );
|
||||
|
||||
//darkens the arches
|
||||
add(new ColorBlock(w, h, 0x88000000));
|
||||
add(new ColorBlock(w, h, 0x44000000));
|
||||
|
||||
ScrollPane list = new ScrollPane( new Component() );
|
||||
add( list );
|
||||
|
||||
@@ -27,9 +27,9 @@ import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Languages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton;
|
||||
@@ -82,9 +82,8 @@ public class ChangesScene extends PixelScene {
|
||||
|
||||
RectF insets = getCommonInsets();
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.setSize(w, h);
|
||||
//archs added later
|
||||
TitleBackground BG = new TitleBackground(w, h);
|
||||
//background added later
|
||||
|
||||
w -= insets.left + insets.right;
|
||||
h -= insets.top + insets.bottom;
|
||||
@@ -350,7 +349,7 @@ public class ChangesScene extends PixelScene {
|
||||
btnOld.setRect(btn0_6.right()-2, btn0_8.top(), 22, changesSelected == 7 ? 19 : 15);
|
||||
addToBack(btnOld);
|
||||
|
||||
addToBack( archs );
|
||||
addToBack( BG );
|
||||
|
||||
fadeIn();
|
||||
}
|
||||
|
||||
@@ -133,19 +133,20 @@ import com.watabou.noosa.Group;
|
||||
import com.watabou.noosa.Image;
|
||||
import com.watabou.noosa.NoosaScript;
|
||||
import com.watabou.noosa.NoosaScriptNoLighting;
|
||||
import com.watabou.noosa.PointerArea;
|
||||
import com.watabou.noosa.SkinnedBlock;
|
||||
import com.watabou.noosa.Visual;
|
||||
import com.watabou.noosa.audio.Sample;
|
||||
import com.watabou.noosa.particles.Emitter;
|
||||
import com.watabou.noosa.tweeners.Tweener;
|
||||
import com.watabou.utils.Callback;
|
||||
import com.watabou.utils.DeviceCompat;
|
||||
import com.watabou.utils.GameMath;
|
||||
import com.watabou.utils.PlatformSupport;
|
||||
import com.watabou.utils.Point;
|
||||
import com.watabou.utils.PointF;
|
||||
import com.watabou.utils.Random;
|
||||
import com.watabou.utils.RectF;
|
||||
import com.watabou.utils.DeviceCompat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -234,6 +235,8 @@ public class GameScene extends PixelScene {
|
||||
}
|
||||
|
||||
RectF insets = getCommonInsets();
|
||||
//we want to check if large is the same as blocking here
|
||||
float largeInsetTop = Game.platform.getSafeInsets(PlatformSupport.INSET_LRG).scale(1f/defaultZoom).top;
|
||||
|
||||
scene = this;
|
||||
|
||||
@@ -365,27 +368,88 @@ public class GameScene extends PixelScene {
|
||||
|
||||
int uiSize = SPDSettings.interfaceSize();
|
||||
|
||||
//TODO this is a good start but just emulating black bars is boring. There must be more to do here.
|
||||
//display cutouts can obstruct various UI elements, so we need to adjust for that sometimes
|
||||
float menuBarMaxLeft = uiCamera.width-insets.right-MenuPane.WIDTH;
|
||||
int hpBarMaxWidth = 50; //default max width
|
||||
float buffBarTopRowMaxWidth = 50; //default max width
|
||||
if (largeInsetTop != insets.top){
|
||||
//iOS's Dynamic island badly obstructs the first buff bar row
|
||||
if (DeviceCompat.isiOS()){
|
||||
//TODO bad to hardcode and approximate this atm
|
||||
// need to change this so iOS platformsupport returns cutout dimensions
|
||||
float cutoutLeft = (Game.width*0.3f)/defaultZoom;
|
||||
buffBarTopRowMaxWidth = Math.min(50, cutoutLeft - 32);
|
||||
} else if (DeviceCompat.isAndroid()) {
|
||||
//Android hole punches are of varying size and may obstruct the menu, HP bar, or buff bar
|
||||
RectF cutout = Game.platform.getDisplayCutout().scale(1f / defaultZoom);
|
||||
//if the cutout is positioned to obstruct the menu bar
|
||||
if (cutout.top < 20
|
||||
&& cutout.left < menuBarMaxLeft + MenuPane.WIDTH
|
||||
&& cutout.right > menuBarMaxLeft) {
|
||||
menuBarMaxLeft = Math.min(menuBarMaxLeft, cutout.left - MenuPane.WIDTH);
|
||||
//make sure we have space to actually move it though
|
||||
menuBarMaxLeft = Math.max(menuBarMaxLeft, PixelScene.MIN_WIDTH_P-MenuPane.WIDTH);
|
||||
}
|
||||
//if the cutout is positioned to obstruct the HP bar
|
||||
if (cutout.left < 78
|
||||
&& cutout.top < 4
|
||||
&& cutout.right > 32) {
|
||||
//subtract starting position, but add a bit back due to end of bar
|
||||
hpBarMaxWidth = Math.round(cutout.left - 32 + 4);
|
||||
hpBarMaxWidth = Math.max(hpBarMaxWidth, 21); //cannot go below 21 (30 effective)
|
||||
}
|
||||
//if the cutout is positioned to obstruct the buff bar
|
||||
if (cutout.left < 80
|
||||
&& cutout.top < 10
|
||||
&& cutout.right > 32
|
||||
&& cutout.bottom > 11) {
|
||||
buffBarTopRowMaxWidth = cutout.left - 32; //subtract starting position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float screentop = largeInsetTop;
|
||||
if (screentop == 0 && uiSize == 0){
|
||||
screentop--; //on mobile UI, if we render in fullscreen, clip the top 1px;
|
||||
}
|
||||
|
||||
menu = new MenuPane();
|
||||
menu.camera = uiCamera;
|
||||
menu.setPos( uiCamera.width-MenuPane.WIDTH-insets.right, insets.top + (uiSize > 0 ? 0 : 1));
|
||||
menu.setPos( menuBarMaxLeft, screentop);
|
||||
add(menu);
|
||||
|
||||
float extraRight = uiCamera.width - (menuBarMaxLeft + MenuPane.WIDTH);
|
||||
if (extraRight > 0){
|
||||
SkinnedBlock bar = new SkinnedBlock(extraRight, 20, TextureCache.createSolid(0x88000000));
|
||||
bar.x = uiCamera.width - extraRight;
|
||||
bar.camera = uiCamera;
|
||||
add(bar);
|
||||
|
||||
PointerArea blocker = new PointerArea(uiCamera.width - extraRight, 0, extraRight, 20);
|
||||
blocker.camera = uiCamera;
|
||||
add(blocker);
|
||||
}
|
||||
|
||||
status = new StatusPane( SPDSettings.interfaceSize() > 0 );
|
||||
status.camera = uiCamera;
|
||||
status.setRect(insets.left, uiSize > 0 ? uiCamera.height-39-insets.bottom : insets.top, uiCamera.width - insets.left - insets.right, 0 );
|
||||
StatusPane.hpBarMaxWidth = hpBarMaxWidth;
|
||||
StatusPane.buffBarTopRowMaxWidth = buffBarTopRowMaxWidth;
|
||||
status.setRect(insets.left, uiSize > 0 ? uiCamera.height-39-insets.bottom : screentop, uiCamera.width - insets.left - insets.right, 0 );
|
||||
add(status);
|
||||
|
||||
if (uiSize < 2 && insets.top > 0) {
|
||||
SkinnedBlock bar = new SkinnedBlock(uiCamera.width, insets.top, TextureCache.createSolid(0xFF1C1E18));
|
||||
if (uiSize < 2 && largeInsetTop != 0) {
|
||||
SkinnedBlock bar = new SkinnedBlock(uiCamera.width, largeInsetTop, TextureCache.createSolid(0x88000000));
|
||||
bar.camera = uiCamera;
|
||||
add(bar);
|
||||
|
||||
PointerArea blocker = new PointerArea(0, 0, uiCamera.width, largeInsetTop);
|
||||
blocker.camera = uiCamera;
|
||||
add(blocker);
|
||||
}
|
||||
|
||||
boss = new BossHealthBar();
|
||||
boss.camera = uiCamera;
|
||||
boss.setPos( insets.left + 6 + (uiCamera.width - insets.left - insets.right - boss.width())/2, insets.top + 20);
|
||||
boss.setPos( (uiCamera.width - boss.width())/2, screentop + 26);
|
||||
add(boss);
|
||||
|
||||
resume = new ResumeIndicator();
|
||||
@@ -433,6 +497,10 @@ public class GameScene extends PixelScene {
|
||||
bar.camera = uiCamera;
|
||||
bar.y = uiCamera.height - insets.bottom;
|
||||
add(bar);
|
||||
|
||||
PointerArea blocker = new PointerArea(0, uiCamera.height - insets.bottom, uiCamera.width, insets.bottom);
|
||||
blocker.camera = uiCamera;
|
||||
add(blocker);
|
||||
}
|
||||
|
||||
layoutTags();
|
||||
|
||||
@@ -336,6 +336,9 @@ public class HeroSelectScene extends PixelScene {
|
||||
SkinnedBlock bar = new SkinnedBlock(Camera.main.width, insets.bottom, TextureCache.createSolid(0xAA000000));
|
||||
bar.y = h + insets.top;
|
||||
add(bar);
|
||||
|
||||
PointerArea blocker = new PointerArea(0, Camera.main.width - insets.bottom, Camera.main.width, insets.bottom);
|
||||
add(blocker);
|
||||
}
|
||||
|
||||
title.setPos(insets.left + (w - title.width()) / 2f, insets.top + (h - HeroBtn.HEIGHT - title.height() - 4));
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.GameLog;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndError;
|
||||
@@ -164,6 +165,7 @@ public class InterlevelScene extends PixelScene {
|
||||
int region = (int)Math.ceil(loadingDepth / 5f);
|
||||
if (region != lastRegion){
|
||||
TextureCache.clear();
|
||||
TitleBackground.reset();
|
||||
lastRegion = region;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,10 +34,10 @@ import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.tiles.TerrainFeaturesTilemap;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.IconTitle;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndJournal;
|
||||
@@ -82,9 +82,8 @@ public class JournalScene extends PixelScene {
|
||||
|
||||
RectF insets = getCommonInsets();
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.setSize( w, h );
|
||||
//archs added later
|
||||
TitleBackground BG = new TitleBackground(w, h);
|
||||
//BG added later
|
||||
|
||||
w -= insets.left + insets.right;
|
||||
h -= insets.top + insets.bottom;
|
||||
@@ -228,7 +227,7 @@ public class JournalScene extends PixelScene {
|
||||
if (lastIDX != 3) btnAlchemy.icon().brightness(0.6f);
|
||||
addToBack(btnAlchemy);
|
||||
|
||||
addToBack(archs);
|
||||
addToBack(BG);
|
||||
|
||||
ExitButton btnExit = new ExitButton();
|
||||
btnExit.setPos( insets.left + w - btnExit.width(), insets.top );
|
||||
|
||||
@@ -29,9 +29,9 @@ import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.services.news.News;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.services.news.NewsArticle;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton;
|
||||
@@ -64,9 +64,8 @@ public class NewsScene extends PixelScene {
|
||||
int h = Camera.main.height;
|
||||
RectF insets = getCommonInsets();
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.setSize(w, h);
|
||||
add(archs);
|
||||
TitleBackground BG = new TitleBackground(w, h);
|
||||
add(BG);
|
||||
|
||||
w -= insets.left + insets.right;
|
||||
h -= insets.top + insets.bottom;
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.BadgeBanner;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Languages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Tooltip;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
|
||||
@@ -102,6 +103,7 @@ public class PixelScene extends Scene {
|
||||
if (!inGameScene && InterlevelScene.lastRegion != -1){
|
||||
InterlevelScene.lastRegion = -1;
|
||||
TextureCache.clear();
|
||||
TitleBackground.reset();
|
||||
//good time to clear holiday cache as well
|
||||
Holiday.clearCachedHoliday();
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.Flare;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Button;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.IconTitle;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndDailies;
|
||||
@@ -58,8 +58,6 @@ public class RankingsScene extends PixelScene {
|
||||
private static final float MAX_ROW_WIDTH = 160;
|
||||
|
||||
private static final float GAP = 4;
|
||||
|
||||
private Archs archs;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
@@ -77,9 +75,8 @@ public class RankingsScene extends PixelScene {
|
||||
int h = Camera.main.height;
|
||||
RectF insets = getCommonInsets();
|
||||
|
||||
archs = new Archs();
|
||||
archs.setSize( w, h );
|
||||
add( archs );
|
||||
TitleBackground BG = new TitleBackground(w, h);
|
||||
add( BG );
|
||||
|
||||
w -= insets.left + insets.right;
|
||||
h -= insets.top + insets.bottom;
|
||||
|
||||
@@ -29,10 +29,10 @@ import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Journal;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Button;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
|
||||
@@ -64,10 +64,9 @@ public class StartScene extends PixelScene {
|
||||
int w = Camera.main.width;
|
||||
int h = Camera.main.height;
|
||||
RectF insets = getCommonInsets();
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.setSize( w, h );
|
||||
add( archs );
|
||||
|
||||
TitleBackground BG = new TitleBackground(w, h);
|
||||
add( BG );
|
||||
|
||||
w -= insets.left + insets.right;
|
||||
h -= insets.top + insets.bottom;
|
||||
|
||||
@@ -25,9 +25,9 @@ import com.shatteredpixel.shatteredpixeldungeon.Chrome;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Languages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
|
||||
@@ -56,9 +56,8 @@ public class SupporterScene extends PixelScene {
|
||||
|
||||
int elementWidth = PixelScene.landscape() ? 202 : 120;
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.setSize(w, h);
|
||||
add(archs);
|
||||
TitleBackground BG = new TitleBackground(w, h);
|
||||
add(BG);
|
||||
|
||||
w -= insets.right + insets.left;
|
||||
h -= insets.top + insets.bottom;
|
||||
|
||||
@@ -36,7 +36,7 @@ import com.shatteredpixel.shatteredpixeldungeon.sprites.EarthGuardianSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.GhostSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.RatSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.WardSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
|
||||
import com.watabou.gltextures.SmartTexture;
|
||||
import com.watabou.gltextures.TextureCache;
|
||||
@@ -98,11 +98,9 @@ public class SurfaceScene extends PixelScene {
|
||||
int h = Camera.main.height;
|
||||
|
||||
RectF insets = getCommonInsets();
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.reversed = true;
|
||||
archs.setSize( w, h );
|
||||
add( archs );
|
||||
|
||||
TitleBackground BG = new TitleBackground(w, h);
|
||||
add( BG );
|
||||
|
||||
w -= insets.left + insets.right;
|
||||
h -= insets.top + insets.bottom;
|
||||
|
||||
@@ -36,10 +36,10 @@ import com.shatteredpixel.shatteredpixeldungeon.services.news.News;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.services.updates.AvailableUpdateData;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.services.updates.Updates;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndSettings;
|
||||
@@ -75,9 +75,8 @@ public class TitleScene extends PixelScene {
|
||||
|
||||
RectF insets = getCommonInsets();
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.setSize( w, h );
|
||||
add( archs );
|
||||
TitleBackground BG = new TitleBackground( w, h );
|
||||
add( BG );
|
||||
|
||||
w -= insets.left + insets.right;
|
||||
h -= insets.top + insets.bottom;
|
||||
|
||||
@@ -34,8 +34,8 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.Fireball;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Journal;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.TitleBackground;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndError;
|
||||
@@ -97,12 +97,11 @@ public class WelcomeScene extends PixelScene {
|
||||
int h = Camera.main.height;
|
||||
RectF insets = getCommonInsets();
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.setSize( w, h );
|
||||
add( archs );
|
||||
TitleBackground BG = new TitleBackground(w, h);
|
||||
add( BG );
|
||||
|
||||
//darkens the arches
|
||||
add(new ColorBlock(w, h, 0x88000000));
|
||||
add(new ColorBlock(w, h, 0x44000000));
|
||||
|
||||
w -= insets.left + insets.right;
|
||||
h -= insets.top + insets.bottom;
|
||||
|
||||
@@ -148,6 +148,8 @@ public class BuffIndicator extends Component {
|
||||
private Char ch;
|
||||
|
||||
private boolean large = false;
|
||||
|
||||
public float firstRowWidth = -1;
|
||||
|
||||
public BuffIndicator( Char ch, boolean large ) {
|
||||
super();
|
||||
@@ -225,32 +227,63 @@ public class BuffIndicator extends Component {
|
||||
buffButtons.put( buff, icon );
|
||||
}
|
||||
}
|
||||
|
||||
//TODO several aspects of the layout code have been a bit hackily changed to support 2 rows
|
||||
// should clean this up
|
||||
|
||||
//layout
|
||||
int row = 0;
|
||||
int pos = 0;
|
||||
float lastIconLeft = 0;
|
||||
int total = 0;
|
||||
for (BuffButton icon : buffButtons.values()){
|
||||
if (total >= 14){ //buff bar supports a max of 14 buffs at once
|
||||
icon.visible = false;
|
||||
continue;
|
||||
}
|
||||
icon.visible = true;
|
||||
|
||||
icon.topOffset = (row > 0 && !large) ? -1 : 0;
|
||||
icon.updateIcon();
|
||||
//button areas are slightly oversized, especially on small buttons
|
||||
icon.setRect(x + pos * (size + 1), y, size + 1, size + (large ? 0 : 5));
|
||||
icon.setRect(x + pos * (size + 1), y + row*(size+1)-icon.topOffset, size + 1, size + (large ? 0 : 5));
|
||||
PixelScene.align(icon);
|
||||
pos++;
|
||||
|
||||
icon.visible = icon.left() <= right();
|
||||
lastIconLeft = icon.left();
|
||||
|
||||
if ((row+1)*(size+1) <= height
|
||||
&& (pos * (size + 1) > width || (row == 0 && firstRowWidth != -1 && pos * (size + 1) > firstRowWidth))){
|
||||
row++;
|
||||
pos = 0;
|
||||
}
|
||||
total++;
|
||||
}
|
||||
|
||||
buffsHidden = false;
|
||||
//squish buff icons together if there isn't enough room
|
||||
float excessWidth = lastIconLeft - right();
|
||||
if (excessWidth > 0) {
|
||||
float leftAdjust = excessWidth/(buffButtons.size()-1);
|
||||
//can't squish by more than 50% on large and 62% on small
|
||||
if (large && leftAdjust >= size*0.48f) leftAdjust = size*0.5f;
|
||||
if (!large && leftAdjust >= size*0.62f) leftAdjust = size*0.65f;
|
||||
float cumulativeAdjust = leftAdjust * (buffButtons.size()-1);
|
||||
|
||||
ArrayList<BuffButton> buttons = new ArrayList<>(buffButtons.values());
|
||||
if (excessWidth > 0) {
|
||||
//if multiple rows, only compress last row
|
||||
ArrayList<BuffButton> buttons = new ArrayList<>();
|
||||
float lastRowY = y + row*(size+1);
|
||||
int i = 1;
|
||||
for (BuffButton button : buffButtons.values()){
|
||||
if (i > 14){
|
||||
button.visible = false;
|
||||
buffsHidden = true;
|
||||
continue;
|
||||
}
|
||||
if (button.top()+button.topOffset == lastRowY){
|
||||
buttons.add(button);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
float leftAdjust = excessWidth/(buttons.size()-1);
|
||||
float cumulativeAdjust = leftAdjust * (buttons.size()-1);
|
||||
|
||||
Collections.reverse(buttons);
|
||||
for (BuffButton icon : buttons) {
|
||||
icon.setPos(icon.left() - cumulativeAdjust, icon.top());
|
||||
@@ -277,6 +310,7 @@ public class BuffIndicator extends Component {
|
||||
private Buff buff;
|
||||
|
||||
private boolean large;
|
||||
private int topOffset = 0;
|
||||
|
||||
public Image grey; //only for small
|
||||
public BitmapText text; //only for large
|
||||
@@ -329,7 +363,7 @@ public class BuffIndicator extends Component {
|
||||
protected void layout() {
|
||||
super.layout();
|
||||
grey.x = icon.x = this.x + (large ? 0 : 1);
|
||||
grey.y = icon.y = this.y + (large ? 0 : 2);
|
||||
grey.y = icon.y = this.y + (large ? 0 : 2) + topOffset;
|
||||
|
||||
if (text.width > width()){
|
||||
text.scale.set(PixelScene.align(0.5f));
|
||||
|
||||
@@ -73,14 +73,14 @@ public class DangerIndicator extends Tag {
|
||||
protected void layout() {
|
||||
super.layout();
|
||||
|
||||
icon.x = right() - 10;
|
||||
icon.x = left() + 14;
|
||||
icon.y = y + (height - icon.height) / 2;
|
||||
|
||||
placeNumber();
|
||||
}
|
||||
|
||||
private void placeNumber() {
|
||||
number.x = right() - 11 - number.width();
|
||||
number.x = left() + 13 - number.width();
|
||||
number.y = y + (height - number.baseLine()) / 2f;
|
||||
PixelScene.align(number);
|
||||
}
|
||||
|
||||
@@ -42,8 +42,10 @@ import com.watabou.input.GameAction;
|
||||
import com.watabou.noosa.BitmapText;
|
||||
import com.watabou.noosa.Game;
|
||||
import com.watabou.noosa.Image;
|
||||
import com.watabou.noosa.NinePatch;
|
||||
import com.watabou.noosa.audio.Sample;
|
||||
import com.watabou.noosa.ui.Component;
|
||||
import com.watabou.utils.DeviceCompat;
|
||||
|
||||
public class MenuPane extends Component {
|
||||
|
||||
@@ -63,18 +65,26 @@ public class MenuPane extends Component {
|
||||
private Toolbar.PickedUpItem pickedUp;
|
||||
|
||||
private BitmapText version;
|
||||
private NinePatch versionOverflowBG;
|
||||
|
||||
private DangerIndicator danger;
|
||||
|
||||
public static final int WIDTH = 32;
|
||||
public static final int WIDTH = 31;
|
||||
|
||||
@Override
|
||||
protected void createChildren() {
|
||||
super.createChildren();
|
||||
|
||||
bg = new Image(Assets.Interfaces.MENU);
|
||||
bg = new Image(Assets.Interfaces.MENU, 1, 0, 31, 21);
|
||||
add(bg);
|
||||
|
||||
versionOverflowBG = new NinePatch(bg.texture, 1, 22, 6, 8, 3, 0, 2, 0);
|
||||
add(versionOverflowBG);
|
||||
|
||||
version = new BitmapText( "v" + Game.version , PixelScene.pixelFont);
|
||||
version.alpha( 0.5f );
|
||||
add(version);
|
||||
|
||||
depthIcon = Icons.get(Dungeon.level.feeling);
|
||||
add(depthIcon);
|
||||
|
||||
@@ -137,10 +147,6 @@ public class MenuPane extends Component {
|
||||
btnMenu = new MenuButton();
|
||||
add( btnMenu );
|
||||
|
||||
version = new BitmapText( "v" + Game.version, PixelScene.pixelFont);
|
||||
version.alpha( 0.5f );
|
||||
add(version);
|
||||
|
||||
danger = new DangerIndicator();
|
||||
add( danger );
|
||||
|
||||
@@ -154,13 +160,31 @@ public class MenuPane extends Component {
|
||||
bg.x = x;
|
||||
bg.y = y;
|
||||
|
||||
version.scale.set(PixelScene.align(0.5f));
|
||||
version.measure();
|
||||
|
||||
float rightMargin = DeviceCompat.isDesktop() ? 1 : 8;
|
||||
if (DeviceCompat.isDebug()) rightMargin = 1; //don't care about hiding 'indev'
|
||||
float overFlow = version.width()-(bg.width()-4-rightMargin);
|
||||
if (overFlow >= 1){
|
||||
version.x = x + 2 - overFlow;
|
||||
versionOverflowBG.size(overFlow+3, 8);
|
||||
versionOverflowBG.x = version.x-3;
|
||||
versionOverflowBG.y = y;
|
||||
} else {
|
||||
version.x = x + 3;
|
||||
versionOverflowBG.visible = false;
|
||||
}
|
||||
version.y = y + 3 - (version.baseLine()*version.scale.y)/2f;
|
||||
version.y -= .001f;
|
||||
PixelScene.align(version);
|
||||
|
||||
btnMenu.setPos( x + WIDTH - btnMenu.width(), y );
|
||||
|
||||
btnJournal.setPos( btnMenu.left() - btnJournal.width() + 2, y );
|
||||
|
||||
depthIcon.x = btnJournal.left() - 7 + (7 - depthIcon.width())/2f - 0.1f;
|
||||
depthIcon.y = y + 1;
|
||||
if (SPDSettings.interfaceSize() == 0) depthIcon.y++;
|
||||
depthIcon.y = y+8;
|
||||
PixelScene.align(depthIcon);
|
||||
|
||||
depthText.scale.set(PixelScene.align(0.67f));
|
||||
@@ -172,8 +196,7 @@ public class MenuPane extends Component {
|
||||
|
||||
if (challengeIcon != null){
|
||||
challengeIcon.x = btnJournal.left() - 14 + (7 - challengeIcon.width())/2f - 0.1f;
|
||||
challengeIcon.y = y + 1;
|
||||
if (SPDSettings.interfaceSize() == 0) challengeIcon.y++;
|
||||
challengeIcon.y = depthIcon.y;
|
||||
PixelScene.align(challengeIcon);
|
||||
|
||||
challengeText.scale.set(PixelScene.align(0.67f));
|
||||
@@ -184,13 +207,8 @@ public class MenuPane extends Component {
|
||||
challengeButton.setRect(challengeIcon.x, challengeIcon.y, challengeIcon.width(), challengeIcon.height() + challengeText.height());
|
||||
}
|
||||
|
||||
version.scale.set(PixelScene.align(0.5f));
|
||||
version.measure();
|
||||
version.x = x + WIDTH - version.width();
|
||||
version.y = y + bg.height() + (3 - version.baseLine());
|
||||
PixelScene.align(version);
|
||||
|
||||
danger.setPos( x + WIDTH - danger.width(), y + bg.height + 3 );
|
||||
danger.setPos( x + WIDTH - danger.width(), y + bg.height + 1 );
|
||||
danger.setSize( camera.width - danger.width(), danger.height());
|
||||
}
|
||||
|
||||
public void pickup(Item item, int cell) {
|
||||
@@ -222,7 +240,7 @@ public class MenuPane extends Component {
|
||||
super();
|
||||
|
||||
width = bg.width + 4;
|
||||
height = bg.height + 4;
|
||||
height = bg.height + 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -250,7 +268,7 @@ public class MenuPane extends Component {
|
||||
super.layout();
|
||||
|
||||
bg.x = x + 2;
|
||||
bg.y = y + 2;
|
||||
bg.y = y + 8;
|
||||
|
||||
journalIcon.x = bg.x + (bg.width() - journalIcon.width())/2f;
|
||||
journalIcon.y = bg.y + (bg.height() - journalIcon.height())/2f;
|
||||
@@ -355,7 +373,7 @@ public class MenuPane extends Component {
|
||||
super();
|
||||
|
||||
width = image.width + 4;
|
||||
height = image.height + 4;
|
||||
height = image.height + 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -371,7 +389,7 @@ public class MenuPane extends Component {
|
||||
super.layout();
|
||||
|
||||
image.x = x + 2;
|
||||
image.y = y + 2;
|
||||
image.y = y + 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -78,6 +78,12 @@ public class StatusPane extends Component {
|
||||
|
||||
private boolean large;
|
||||
|
||||
//potentially shrinks and/or repositions the hp bar to avoid some cutouts
|
||||
public static int hpBarMaxWidth = 50;
|
||||
private Image hpCutout;
|
||||
//potentially cuts off the top row of the the buff indicator to avoid some cutouts
|
||||
public static float buffBarTopRowMaxWidth = 50;
|
||||
|
||||
public StatusPane( boolean large ){
|
||||
super();
|
||||
|
||||
@@ -86,9 +92,13 @@ public class StatusPane extends Component {
|
||||
this.large = large;
|
||||
|
||||
if (large) bg = new NinePatch( asset, 0, 64, 41, 39, 33, 0, 4, 0 );
|
||||
else bg = new NinePatch( asset, 0, 0, 128, 36, 85, 0, 45, 0 );
|
||||
else bg = new NinePatch( asset, 0, 0, 82, 38, 32, 0, 5, 0 );
|
||||
add( bg );
|
||||
|
||||
hpCutout = new Image(asset, 90, 0, 12, 9);
|
||||
hpCutout.visible = false;
|
||||
add(hpCutout);
|
||||
|
||||
heroInfo = new Button(){
|
||||
@Override
|
||||
protected void onClick () {
|
||||
@@ -117,16 +127,16 @@ public class StatusPane extends Component {
|
||||
add( compass );
|
||||
|
||||
if (large) rawShielding = new Image(asset, 0, 112, 128, 9);
|
||||
else rawShielding = new Image(asset, 0, 40, 50, 4);
|
||||
else rawShielding = new Image(asset, 0, 44, 50, 4);
|
||||
rawShielding.alpha(0.5f);
|
||||
add(rawShielding);
|
||||
|
||||
if (large) shieldedHP = new Image(asset, 0, 112, 128, 9);
|
||||
else shieldedHP = new Image(asset, 0, 40, 50, 4);
|
||||
else shieldedHP = new Image(asset, 0, 44, 50, 4);
|
||||
add(shieldedHP);
|
||||
|
||||
if (large) hp = new Image(asset, 0, 103, 128, 9);
|
||||
else hp = new Image(asset, 0, 36, 50, 4);
|
||||
else hp = new Image(asset, 0, 40, 50, 4);
|
||||
add( hp );
|
||||
|
||||
hpText = new BitmapText(PixelScene.pixelFont);
|
||||
@@ -143,15 +153,13 @@ public class StatusPane extends Component {
|
||||
add(heroInfoOnBar);
|
||||
|
||||
if (large) exp = new Image(asset, 0, 121, 128, 7);
|
||||
else exp = new Image(asset, 0, 44, 16, 1);
|
||||
else exp = new Image(asset, 0, 48, 17, 4);
|
||||
add( exp );
|
||||
|
||||
if (large){
|
||||
expText = new BitmapText(PixelScene.pixelFont);
|
||||
expText.hardlight( 0xFFFFAA );
|
||||
expText.alpha(0.6f);
|
||||
add(expText);
|
||||
}
|
||||
expText = new BitmapText(PixelScene.pixelFont);
|
||||
expText.hardlight( 0xFFFFAA );
|
||||
expText.alpha(0.6f);
|
||||
add(expText);
|
||||
|
||||
level = new BitmapText( PixelScene.pixelFont);
|
||||
level.hardlight( 0xFFFFAA );
|
||||
@@ -171,18 +179,18 @@ public class StatusPane extends Component {
|
||||
@Override
|
||||
protected void layout() {
|
||||
|
||||
height = large ? 39 : 32;
|
||||
height = large ? 39 : 38;
|
||||
|
||||
bg.x = x;
|
||||
bg.y = y;
|
||||
if (large) bg.size( 160, bg.height ); //HP bars must be 128px wide atm
|
||||
else bg.size( width, bg.height );
|
||||
else bg.size(hpBarMaxWidth+32, bg.height ); //default max right is 50px health bar + 32
|
||||
|
||||
avatar.x = bg.x - avatar.width / 2f + 15;
|
||||
avatar.y = bg.y - avatar.height / 2f + (large ? 15 : 16);
|
||||
avatar.y = bg.y - avatar.height / 2f + 16;
|
||||
PixelScene.align(avatar);
|
||||
|
||||
heroInfo.setRect( x, y+(large ? 0 : 1), 30, large ? 40 : 30 );
|
||||
heroInfo.setRect( x, y, 30, large ? 40 : 36 );
|
||||
|
||||
compass.x = avatar.x + avatar.width / 2f - compass.origin.x;
|
||||
compass.y = avatar.y + avatar.height / 2f - compass.origin.y;
|
||||
@@ -210,11 +218,27 @@ public class StatusPane extends Component {
|
||||
busy.x = x + bg.width + 1;
|
||||
busy.y = y + bg.height - 9;
|
||||
} else {
|
||||
exp.x = x;
|
||||
exp.y = y;
|
||||
exp.x = x+2;
|
||||
exp.y = y+30;
|
||||
|
||||
hp.x = shieldedHP.x = rawShielding.x = x + 30;
|
||||
hp.y = shieldedHP.y = rawShielding.y = y + 3;
|
||||
float hpleft = x + 30;
|
||||
if (hpBarMaxWidth < 82){
|
||||
//the class variable assumes the left of the bar can't move, but we can inset it 9px
|
||||
int hpWidth = (int)hpBarMaxWidth;
|
||||
if (hpWidth <= 41){
|
||||
hpleft -= 9;
|
||||
hpWidth += 9;
|
||||
hpCutout.visible = true;
|
||||
hpCutout.x = hpleft - 2;
|
||||
hpCutout.y = y;
|
||||
}
|
||||
hp.frame(50-hpWidth, 40, 50, 4);
|
||||
shieldedHP.frame(50-hpWidth, 44, 50, 4);
|
||||
rawShielding.frame(50-hpWidth, 44, 50, 4);
|
||||
}
|
||||
|
||||
hp.x = shieldedHP.x = rawShielding.x = hpleft;
|
||||
hp.y = shieldedHP.y = rawShielding.y = y + 2;
|
||||
|
||||
hpText.scale.set(PixelScene.align(0.5f));
|
||||
hpText.x = hp.x + 1;
|
||||
@@ -222,12 +246,19 @@ public class StatusPane extends Component {
|
||||
hpText.y -= 0.001f; //prefer to be slightly higher
|
||||
PixelScene.align(hpText);
|
||||
|
||||
expText.scale.set(PixelScene.align(0.5f));
|
||||
expText.x = exp.x + 1;
|
||||
expText.y = exp.y + (exp.height - (expText.baseLine()+expText.scale.y))/2f;
|
||||
expText.y -= 0.001f; //prefer to be slightly higher
|
||||
PixelScene.align(expText);
|
||||
|
||||
heroInfoOnBar.setRect(heroInfo.right(), y, 50, 9);
|
||||
|
||||
buffs.setRect( x + 31, y + 9, 50, 8 );
|
||||
buffs.firstRowWidth = buffBarTopRowMaxWidth;
|
||||
buffs.setRect( x + 31, y + 8, 50, 15 );
|
||||
|
||||
busy.x = x + 1;
|
||||
busy.y = y + 33;
|
||||
busy.y = y + 37;
|
||||
}
|
||||
|
||||
counter.point(busy.center());
|
||||
@@ -291,7 +322,8 @@ public class StatusPane extends Component {
|
||||
expText.x = hp.x + (128 - expText.width())/2f;
|
||||
|
||||
} else {
|
||||
exp.scale.x = (width / exp.width) * Dungeon.hero.exp / Dungeon.hero.maxExp();
|
||||
exp.scale.x = (17 / exp.width) * Dungeon.hero.exp / Dungeon.hero.maxExp();
|
||||
expText.text(Dungeon.hero.exp + "/" + Dungeon.hero.maxExp());
|
||||
}
|
||||
|
||||
if (Dungeon.hero.lvl != lastLvl) {
|
||||
@@ -310,8 +342,8 @@ public class StatusPane extends Component {
|
||||
} else {
|
||||
level.text( Integer.toString( lastLvl ) );
|
||||
level.measure();
|
||||
level.x = x + 27.5f - level.width() / 2f;
|
||||
level.y = y + 28.0f - level.baseLine() / 2f;
|
||||
level.x = x + 25.5f - level.width() / 2f;
|
||||
level.y = y + 31.0f - level.baseLine() / 2f;
|
||||
}
|
||||
PixelScene.align(level);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,740 @@
|
||||
/*
|
||||
* Pixel Dungeon
|
||||
* Copyright (C) 2012-2015 Oleg Dolya
|
||||
*
|
||||
* Shattered Pixel Dungeon
|
||||
* Copyright (C) 2014-2025 Evan Debenham
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package com.shatteredpixel.shatteredpixeldungeon.ui;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Assets;
|
||||
import com.watabou.gltextures.TextureCache;
|
||||
import com.watabou.noosa.Game;
|
||||
import com.watabou.noosa.Group;
|
||||
import com.watabou.noosa.Image;
|
||||
import com.watabou.noosa.TextureFilm;
|
||||
import com.watabou.noosa.ui.Component;
|
||||
import com.watabou.utils.Random;
|
||||
import com.watabou.utils.RectF;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
//TODO still a couple of refinements here:
|
||||
// now that all assets have 2x layers there's a lot of copypasta, surely logic can be shared in more places
|
||||
// asset picking isn't ideal, more sophisticated algorithm should help with this
|
||||
// back layers are dark, which works well on desktop but may be hard to see on mobile
|
||||
public class TitleBackground extends Component {
|
||||
|
||||
public static float SCROLL_SPEED = 15f;
|
||||
|
||||
private float density = 1f;
|
||||
|
||||
//Arch back layer
|
||||
private static final TextureFilm ARCH_FILM = new TextureFilm(Assets.Splashes.Title.ARCHS, 333, 100);
|
||||
private static Group archLayer;
|
||||
private static ArrayList<Image> archs;
|
||||
|
||||
//Cluster far layer
|
||||
private static TextureFilm CLUSTER_FILM = new TextureFilm(Assets.Splashes.Title.BACK_CLUSTERS, 450, 250);
|
||||
private static ArrayList<Image> clusters;
|
||||
private static Group clusterLayer;
|
||||
|
||||
private static ArrayList<Image> clustersFar;
|
||||
private static Group clustersFarLayer;
|
||||
|
||||
//Small far layer
|
||||
private static TextureFilm SMALL_FILM = new TextureFilm(Assets.Splashes.Title.FRONT_SMALL, 112, 116);
|
||||
private static ArrayList<Image> smallFars;
|
||||
private static Group smallFarLayer;
|
||||
|
||||
//Mixed Item middle layer 1
|
||||
private static TextureFilm MID_FILM = new TextureFilm(Assets.Splashes.Title.MID_MIXED, 273, 242);
|
||||
private static ArrayList<Image> mids1;
|
||||
private static Group mids1Layer;
|
||||
//Mixed Item middle layer 2
|
||||
private static ArrayList<Image> mids2;
|
||||
private static Group mids2Layer;
|
||||
|
||||
//Small Item front layer
|
||||
private static ArrayList<Image> smallCloses;
|
||||
private static Group smallCloseLayer;
|
||||
|
||||
private static boolean wasLandscape;
|
||||
private static float oldBaseScale = 1;
|
||||
private static float oldWidth = 0;
|
||||
|
||||
public static void reset(){
|
||||
archs = null;
|
||||
clusters = null;
|
||||
clustersFar = null;
|
||||
smallFars = null;
|
||||
mids1 = null;
|
||||
mids2 = null;
|
||||
smallCloses = null;
|
||||
}
|
||||
|
||||
public TitleBackground(int width, int height){
|
||||
super();
|
||||
x = y = 0;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
setupObjects();
|
||||
}
|
||||
|
||||
protected void setupObjects() {
|
||||
|
||||
boolean landscape = width > height;
|
||||
|
||||
float scale = height / 450f;
|
||||
|
||||
//we reset in this case as scale changes
|
||||
if (archs != null && (landscape != wasLandscape)){
|
||||
archs = null;
|
||||
clustersFar = null;
|
||||
clusters = null;
|
||||
smallFarLayer = null;
|
||||
mids1 = null;
|
||||
mids2 = null;
|
||||
smallCloses = null;
|
||||
}
|
||||
wasLandscape = landscape;
|
||||
|
||||
archLayer = new Group();
|
||||
if (archs == null) {
|
||||
archs = new ArrayList<>();
|
||||
} else {
|
||||
convertArchLayer(archs, archLayer, scale);
|
||||
}
|
||||
add(archLayer);
|
||||
|
||||
Image darkness = new Image(TextureCache.createGradient(0x00000000, 0x11000000, 0x22000000, 0x33000000, 0x44000000, 0x88000000));
|
||||
darkness.angle = 90;
|
||||
darkness.x = width;
|
||||
darkness.scale.x = height/6f;
|
||||
darkness.scale.y = width;
|
||||
add(darkness);
|
||||
|
||||
if (!landscape){
|
||||
scale /= 1.5f;
|
||||
oldBaseScale /= 1.5f;
|
||||
}
|
||||
|
||||
density = width / (800f * scale);
|
||||
density = (density+0.5f)/1.5f; //pull density 33% of the way toward 1 if it is beyond it
|
||||
|
||||
clustersFarLayer = new Group();
|
||||
if (clustersFar == null) {
|
||||
clustersFar = new ArrayList<>();
|
||||
} else {
|
||||
convertFloatingLayer(clustersFar, clustersFarLayer, scale, oldWidth);
|
||||
}
|
||||
add(clustersFarLayer);
|
||||
|
||||
clusterLayer = new Group();
|
||||
if (clusters == null) {
|
||||
clusters = new ArrayList<>();
|
||||
} else {
|
||||
convertFloatingLayer(clusters, clusterLayer, scale, oldWidth);
|
||||
}
|
||||
add(clusterLayer);
|
||||
|
||||
smallFarLayer = new Group();
|
||||
if (smallFars == null){
|
||||
smallFars = new ArrayList<>();
|
||||
} else {
|
||||
convertFloatingLayer(smallFars, smallFarLayer, scale, oldWidth);
|
||||
}
|
||||
add(smallFarLayer);
|
||||
|
||||
mids1Layer = new Group();
|
||||
if (mids1 == null){
|
||||
mids1 = new ArrayList<>();
|
||||
} else {
|
||||
convertFloatingLayer(mids1, mids1Layer, scale, oldWidth);
|
||||
}
|
||||
add(mids1Layer);
|
||||
|
||||
mids2Layer = new Group();
|
||||
if (mids2 == null){
|
||||
mids2 = new ArrayList<>();
|
||||
} else {
|
||||
convertFloatingLayer(mids2, mids2Layer, scale, oldWidth);
|
||||
}
|
||||
add(mids2Layer);
|
||||
|
||||
smallCloseLayer = new Group();
|
||||
if (smallCloses == null){
|
||||
smallCloses = new ArrayList<>();
|
||||
} else {
|
||||
convertFloatingLayer(smallCloses, smallCloseLayer, scale, oldWidth);
|
||||
}
|
||||
add(smallCloseLayer);
|
||||
|
||||
oldWidth = width/scale;
|
||||
if (!landscape){
|
||||
scale *= 1.5f;
|
||||
}
|
||||
oldBaseScale = scale;
|
||||
|
||||
}
|
||||
|
||||
//*** Logic for converting images between scene resets. ***
|
||||
|
||||
//creates a new instance of a disposed of image, for recreation of this component after scene transition
|
||||
protected Image convertImage(Image oldImg, float newBaseScale){
|
||||
Image newImg = new Image(oldImg.texture);
|
||||
newImg.frame(oldImg.frame());
|
||||
float oldScale = oldImg.scale.x / oldBaseScale;
|
||||
newImg.scale.set(newBaseScale*oldScale);
|
||||
newImg.brightness(oldImg.rm);
|
||||
float scaleDiff = newImg.scale.y / oldImg.scale.y;
|
||||
newImg.x = oldImg.x * scaleDiff;
|
||||
newImg.y = oldImg.y * scaleDiff;
|
||||
newImg.angle = oldImg.angle;
|
||||
return newImg;
|
||||
}
|
||||
|
||||
protected void convertArchLayer(ArrayList<Image> layerList, Group layerGroup, float newBaseScale){
|
||||
ArrayList<Image> oldImages = new ArrayList<>(layerList);
|
||||
layerList.clear();
|
||||
for (int i = 0; i < oldImages.size(); i++){
|
||||
Image oldArch = oldImages.get(i);
|
||||
Image newArch = convertImage(oldArch, newBaseScale);
|
||||
layerList.add(newArch);
|
||||
layerGroup.add(newArch);
|
||||
//if we're at the end of a row and haven't hit the end yet, add more archs!
|
||||
while (newArch.x+newArch.width() < width
|
||||
&& (i == oldImages.size()-1 || oldImages.get(i+1).y != oldArch.y)){
|
||||
Image extraArch = new Image(Assets.Splashes.Title.ARCHS);
|
||||
extraArch.frame(getArchFrame());
|
||||
extraArch.scale.set(newBaseScale);
|
||||
extraArch.x = newArch.x + newArch.width();
|
||||
extraArch.x -= 9 * newArch.scale.x; //still want to inset here
|
||||
extraArch.y = newArch.y; //y inset already done
|
||||
layerList.add(extraArch);
|
||||
layerGroup.add(extraArch);
|
||||
newArch = extraArch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void convertFloatingLayer(ArrayList<Image> layerList, Group layerGroup, float newBaseScale, float oldWidth){
|
||||
ArrayList<Image> oldImages = new ArrayList<>(layerList);
|
||||
layerList.clear();
|
||||
|
||||
float xShift = (width()/newBaseScale)/oldWidth;
|
||||
for (int i = 0; i < oldImages.size(); i++){
|
||||
Image oldImage = oldImages.get(i);
|
||||
Image newImage = convertImage(oldImage, newBaseScale);
|
||||
if (newImage.x > 0) {
|
||||
newImage.x *= xShift;
|
||||
}
|
||||
layerList.add(newImage);
|
||||
layerGroup.add(newImage);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Image> toMove = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public synchronized void update() {
|
||||
super.update();
|
||||
|
||||
float scale = height / 450f;
|
||||
float shift = Game.elapsed * SCROLL_SPEED * scale;
|
||||
|
||||
if (width <= height){
|
||||
shift /= 1.5f;
|
||||
}
|
||||
updateArchLayer(scale, shift);
|
||||
if (width <= height){
|
||||
scale /= 1.5f;
|
||||
}
|
||||
shift *= 1.33f;
|
||||
updateClusterFarLayer(scale, shift);
|
||||
shift *= 1.5f;
|
||||
updateClusterLayer(scale, shift);
|
||||
shift *= 1.33f;
|
||||
updateFarSmallLayer(scale, shift);
|
||||
shift *= 1.33f;
|
||||
updateMid1Layer(scale, shift);
|
||||
shift *= 1.33f;
|
||||
updateMid2Layer(scale, shift);
|
||||
shift *= 1.33f;
|
||||
updateFrontSmallLayer(scale, shift);
|
||||
|
||||
}
|
||||
|
||||
//*** Arch layer logic ***
|
||||
|
||||
private static final float[] INIT_ARCH_CHANCES = {5, 5, 2, 2, 2, 2};
|
||||
private static float[] arch_chances = INIT_ARCH_CHANCES.clone();
|
||||
|
||||
public RectF getArchFrame(){
|
||||
|
||||
int tile = Random.chances(arch_chances);
|
||||
if (tile == -1){
|
||||
arch_chances = INIT_ARCH_CHANCES.clone();
|
||||
tile = Random.chances(arch_chances);
|
||||
}
|
||||
arch_chances[tile]--;
|
||||
|
||||
return ARCH_FILM.get(tile);
|
||||
}
|
||||
|
||||
private void updateArchLayer(float scale, float shift){
|
||||
float bottom = 0;
|
||||
|
||||
//pass over existing archs, raise them
|
||||
for (Image arch : archs){
|
||||
arch.y -= shift;
|
||||
if (arch.y + arch.height() < 0){
|
||||
toMove.add(arch);
|
||||
} else if (arch.y + arch.height() > bottom){
|
||||
bottom = arch.y + arch.height();
|
||||
}
|
||||
}
|
||||
|
||||
//move any archs that scrolled off camera to the bottom
|
||||
if (!toMove.isEmpty()){
|
||||
//we know it'll be one row
|
||||
for (Image arch : toMove){
|
||||
arch.frame(getArchFrame());
|
||||
arch.y = bottom - 5*scale;
|
||||
}
|
||||
bottom += 100*scale; //arch height
|
||||
toMove.clear();
|
||||
}
|
||||
|
||||
//if we aren't low enough, add more arch layers
|
||||
while (bottom < height){
|
||||
float left = -5 + (-33.334f * Random.Int(1, 9) * scale);
|
||||
while (left < width){
|
||||
Image arch = new Image(Assets.Splashes.Title.ARCHS);
|
||||
arch.frame(getArchFrame());
|
||||
arch.scale.set(scale);
|
||||
arch.x = left;
|
||||
arch.y = bottom - 5*scale;
|
||||
archLayer.add(arch);
|
||||
archs.add(arch);
|
||||
left += arch.width() - (9 * scale);
|
||||
}
|
||||
bottom += 100*scale; //arch height
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//*** Cluster layer logic ***
|
||||
|
||||
private static final float[] INIT_CLUSTER_CHANCES = {2, 2};
|
||||
private static float[] cluster_chances = INIT_CLUSTER_CHANCES.clone();
|
||||
|
||||
public RectF getClusterFrame(){
|
||||
|
||||
int tile = Random.chances(cluster_chances);
|
||||
if (tile == -1){
|
||||
cluster_chances = INIT_CLUSTER_CHANCES.clone();
|
||||
tile = Random.chances(cluster_chances);
|
||||
}
|
||||
cluster_chances[tile]--;
|
||||
|
||||
return CLUSTER_FILM.get(tile);
|
||||
}
|
||||
|
||||
private void updateClusterFarLayer(float scale, float shift){
|
||||
float bottom = 0;
|
||||
float lastX = 0;
|
||||
|
||||
for (Image cluster : clustersFar){
|
||||
cluster.y -= shift;
|
||||
if (cluster.y + cluster.height() < -20){
|
||||
toMove.add(cluster);
|
||||
} else if (cluster.y + cluster.height() > bottom){
|
||||
bottom = cluster.y + cluster.height();
|
||||
lastX = cluster.x;
|
||||
}
|
||||
}
|
||||
|
||||
if (!toMove.isEmpty()){
|
||||
for (Image cluster : toMove){
|
||||
cluster.frame(getClusterFrame());
|
||||
float flex = 0;
|
||||
do {
|
||||
cluster.x = Random.Float(-cluster.width()/3f, width - 2*cluster.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(cluster.x - lastX) < density*(cluster.width()/2f - flex));
|
||||
cluster.y = bottom - cluster.height() + Random.Float(cluster.height()/2f, cluster.height())/density;
|
||||
cluster.angle = Random.Float(-20, 20);
|
||||
bottom = cluster.y + cluster.height();
|
||||
lastX = cluster.x;
|
||||
}
|
||||
toMove.clear();
|
||||
}
|
||||
|
||||
//clusters are 250 tall, add a bit for safety
|
||||
float padding = 300 - (300/2f / density);
|
||||
while (bottom < (height + padding)){
|
||||
Image cluster = new Image(Assets.Splashes.Title.BACK_CLUSTERS);
|
||||
cluster.frame(getClusterFrame());
|
||||
cluster.scale.set(scale * 0.5f);
|
||||
|
||||
float flex = 0;
|
||||
do {
|
||||
cluster.x = Random.Float(-cluster.width()/3f, width - 2*cluster.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(cluster.x - lastX) < density*(cluster.width()/2f - flex));
|
||||
cluster.y = bottom - cluster.height() + Random.Float(cluster.height()/2f, cluster.height())/density;
|
||||
cluster.angle = Random.Float(-20, 20);
|
||||
|
||||
cluster.brightness(0.5f);
|
||||
|
||||
clustersFar.add(cluster);
|
||||
clustersFarLayer.add(cluster);
|
||||
|
||||
bottom = cluster.y + cluster.height();
|
||||
lastX = cluster.x;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateClusterLayer(float scale, float shift){
|
||||
float bottom = 0;
|
||||
float lastX = 0;
|
||||
|
||||
for (Image cluster : clusters){
|
||||
cluster.y -= shift;
|
||||
if (cluster.y + cluster.height() < -20){
|
||||
toMove.add(cluster);
|
||||
} else if (cluster.y + cluster.height() > bottom){
|
||||
bottom = cluster.y + cluster.height();
|
||||
lastX = cluster.x;
|
||||
}
|
||||
}
|
||||
|
||||
if (!toMove.isEmpty()){
|
||||
for (Image cluster : toMove){
|
||||
cluster.frame(getClusterFrame());
|
||||
float flex = 0;
|
||||
do {
|
||||
cluster.x = Random.Float(-cluster.width()/3f, width - 2*cluster.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(cluster.x - lastX) < density*(cluster.width()/2f - flex));
|
||||
cluster.y = bottom - cluster.height() + Random.Float(cluster.height()/2f, cluster.height())/density;
|
||||
cluster.angle = Random.Float(-20, 20);
|
||||
bottom = cluster.y + cluster.height();
|
||||
lastX = cluster.x;
|
||||
}
|
||||
toMove.clear();
|
||||
}
|
||||
|
||||
//clusters are 250 tall, add a bit for safety
|
||||
float padding = 300 - (300/2f / density);
|
||||
while (bottom < (height + padding)){
|
||||
Image cluster = new Image(Assets.Splashes.Title.BACK_CLUSTERS);
|
||||
cluster.frame(getClusterFrame());
|
||||
cluster.scale.set(scale);
|
||||
|
||||
float flex = 0;
|
||||
do {
|
||||
cluster.x = Random.Float(-cluster.width()/3f, width - 2*cluster.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(cluster.x - lastX) < density*(cluster.width()/2f - flex));
|
||||
cluster.y = bottom - cluster.height() + Random.Float(cluster.height()/2f, cluster.height())/density;
|
||||
cluster.angle = Random.Float(-20, 20);
|
||||
|
||||
cluster.brightness(0.75f);
|
||||
|
||||
clusters.add(cluster);
|
||||
clusterLayer.add(cluster);
|
||||
|
||||
bottom = cluster.y + cluster.height();
|
||||
lastX = cluster.x;
|
||||
}
|
||||
}
|
||||
|
||||
//*** Mid layer (1 and 2) logic ***
|
||||
|
||||
private static final float[] INIT_MID_CHANCES = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
private static float[] mid_chances = INIT_MID_CHANCES.clone();
|
||||
|
||||
private static ArrayList<Integer> lastMids = new ArrayList<>();
|
||||
|
||||
public RectF getMidFrame(){
|
||||
|
||||
int tile = -1;
|
||||
do {
|
||||
tile = Random.chances(mid_chances);
|
||||
if (tile == -1) {
|
||||
mid_chances = INIT_MID_CHANCES.clone();
|
||||
tile = Random.chances(mid_chances);
|
||||
}
|
||||
} while (lastMids.contains(tile));
|
||||
mid_chances[tile]--;
|
||||
|
||||
lastMids.add(0, tile);
|
||||
if (lastMids.size() >= 20){
|
||||
lastMids.remove(19);
|
||||
}
|
||||
|
||||
return MID_FILM.get(tile);
|
||||
}
|
||||
|
||||
private void updateMid1Layer(float scale, float shift){
|
||||
float bottom = 0;
|
||||
float lastX = 0;
|
||||
|
||||
for (Image mid : mids1){
|
||||
mid.y -= shift;
|
||||
if (mid.y + mid.height() < -20){
|
||||
toMove.add(mid);
|
||||
} else if (mid.y + mid.height() > bottom){
|
||||
bottom = mid.y + mid.height();
|
||||
lastX = mid.x;
|
||||
}
|
||||
}
|
||||
|
||||
if (!toMove.isEmpty()){
|
||||
for (Image mid : toMove){
|
||||
mid.frame(getMidFrame());
|
||||
mid.scale.set(scale * Random.Float(0.75f, 1.25f));
|
||||
float flex = 0;
|
||||
do {
|
||||
mid.x = Random.Float(-mid.width()/3f, width - 2*mid.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(mid.x - lastX) < density*(mid.width()*0.75f - flex));
|
||||
mid.y = bottom - mid.height() + Random.Float(mid.height()*0.75f, mid.height())/density;
|
||||
mid.angle = Random.Float(-20, 20);
|
||||
bottom = mid.y + mid.height();
|
||||
lastX = mid.x;
|
||||
}
|
||||
toMove.clear();
|
||||
}
|
||||
|
||||
//mids are ~250 tall, add a bit for safety
|
||||
float padding = 300 - (300/2f / density);
|
||||
while (bottom < (height + padding)){
|
||||
Image mid = new Image(Assets.Splashes.Title.MID_MIXED);
|
||||
mid.frame(getMidFrame());
|
||||
mid.scale.set(scale * Random.Float(0.75f, 1.25f));
|
||||
mid.brightness(0.9f);
|
||||
|
||||
float flex = 0;
|
||||
do {
|
||||
mid.x = Random.Float(-mid.width()/3f, width - 2*mid.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(mid.x - lastX) < density*(mid.width()*0.75f - flex));
|
||||
mid.y = bottom - mid.height() + Random.Float(mid.height()/2f, mid.height())/density;
|
||||
mid.angle = Random.Float(-20, 20);
|
||||
|
||||
mids1.add(mid);
|
||||
mids1Layer.add(mid);
|
||||
|
||||
bottom = mid.y + mid.height();
|
||||
lastX = mid.x;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMid2Layer(float scale, float shift){
|
||||
float bottom = 0;
|
||||
float lastX = 0;
|
||||
|
||||
for (Image mid : mids2){
|
||||
mid.y -= shift;
|
||||
if (mid.y + mid.height() < -20){
|
||||
toMove.add(mid);
|
||||
} else if (mid.y + mid.height() > bottom){
|
||||
bottom = mid.y + mid.height();
|
||||
lastX = mid.x;
|
||||
}
|
||||
}
|
||||
|
||||
if (!toMove.isEmpty()){
|
||||
for (Image mid : toMove){
|
||||
mid.frame(getMidFrame());
|
||||
mid.scale.set(scale * Random.Float(1.25f, 1.75f));
|
||||
float flex = 0;
|
||||
do {
|
||||
mid.x = Random.Float(-mid.width()/3f, width - 2*mid.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(mid.x - lastX) < density*(mid.width()*0.75f - flex));
|
||||
mid.y = bottom - mid.height() + Random.Float(mid.height()/2f, mid.height())/density;
|
||||
mid.angle = Random.Float(-20, 20);
|
||||
bottom = mid.y + mid.height();
|
||||
lastX = mid.x;
|
||||
}
|
||||
toMove.clear();
|
||||
}
|
||||
|
||||
//mids are ~250 tall, add a bit for safety
|
||||
float padding = 300 - (300/2f / density);
|
||||
while (bottom < (height + padding)){
|
||||
Image mid = new Image(Assets.Splashes.Title.MID_MIXED);
|
||||
mid.frame(getMidFrame());
|
||||
mid.scale.set(scale * Random.Float(1.25f, 1.75f));
|
||||
|
||||
float flex = 0;
|
||||
do {
|
||||
mid.x = Random.Float(-mid.width()/3f, width - 2*mid.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(mid.x - lastX) < density*(mid.width()*0.75f - flex));
|
||||
mid.y = bottom - mid.height() + Random.Float(mid.height()/2f, mid.height())/density;
|
||||
mid.angle = Random.Float(-20, 20);
|
||||
|
||||
mids2.add(mid);
|
||||
mids2Layer.add(mid);
|
||||
|
||||
bottom = mid.y + mid.height();
|
||||
lastX = mid.x;
|
||||
}
|
||||
}
|
||||
|
||||
//*** Small front layer logic ***
|
||||
|
||||
private static final float[] INIT_SMALL_CHANCES = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
private static float[] small_chances = INIT_SMALL_CHANCES.clone();
|
||||
|
||||
private static ArrayList<Integer> lastSmalls = new ArrayList<>();
|
||||
|
||||
public RectF getSmallFrame(){
|
||||
|
||||
int tile = -1;
|
||||
do {
|
||||
tile = Random.chances(small_chances);
|
||||
if (tile == -1) {
|
||||
small_chances = INIT_SMALL_CHANCES.clone();
|
||||
tile = Random.chances(small_chances);
|
||||
}
|
||||
} while (lastSmalls.contains(tile));
|
||||
small_chances[tile]--;
|
||||
|
||||
lastSmalls.add(0, tile);
|
||||
if (lastSmalls.size() >= 15){
|
||||
lastSmalls.remove(14);
|
||||
}
|
||||
|
||||
return SMALL_FILM.get(tile);
|
||||
}
|
||||
|
||||
private void updateFarSmallLayer(float scale, float shift) {
|
||||
float bottom = 0;
|
||||
float lastX = 0;
|
||||
|
||||
for (Image small : smallFars) {
|
||||
small.y -= shift;
|
||||
if (small.y + small.height() < -20) {
|
||||
toMove.add(small);
|
||||
} else if (small.y + small.height() > bottom) {
|
||||
bottom = small.y + small.height();
|
||||
lastX = small.x;
|
||||
}
|
||||
}
|
||||
|
||||
if (!toMove.isEmpty()) {
|
||||
for (Image small : toMove) {
|
||||
small.frame(getSmallFrame());
|
||||
small.scale.set(scale * Random.Float(0.75f, 1.25f));
|
||||
float flex = 0;
|
||||
do {
|
||||
small.x = Random.Float(small.width() / 3f, width - 4 * small.width() / 3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(small.x - lastX) < density * (small.width() - flex));
|
||||
small.y = bottom - small.height()/2f + Random.Float(small.height() / 2f, small.height()) / density;
|
||||
small.angle = Random.Float(-20, 20);
|
||||
bottom = small.y + small.height();
|
||||
lastX = small.x;
|
||||
}
|
||||
toMove.clear();
|
||||
}
|
||||
|
||||
//smalls are ~115 tall, add a bit for safety
|
||||
float padding = 150 - (150 / 2f / density);
|
||||
while (bottom < (height + padding)) {
|
||||
Image small = new Image(Assets.Splashes.Title.FRONT_SMALL);
|
||||
small.frame(getSmallFrame());
|
||||
small.scale.set(scale * Random.Float(0.75f, 1.25f));
|
||||
small.brightness(0.8f);
|
||||
|
||||
float flex = 0;
|
||||
do {
|
||||
small.x = Random.Float(small.width() / 3f, width - 4 * small.width() / 3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(small.x - lastX) < density * (small.width() - flex));
|
||||
small.y = bottom - small.height()/2f + Random.Float(small.height() / 2f, small.height()) / density;
|
||||
small.angle = Random.Float(-20, 20);
|
||||
|
||||
smallFars.add(small);
|
||||
smallFarLayer.add(small);
|
||||
|
||||
bottom = small.y + small.height();
|
||||
lastX = small.x;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFrontSmallLayer(float scale, float shift){
|
||||
float bottom = 0;
|
||||
float lastX = 0;
|
||||
|
||||
for (Image small : smallCloses){
|
||||
small.y -= shift;
|
||||
if (small.y + small.height() < -20){
|
||||
toMove.add(small);
|
||||
} else if (small.y + small.height() > bottom){
|
||||
bottom = small.y + small.height();
|
||||
lastX = small.x;
|
||||
}
|
||||
}
|
||||
|
||||
if (!toMove.isEmpty()){
|
||||
for (Image small : toMove){
|
||||
small.frame(getSmallFrame());
|
||||
small.scale.set(scale * Random.Float(2f, 2.5f));
|
||||
float flex = 0;
|
||||
do {
|
||||
small.x = Random.Float(-small.width()/3f, width - 2*small.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(small.x - lastX) < density*(small.width() - flex));
|
||||
small.y = bottom - small.height() + Random.Float(small.height()/2f, small.height())/density;
|
||||
small.angle = Random.Float(-20, 20);
|
||||
bottom = small.y + small.height();
|
||||
lastX = small.x;
|
||||
}
|
||||
toMove.clear();
|
||||
}
|
||||
|
||||
//smalls are ~115 tall, add a bit for safety
|
||||
float padding = 150 - (150/2f / density);
|
||||
while (bottom < (height + padding)){
|
||||
Image small = new Image(Assets.Splashes.Title.FRONT_SMALL);
|
||||
small.frame(getSmallFrame());
|
||||
small.scale.set(scale * Random.Float(2f, 2.5f));
|
||||
|
||||
float flex = 0;
|
||||
do {
|
||||
small.x = Random.Float(-small.width()/3f, width - 2*small.width()/3f);
|
||||
flex += 1;
|
||||
} while (Math.abs(small.x - lastX) < density*(small.width() - flex));
|
||||
small.y = bottom - small.height() + Random.Float(small.height()/2f, small.height())/density;
|
||||
small.angle = Random.Float(-20, 20);
|
||||
|
||||
smallCloses.add(small);
|
||||
smallCloseLayer.add(small);
|
||||
|
||||
bottom = small.y + small.height();
|
||||
lastX = small.x;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -86,6 +86,41 @@ public class v3_X_Changes {
|
||||
changes.hardlight(Window.TITLE_COLOR);
|
||||
changeInfos.add(changes);
|
||||
|
||||
changes = new ChangeInfo("v3.2.4", false, null);
|
||||
changes.hardlight(Window.TITLE_COLOR);
|
||||
changeInfos.add(changes);
|
||||
|
||||
changes.addButton( new ChangeButton(Icons.get(Icons.SHPX), "Dev Commentary",
|
||||
"Hey Folks, here's what is hopefully the last patch with major changes following v3.2, but it's a doozy!\n" +
|
||||
"\n" +
|
||||
"In addition to more mobile improvements, there's also a just recently finished overhaul to the title screen background!\n" +
|
||||
"\n" +
|
||||
"Note that both of the big new things in this update are likely to have a few quirks. If something seems out of place please let me know! I do expect to do one or two more patches after v3.2.4 to fix any minor issues that crop up."));
|
||||
|
||||
changes.addButton(new ChangeButton(Icons.get(Icons.DISPLAY_LAND), "New Title Screen Background!",
|
||||
"**Shattered Pixel Dungeon has a new title screen background, with art by Aleksandar Komitov!**\n" +
|
||||
"\n" +
|
||||
"This new background is meant to both extend the classis archs, and tie the title screens more directly to the region splash arts! It features randomly selected floating fully rendered chunks of dungeon in front of an arch back-layer.\n" +
|
||||
"\n" +
|
||||
"Currently the background most strongly ties into the sewers region, but we have plans to add more variants in the future, one for each dungeon region!"));
|
||||
|
||||
changes.addButton(new ChangeButton(Icons.get(Icons.DISPLAY_PORT), "Mobile Layout Changes",
|
||||
"**Shattered now renders in true fullscreen on most mobile devices!**\n" +
|
||||
"\n" +
|
||||
"The status bar at the top of the in-game UI has been modified to work around small and medium sized hole punches and rounded corners on modern displays! This includes the dynamic island on modern iPhones. Thanks to these adjustments it's now possible for the UI to move up and for the game to display in true fullscreen during gameplay! Devices with larger cutouts like full-sized notches will unfortunately still have a dark bar on the top, as there isn't room for the UI.\n" +
|
||||
"\n" +
|
||||
"This also comes with a few other benefits for all mobile players. The XP bar has been moved and is now much more visible, and the hero buff bar now supports two rows, rather than compressing as heavily when there are many buffs at once."));
|
||||
|
||||
changes.addButton(new ChangeButton(Icons.get(Icons.PREFS), "v3.2.4 Beta Tweaks",
|
||||
"During v3.2.4's short beta I got a lot of good feedback about the new UI, and have made the following tweaks to address feedback before full release:\n" +
|
||||
"\n" +
|
||||
"**-** The HP bar can now shrink and/or reposition slightly if it would otherwise be cut off by a display cutout. The HP bar should shrink by about 20% at most.\n" +
|
||||
"**-** Adjusted buff bar layout logic to reduce cases of top row getting slightly cut off by display cutouts on Android.\n" +
|
||||
"**-** The menu pane on the right will now move to the left if there is room to do so and it would otherwise be cut off by a display cutout.\n" +
|
||||
"**-** Slightly increased the permissiveness for what cutouts the game will try to render into. This should let the game render in true fullscreen on some devices with hole punches that it couldn't previously.\n" +
|
||||
"**-** Improved how buff bar layout handles iOS dynamic island at smaller scale values.\n" +
|
||||
"**-** Renamed the mobile fullscreen setting to 'hide navigation bar' or 'hide gesture bar'"));
|
||||
|
||||
changes = new ChangeInfo("v3.2.3", false, null);
|
||||
changes.hardlight(Window.TITLE_COLOR);
|
||||
changeInfos.add(changes);
|
||||
|
||||
@@ -236,7 +236,16 @@ public class WndSettings extends WndTabbed {
|
||||
sep1 = new ColorBlock(1, 1, 0xFF000000);
|
||||
add(sep1);
|
||||
|
||||
chkFullscreen = new CheckBox( Messages.get(this, "fullscreen") ) {
|
||||
String fullscreenText = Messages.get(this, "fullscreen");
|
||||
//TODO English only for now, make sure to translate later
|
||||
if (Messages.lang() == Languages.ENGLISH){
|
||||
if (DeviceCompat.isAndroid()){
|
||||
fullscreenText = Messages.get(this, "hide_navigation");
|
||||
} else if (DeviceCompat.isiOS()){
|
||||
fullscreenText = Messages.get(this, "hide_gesture");
|
||||
}
|
||||
}
|
||||
chkFullscreen = new CheckBox( fullscreenText ) {
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
apply plugin: 'java-library'
|
||||
|
||||
dependencies {
|
||||
implementation "com.badlogicgames.gdx:gdx:$gdxVersion"
|
||||
implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion"
|
||||
implementation "com.github.xpenatan.gdx-teavm:backend-teavm:$gdxTeaVMVersion"
|
||||
implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
|
||||
implementation "com.github.xpenatan.gdx-teavm:gdx-freetype-teavm:$gdxTeaVMVersion"
|
||||
implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
|
||||
implementation project(':core')
|
||||
implementation "org.teavm:teavm-jso:$teaVMVersion"
|
||||
implementation "org.teavm:teavm-core:$teaVMVersion"
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://teavm.org/maven/repository' }
|
||||
mavenCentral()
|
||||
maven { url = uri("https://central.sonatype.com/repository/maven-snapshots/") }
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
maven { url 'https://teavm.org/maven/repository' }
|
||||
}
|
||||
|
||||
tasks.register('compileClient', JavaExec) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
*
|
||||
* Modifications made by Konsthol on 13/4/25:
|
||||
* - Adjusted to compile Shattered Pixel Dungeon
|
||||
* Modifications made by Konsthol on 27/9/25:
|
||||
* - Adjusted to be used with gdx-teavm 1.2.4
|
||||
*
|
||||
* Copyright 2022 Daniel Hollingsworth
|
||||
*/
|
||||
@@ -25,6 +27,8 @@ import org.teavm.vm.TeaVMOptimizationLevel;
|
||||
public class Compile {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
File webappDir = new File("../release/webapp");
|
||||
Configure.deleteDir(webappDir);
|
||||
Configure.configure();
|
||||
|
||||
TeaVMTool tool = new TeaVMTool();
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
*
|
||||
* Modifications made by Konsthol on 13/4/25:
|
||||
* - Adjusted to compile Shattered Pixel Dungeon
|
||||
* Modifications made by Konsthol on 27/9/25:
|
||||
* - Adjusted to be used with gdx-teavm 1.2.4
|
||||
*
|
||||
* Copyright 2022 Daniel Hollingsworth
|
||||
*/
|
||||
@@ -27,12 +29,9 @@ import org.teavm.vm.TeaVMOptimizationLevel;
|
||||
|
||||
public class Configure {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
deleteDir(new File("../release/webapp"));
|
||||
configure();
|
||||
}
|
||||
public static void main(String[] args) {}
|
||||
|
||||
private static void deleteDir(File dir) {
|
||||
public static void deleteDir(File dir) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
@@ -48,8 +47,8 @@ public class Configure {
|
||||
conf.shouldGenerateAssetFile = true;
|
||||
conf.webappPath = new File("../release").getAbsolutePath();
|
||||
conf.targetType = TeaTargetType.JAVASCRIPT;
|
||||
//conf.targetType = TeaTargetType.WEBASSEMBLY;
|
||||
TeaBuilder.config(conf);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class TeaVMLauncher {
|
||||
}
|
||||
|
||||
private static void initializeServices() {
|
||||
Game.version = "3.2.3";
|
||||
Game.versionCode = 868;
|
||||
Game.version = "3.2.4";
|
||||
Game.versionCode = 875;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,15 @@ public class IOSLauncher extends IOSApplication.Delegate {
|
||||
config.addIosDevice("IPHONE_15_PLUS", "iPhone15,5", 460);
|
||||
config.addIosDevice("IPHONE_15_PRO", "iPhone16,1", 460);
|
||||
config.addIosDevice("IPHONE_15_PRO_MAX", "iPhone16,2", 460);
|
||||
config.addIosDevice("IPHONE_16_PRO", "iPhone17,1", 460);
|
||||
config.addIosDevice("IPHONE_16_PRO_MAX", "iPhone17,2", 460);
|
||||
config.addIosDevice("IPHONE_16", "iPhone17,3", 460);
|
||||
config.addIosDevice("IPHONE_16_PLUS", "iPhone17,4", 460);
|
||||
config.addIosDevice("IPHONE_16E", "iPhone17,5", 460);
|
||||
config.addIosDevice("IPHONE_17_PRO", "iPhone18,1", 460);
|
||||
config.addIosDevice("IPHONE_17_PRO_MAX", "iPhone18,2", 460);
|
||||
config.addIosDevice("IPHONE_17", "iPhone18,3", 460);
|
||||
config.addIosDevice("IPHONE_AIR", "iPhone18,4", 460);
|
||||
|
||||
config.addIosDevice("IPAD_7G_WIFI", "iPad7,11", 264);
|
||||
config.addIosDevice("IPAD_7G_WIFI_CELLULAR", "iPad7,12", 264);
|
||||
@@ -144,16 +153,37 @@ public class IOSLauncher extends IOSApplication.Delegate {
|
||||
config.addIosDevice("IPAD_PRO_11_3G", "iPad13,7", 264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_5G", "iPad13,8", 264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_5G", "iPad13,9", 264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_5G", "iPad13,10", 264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_5G", "iPad13,11", 264);
|
||||
config.addIosDevice("IPAD_AIR_5G_WIF", "iPad13,16", 264);
|
||||
config.addIosDevice("IPAD_AIR_5G_WIFI_CELLULAR", "iPad13,17", 264);
|
||||
config.addIosDevice("IPAD_10G", "iPad13,18", 264);
|
||||
config.addIosDevice("IPAD_10G", "iPad13,19", 264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_5G", "iPad13,10",264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_5G", "iPad13,11",264);
|
||||
config.addIosDevice("IPAD_AIR_5G_WIFI", "iPad13,16",264);
|
||||
config.addIosDevice("IPAD_AIR_5G_WIFI_CELLULAR", "iPad13,17",264);
|
||||
config.addIosDevice("IPAD_10G", "iPad13,18",264);
|
||||
config.addIosDevice("IPAD_10G", "iPad13,19",264);
|
||||
config.addIosDevice("IPAD_PRO_11_4G", "iPad14,3", 264);
|
||||
config.addIosDevice("IPAD_PRO_11_4G", "iPad14,4", 264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_6G", "iPad14,5", 264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_6G", "iPad14,6", 264);
|
||||
config.addIosDevice("IPAD_AIR_11_6G_WIFI", "iPad14,8", 264);
|
||||
config.addIosDevice("IPAD_AIR_11_6G_WIFI_CELLULAR", "iPad14,9", 264);
|
||||
config.addIosDevice("IPAD_AIR_13_6G_WIFI", "iPad14,10",264);
|
||||
config.addIosDevice("IPAD_AIR_13_6G_WIFI_CELLULAR", "iPad14,11",264);
|
||||
config.addIosDevice("IPAD_AIR_11_7G_WIFI", "iPad15,3", 264);
|
||||
config.addIosDevice("IPAD_AIR_11_7G_WIFI_CELLULAR", "iPad15,4", 264);
|
||||
config.addIosDevice("IPAD_AIR_13_7G_WIFI", "iPad15,5", 264);
|
||||
config.addIosDevice("IPAD_AIR_13_7G_WIFI_CELLULAR", "iPad15,6", 264);
|
||||
config.addIosDevice("IPAD_11G_WIFI", "iPad15,7", 264);
|
||||
config.addIosDevice("IPAD_11G_WIFI_CELLULAR", "iPad15,8", 264);
|
||||
config.addIosDevice("IPAD_MINI_7G_WIFI", "iPad16,1", 326);
|
||||
config.addIosDevice("IPAD_MINI_7G_WIFI_CELLULAR", "iPad16,2", 326);
|
||||
config.addIosDevice("IPAD_PRO_11_5G", "iPad16,3", 264);
|
||||
config.addIosDevice("IPAD_PRO_11_5G", "iPad16,4", 264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_7G", "iPad16,5", 264);
|
||||
config.addIosDevice("IPAD_PRO_12.9_7G", "iPad16,6", 264);
|
||||
|
||||
//also override simulator devices for better testing when simulating modern iPhones
|
||||
config.addIosDevice("SIMULATOR_32", "i386", 460);
|
||||
config.addIosDevice("SIMULATOR_64", "x86_64", 460);
|
||||
config.addIosDevice("SIMULATOR_ARM64", "arm64", 460);
|
||||
|
||||
return new IOSApplication(new ShatteredPixelDungeon(new IOSPlatformSupport()), config);
|
||||
}
|
||||
|
||||
@@ -62,26 +62,22 @@ public class IOSPlatformSupport extends PlatformSupport {
|
||||
public RectF getSafeInsets(int level) {
|
||||
RectF insets = super.getSafeInsets(INSET_ALL);
|
||||
|
||||
//magic number BS for larger status bar caused by dynamic island
|
||||
boolean hasDynamicIsland = insets.top / Gdx.graphics.getBackBufferScale() >= 51;
|
||||
|
||||
//iOS gives us ALL insets by default, and so we need to filter from there:
|
||||
|
||||
//ignore the home indicator if we're in fullscreen
|
||||
if (!supportsFullScreen() || SPDSettings.fullscreen()){
|
||||
insets.bottom = 0;
|
||||
} else {
|
||||
//otherwise bottom inset is pretty big, halve it
|
||||
insets.bottom /= 2;
|
||||
}
|
||||
|
||||
//only cutouts can be on top/left/right, which are never blocking
|
||||
if (level == INSET_BLK){
|
||||
insets.left = insets.top = insets.right = 0;
|
||||
} else if (level == INSET_LRG){
|
||||
//Dynamic Island counts as a 'small cutout', we have to use status bar height to get it =I
|
||||
CGRect statusBarFrame = UIApplication.getSharedApplication().getStatusBarFrame();
|
||||
double statusBarHeight = Math.min(statusBarFrame.getWidth(), statusBarFrame.getHeight());
|
||||
if (statusBarHeight >= 51){ //magic number BS for larger status bar caused by island
|
||||
insets.left = insets.top = insets.right = 0;
|
||||
}
|
||||
} else if (level == INSET_LRG && hasDynamicIsland){
|
||||
//Dynamic Island counts as a 'small cutout'
|
||||
insets.left = insets.top = insets.right = 0;
|
||||
}
|
||||
|
||||
//if we are in landscape, the display cutout is only actually on one side, so cancel the other
|
||||
@@ -93,6 +89,12 @@ public class IOSPlatformSupport extends PlatformSupport {
|
||||
}
|
||||
}
|
||||
|
||||
//finally iOS is very conservative with these insets, we can shrink them a bit.
|
||||
insets.top /= hasDynamicIsland ? 1.2f : 1.4f;
|
||||
insets.left /= hasDynamicIsland ? 1.2f : 1.4f;
|
||||
insets.right /= hasDynamicIsland ? 1.2f : 1.4f;
|
||||
insets.bottom /= 2; //home bar inset is especially big for no reason
|
||||
|
||||
return insets;
|
||||
}
|
||||
|
||||
|
||||