V0.1.0 Partial Commit
changed package and application names to differentiate from main PD release
This commit is contained in:
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
* Pixel Dungeon
|
||||
* Copyright (C) 2012-2014 Oleg Dolya
|
||||
*
|
||||
* 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.scenes;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.*;
|
||||
import com.watabou.noosa.Camera;
|
||||
import com.watabou.noosa.Game;
|
||||
import com.watabou.noosa.Group;
|
||||
import com.watabou.noosa.SkinnedBlock;
|
||||
import com.watabou.noosa.Visual;
|
||||
import com.watabou.noosa.audio.Music;
|
||||
import com.watabou.noosa.audio.Sample;
|
||||
import com.watabou.noosa.particles.Emitter;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.BannerSprites;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.BlobEmitter;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.EmoIcon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Flare;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Ripple;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.SpellSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfBlink;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.plants.Plant;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.DiscardedItemSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.HeroSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.PlantSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.AttackIndicator;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Banner;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.BusyIndicator;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.GameLog;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.HealthIndicator;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.QuickSlot;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StatusPane;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Toast;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Toolbar;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag.Mode;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndGame;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndStory;
|
||||
import com.watabou.utils.Random;
|
||||
|
||||
public class GameScene extends PixelScene {
|
||||
|
||||
private static final String TXT_WELCOME = "Welcome to the level %d of Pixel Dungeon!";
|
||||
private static final String TXT_WELCOME_BACK = "Welcome back to the level %d of Pixel Dungeon!";
|
||||
private static final String TXT_NIGHT_MODE = "Be cautious, since the dungeon is even more dangerous at night!";
|
||||
|
||||
private static final String TXT_CHASM = "Your steps echo across the dungeon.";
|
||||
private static final String TXT_WATER = "You hear the water splashing around you.";
|
||||
private static final String TXT_GRASS = "The smell of vegetation is thick in the air.";
|
||||
private static final String TXT_SECRETS = "The atmosphere hints that this floor hides many secrets.";
|
||||
|
||||
static GameScene scene;
|
||||
|
||||
private SkinnedBlock water;
|
||||
private DungeonTilemap tiles;
|
||||
private FogOfWar fog;
|
||||
private HeroSprite hero;
|
||||
|
||||
private GameLog log;
|
||||
|
||||
private BusyIndicator busy;
|
||||
|
||||
private static CellSelector cellSelector;
|
||||
|
||||
private Group terrain;
|
||||
private Group ripples;
|
||||
private Group plants;
|
||||
private Group heaps;
|
||||
private Group mobs;
|
||||
private Group emitters;
|
||||
private Group effects;
|
||||
private Group gases;
|
||||
private Group spells;
|
||||
private Group statuses;
|
||||
private Group emoicons;
|
||||
|
||||
private Toolbar toolbar;
|
||||
private Toast prompt;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
|
||||
Music.INSTANCE.play( Assets.TUNE, true );
|
||||
Music.INSTANCE.volume( 1f );
|
||||
|
||||
ShatteredPixelDungeon.lastClass(Dungeon.hero.heroClass.ordinal());
|
||||
|
||||
super.create();
|
||||
Camera.main.zoom( defaultZoom + ShatteredPixelDungeon.zoom() );
|
||||
|
||||
scene = this;
|
||||
|
||||
terrain = new Group();
|
||||
add( terrain );
|
||||
|
||||
water = new SkinnedBlock(
|
||||
Level.WIDTH * DungeonTilemap.SIZE,
|
||||
Level.HEIGHT * DungeonTilemap.SIZE,
|
||||
Dungeon.level.waterTex() );
|
||||
terrain.add( water );
|
||||
|
||||
ripples = new Group();
|
||||
terrain.add( ripples );
|
||||
|
||||
tiles = new DungeonTilemap();
|
||||
terrain.add( tiles );
|
||||
|
||||
Dungeon.level.addVisuals( this );
|
||||
|
||||
plants = new Group();
|
||||
add( plants );
|
||||
|
||||
int size = Dungeon.level.plants.size();
|
||||
for (int i=0; i < size; i++) {
|
||||
addPlantSprite( Dungeon.level.plants.valueAt( i ) );
|
||||
}
|
||||
|
||||
heaps = new Group();
|
||||
add( heaps );
|
||||
|
||||
size = Dungeon.level.heaps.size();
|
||||
for (int i=0; i < size; i++) {
|
||||
addHeapSprite( Dungeon.level.heaps.valueAt( i ) );
|
||||
}
|
||||
|
||||
emitters = new Group();
|
||||
effects = new Group();
|
||||
emoicons = new Group();
|
||||
|
||||
mobs = new Group();
|
||||
add( mobs );
|
||||
|
||||
for (Mob mob : Dungeon.level.mobs) {
|
||||
addMobSprite( mob );
|
||||
if (Statistics.amuletObtained) {
|
||||
mob.beckon( Dungeon.hero.pos );
|
||||
}
|
||||
}
|
||||
|
||||
add( emitters );
|
||||
add( effects );
|
||||
|
||||
gases = new Group();
|
||||
add( gases );
|
||||
|
||||
for (Blob blob : Dungeon.level.blobs.values()) {
|
||||
blob.emitter = null;
|
||||
addBlobSprite( blob );
|
||||
}
|
||||
|
||||
fog = new FogOfWar( Level.WIDTH, Level.HEIGHT );
|
||||
fog.updateVisibility( Dungeon.visible, Dungeon.level.visited, Dungeon.level.mapped );
|
||||
add( fog );
|
||||
|
||||
brightness( ShatteredPixelDungeon.brightness() );
|
||||
|
||||
spells = new Group();
|
||||
add( spells );
|
||||
|
||||
statuses = new Group();
|
||||
add( statuses );
|
||||
|
||||
add( emoicons );
|
||||
|
||||
hero = new HeroSprite();
|
||||
hero.place( Dungeon.hero.pos );
|
||||
hero.updateArmor();
|
||||
mobs.add( hero );
|
||||
|
||||
|
||||
add( new HealthIndicator() );
|
||||
|
||||
add( cellSelector = new CellSelector( tiles ) );
|
||||
|
||||
StatusPane sb = new StatusPane();
|
||||
sb.camera = uiCamera;
|
||||
sb.setSize( uiCamera.width, 0 );
|
||||
add( sb );
|
||||
|
||||
toolbar = new Toolbar();
|
||||
toolbar.camera = uiCamera;
|
||||
toolbar.setRect( 0,uiCamera.height - toolbar.height(), uiCamera.width, toolbar.height() );
|
||||
add( toolbar );
|
||||
|
||||
AttackIndicator attack = new AttackIndicator();
|
||||
attack.camera = uiCamera;
|
||||
attack.setPos(
|
||||
uiCamera.width - attack.width(),
|
||||
toolbar.top() - attack.height() );
|
||||
add( attack );
|
||||
|
||||
log = new GameLog();
|
||||
log.camera = uiCamera;
|
||||
log.setRect( 0, toolbar.top(), attack.left(), 0 );
|
||||
add( log );
|
||||
|
||||
if (Dungeon.depth < Statistics.deepestFloor) {
|
||||
GLog.i( TXT_WELCOME_BACK, Dungeon.depth );
|
||||
} else {
|
||||
GLog.i( TXT_WELCOME, Dungeon.depth );
|
||||
Sample.INSTANCE.play( Assets.SND_DESCEND );
|
||||
}
|
||||
switch (Dungeon.level.feeling) {
|
||||
case CHASM:
|
||||
GLog.w( TXT_CHASM );
|
||||
break;
|
||||
case WATER:
|
||||
GLog.w( TXT_WATER );
|
||||
break;
|
||||
case GRASS:
|
||||
GLog.w( TXT_GRASS );
|
||||
break;
|
||||
default:
|
||||
}
|
||||
if (Dungeon.level instanceof RegularLevel &&
|
||||
((RegularLevel)Dungeon.level).secretDoors > Random.IntRange( 3, 4 )) {
|
||||
GLog.w( TXT_SECRETS );
|
||||
}
|
||||
if (Dungeon.nightMode && !Dungeon.bossLevel()) {
|
||||
GLog.w( TXT_NIGHT_MODE );
|
||||
}
|
||||
|
||||
busy = new BusyIndicator();
|
||||
busy.camera = uiCamera;
|
||||
busy.x = 1;
|
||||
busy.y = sb.bottom() + 1;
|
||||
add( busy );
|
||||
|
||||
switch (InterlevelScene.mode) {
|
||||
case RESURRECT:
|
||||
WandOfBlink.appear( Dungeon.hero, Dungeon.level.entrance );
|
||||
new Flare( 8, 32 ).color( 0xFFFF66, true ).show( hero, 2f ) ;
|
||||
break;
|
||||
case RETURN:
|
||||
WandOfBlink.appear( Dungeon.hero, Dungeon.hero.pos );
|
||||
break;
|
||||
case FALL:
|
||||
Chasm.heroLand();
|
||||
break;
|
||||
case DESCEND:
|
||||
switch (Dungeon.depth) {
|
||||
case 1:
|
||||
WndStory.showChapter( WndStory.ID_SEWERS );
|
||||
break;
|
||||
case 6:
|
||||
WndStory.showChapter( WndStory.ID_PRISON );
|
||||
break;
|
||||
case 11:
|
||||
WndStory.showChapter( WndStory.ID_CAVES );
|
||||
break;
|
||||
case 16:
|
||||
WndStory.showChapter( WndStory.ID_METROPOLIS );
|
||||
break;
|
||||
case 22:
|
||||
WndStory.showChapter( WndStory.ID_HALLS );
|
||||
break;
|
||||
}
|
||||
if (Dungeon.hero.isAlive() && Dungeon.depth != 22) {
|
||||
Badges.validateNoKilling();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
Camera.main.target = hero;
|
||||
fadeIn();
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
|
||||
scene = null;
|
||||
Badges.saveGlobal();
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void pause() {
|
||||
try {
|
||||
Dungeon.saveAll();
|
||||
Badges.saveGlobal();
|
||||
} catch (IOException e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void update() {
|
||||
if (Dungeon.hero == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.update();
|
||||
|
||||
water.offset( 0, -5 * Game.elapsed );
|
||||
|
||||
Actor.process();
|
||||
|
||||
if (Dungeon.hero.ready && !Dungeon.hero.paralysed) {
|
||||
log.newLine();
|
||||
}
|
||||
|
||||
cellSelector.enabled = Dungeon.hero.ready;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBackPressed() {
|
||||
if (!cancel()) {
|
||||
add( new WndGame() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMenuPressed() {
|
||||
if (Dungeon.hero.ready) {
|
||||
selectItem( null, WndBag.Mode.ALL, null );
|
||||
}
|
||||
}
|
||||
|
||||
public void brightness( boolean value ) {
|
||||
water.rm = water.gm = water.bm =
|
||||
tiles.rm = tiles.gm = tiles.bm =
|
||||
value ? 1.5f : 1.0f;
|
||||
if (value) {
|
||||
fog.am = +2f;
|
||||
fog.aa = -1f;
|
||||
} else {
|
||||
fog.am = +1f;
|
||||
fog.aa = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void addHeapSprite( Heap heap ) {
|
||||
ItemSprite sprite = heap.sprite = (ItemSprite)heaps.recycle( ItemSprite.class );
|
||||
sprite.revive();
|
||||
sprite.link( heap );
|
||||
heaps.add( sprite );
|
||||
}
|
||||
|
||||
private void addDiscardedSprite( Heap heap ) {
|
||||
heap.sprite = (DiscardedItemSprite)heaps.recycle( DiscardedItemSprite.class );
|
||||
heap.sprite.revive();
|
||||
heap.sprite.link( heap );
|
||||
heaps.add( heap.sprite );
|
||||
}
|
||||
|
||||
private void addPlantSprite( Plant plant ) {
|
||||
(plant.sprite = (PlantSprite)plants.recycle( PlantSprite.class )).reset( plant );
|
||||
}
|
||||
|
||||
private void addBlobSprite( final Blob gas ) {
|
||||
if (gas.emitter == null) {
|
||||
gases.add( new BlobEmitter( gas ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void addMobSprite( Mob mob ) {
|
||||
CharSprite sprite = mob.sprite();
|
||||
sprite.visible = Dungeon.visible[mob.pos];
|
||||
mobs.add( sprite );
|
||||
sprite.link( mob );
|
||||
}
|
||||
|
||||
private void prompt( String text ) {
|
||||
|
||||
if (prompt != null) {
|
||||
prompt.killAndErase();
|
||||
prompt = null;
|
||||
}
|
||||
|
||||
if (text != null) {
|
||||
prompt = new Toast( text ) {
|
||||
@Override
|
||||
protected void onClose() {
|
||||
cancel();
|
||||
}
|
||||
};
|
||||
prompt.camera = uiCamera;
|
||||
prompt.setPos( (uiCamera.width - prompt.width()) / 2, uiCamera.height - 60 );
|
||||
add( prompt );
|
||||
}
|
||||
}
|
||||
|
||||
private void showBanner( Banner banner ) {
|
||||
banner.camera = uiCamera;
|
||||
banner.x = align( uiCamera, (uiCamera.width - banner.width) / 2 );
|
||||
banner.y = align( uiCamera, (uiCamera.height - banner.height) / 3 );
|
||||
add( banner );
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
|
||||
public static void add( Plant plant ) {
|
||||
if (scene != null) {
|
||||
scene.addPlantSprite( plant );
|
||||
}
|
||||
}
|
||||
|
||||
public static void add( Blob gas ) {
|
||||
Actor.add( gas );
|
||||
if (scene != null) {
|
||||
scene.addBlobSprite( gas );
|
||||
}
|
||||
}
|
||||
|
||||
public static void add( Heap heap ) {
|
||||
if (scene != null) {
|
||||
scene.addHeapSprite( heap );
|
||||
}
|
||||
}
|
||||
|
||||
public static void discard( Heap heap ) {
|
||||
if (scene != null) {
|
||||
scene.addDiscardedSprite( heap );
|
||||
}
|
||||
}
|
||||
|
||||
public static void add( Mob mob ) {
|
||||
Dungeon.level.mobs.add( mob );
|
||||
Actor.add( mob );
|
||||
Actor.occupyCell( mob );
|
||||
scene.addMobSprite( mob );
|
||||
}
|
||||
|
||||
public static void add( Mob mob, float delay ) {
|
||||
Dungeon.level.mobs.add( mob );
|
||||
Actor.addDelayed( mob, delay );
|
||||
Actor.occupyCell( mob );
|
||||
scene.addMobSprite( mob );
|
||||
}
|
||||
|
||||
public static void add( EmoIcon icon ) {
|
||||
scene.emoicons.add( icon );
|
||||
}
|
||||
|
||||
public static void effect( Visual effect ) {
|
||||
scene.effects.add( effect );
|
||||
}
|
||||
|
||||
public static Ripple ripple( int pos ) {
|
||||
Ripple ripple = (Ripple)scene.ripples.recycle( Ripple.class );
|
||||
ripple.reset( pos );
|
||||
return ripple;
|
||||
}
|
||||
|
||||
public static SpellSprite spellSprite() {
|
||||
return (SpellSprite)scene.spells.recycle( SpellSprite.class );
|
||||
}
|
||||
|
||||
public static Emitter emitter() {
|
||||
if (scene != null) {
|
||||
Emitter emitter = (Emitter)scene.emitters.recycle( Emitter.class );
|
||||
emitter.revive();
|
||||
return emitter;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static FloatingText status() {
|
||||
return scene != null ? (FloatingText)scene.statuses.recycle( FloatingText.class ) : null;
|
||||
}
|
||||
|
||||
public static void pickUp( Item item ) {
|
||||
scene.toolbar.pickup( item );
|
||||
}
|
||||
|
||||
public static void updateMap() {
|
||||
if (scene != null) {
|
||||
scene.tiles.updated.set( 0, 0, Level.WIDTH, Level.HEIGHT );
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateMap( int cell ) {
|
||||
if (scene != null) {
|
||||
scene.tiles.updated.union( cell % Level.WIDTH, cell / Level.WIDTH );
|
||||
}
|
||||
}
|
||||
|
||||
public static void discoverTile( int pos, int oldValue ) {
|
||||
if (scene != null) {
|
||||
scene.tiles.discover( pos, oldValue );
|
||||
}
|
||||
}
|
||||
|
||||
public static void show( Window wnd ) {
|
||||
cancelCellSelector();
|
||||
scene.add( wnd );
|
||||
}
|
||||
|
||||
public static void afterObserve() {
|
||||
if (scene != null) {
|
||||
scene.fog.updateVisibility( Dungeon.visible, Dungeon.level.visited, Dungeon.level.mapped );
|
||||
|
||||
for (Mob mob : Dungeon.level.mobs) {
|
||||
mob.sprite.visible = Dungeon.visible[mob.pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void flash( int color ) {
|
||||
scene.fadeIn( 0xFF000000 | color, true );
|
||||
}
|
||||
|
||||
public static void gameOver() {
|
||||
Banner gameOver = new Banner( BannerSprites.get( BannerSprites.Type.GAME_OVER ) );
|
||||
gameOver.show( 0x000000, 1f );
|
||||
scene.showBanner( gameOver );
|
||||
|
||||
Sample.INSTANCE.play( Assets.SND_DEATH );
|
||||
}
|
||||
|
||||
public static void bossSlain() {
|
||||
if (Dungeon.hero.isAlive()) {
|
||||
Banner bossSlain = new Banner( BannerSprites.get( BannerSprites.Type.BOSS_SLAIN ) );
|
||||
bossSlain.show( 0xFFFFFF, 0.3f, 5f );
|
||||
scene.showBanner( bossSlain );
|
||||
|
||||
Sample.INSTANCE.play( Assets.SND_BOSS );
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleCell( int cell ) {
|
||||
cellSelector.select( cell );
|
||||
}
|
||||
|
||||
public static void selectCell( CellSelector.Listener listener ) {
|
||||
cellSelector.listener = listener;
|
||||
scene.prompt( listener.prompt() );
|
||||
}
|
||||
|
||||
private static boolean cancelCellSelector() {
|
||||
if (cellSelector.listener != null && cellSelector.listener != defaultCellListener) {
|
||||
cellSelector.cancel();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static WndBag selectItem( WndBag.Listener listener, WndBag.Mode mode, String title ) {
|
||||
cancelCellSelector();
|
||||
|
||||
WndBag wnd = mode == Mode.SEED ?
|
||||
WndBag.seedPouch( listener, mode, title ) :
|
||||
WndBag.lastBag( listener, mode, title );
|
||||
scene.add( wnd );
|
||||
|
||||
return wnd;
|
||||
}
|
||||
|
||||
static boolean cancel() {
|
||||
if (Dungeon.hero.curAction != null || Dungeon.hero.restoreHealth) {
|
||||
|
||||
Dungeon.hero.curAction = null;
|
||||
Dungeon.hero.restoreHealth = false;
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return cancelCellSelector();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void ready() {
|
||||
selectCell( defaultCellListener );
|
||||
QuickSlot.cancel();
|
||||
}
|
||||
|
||||
private static final CellSelector.Listener defaultCellListener = new CellSelector.Listener() {
|
||||
@Override
|
||||
public void onSelect( Integer cell ) {
|
||||
if (cell != -1) {
|
||||
Dungeon.hero.handle( cell );
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String prompt() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user