Balancing / Bugfixes / Badly antialiased turret outlines
|
|
@ -26,6 +26,10 @@ allprojects{
|
|||
gdxVersion = '1.9.9'
|
||||
roboVMVersion = '2.3.0'
|
||||
arcHash = null
|
||||
|
||||
debugged = {
|
||||
return new File(projectDir.parent, '../debug').exists() && System.properties["release"] == null
|
||||
}
|
||||
|
||||
localArc = {
|
||||
return (System.properties["release"] == null || System.properties["release"] == "false") && new File(projectDir.parent, '../Arc').exists()
|
||||
|
|
@ -117,7 +121,7 @@ project(":desktop"){
|
|||
compile project(":core")
|
||||
compile project(":net")
|
||||
|
||||
if(new File(projectDir.parent, '../debug').exists() && System.properties["release"] == null) compile project(":debug")
|
||||
if(debugged()) compile project(":debug")
|
||||
|
||||
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
||||
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
||||
|
|
@ -194,7 +198,7 @@ project(":core"){
|
|||
|
||||
compile arcModule("arc-core")
|
||||
compile arcModule("extensions:freetype")
|
||||
if(localArc()) compile arcModule("extensions:recorder")
|
||||
if(localArc() && debugged()) compile arcModule("extensions:recorder")
|
||||
|
||||
compileOnly project(":annotations")
|
||||
annotationProcessor project(":annotations")
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 135 B After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
|
@ -469,7 +469,7 @@ item.metaglass.description = A super-tough glass compound. Extensively used for
|
|||
item.scrap.name = Scrap
|
||||
item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals.
|
||||
liquid.water.name = Water
|
||||
liquid.lava.name = Lava
|
||||
liquid.slag.name = Slag
|
||||
liquid.oil.name = Oil
|
||||
liquid.cryofluid.name = Cryofluid
|
||||
mech.alpha-mech.name = Alpha
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
|
@ -457,7 +457,7 @@ public class Blocks implements ContentList{
|
|||
spinnerSpeed = 3f;
|
||||
size = 2;
|
||||
|
||||
consumes.liquid(Liquids.slag, 0.3f);
|
||||
consumes.liquid(Liquids.slag, 0.1f);
|
||||
}};
|
||||
|
||||
cultivator = new Cultivator("cultivator"){{
|
||||
|
|
@ -467,6 +467,7 @@ public class Blocks implements ContentList{
|
|||
size = 2;
|
||||
hasLiquids = true;
|
||||
hasPower = true;
|
||||
hasItems = true;
|
||||
|
||||
consumes.power(0.80f);
|
||||
consumes.liquid(Liquids.water, 0.15f);
|
||||
|
|
@ -848,12 +849,12 @@ public class Blocks implements ContentList{
|
|||
|
||||
differentialGenerator = new DifferentialGenerator("differential-generator"){{
|
||||
requirements(Category.power, ItemStack.with(Items.copper, 140, Items.titanium, 100, Items.lead, 200, Items.silicon, 130, Items.metaglass, 100));
|
||||
powerProduction = 24f;
|
||||
itemDuration = 60f;
|
||||
powerProduction = 16f;
|
||||
itemDuration = 50f;
|
||||
consumes.remove(ConsumeItemFilter.class);
|
||||
consumes.remove(ConsumeLiquidFilter.class);
|
||||
consumes.item(Items.pyratite);
|
||||
consumes.liquid(Liquids.cryofluid, 0.06f);
|
||||
consumes.liquid(Liquids.cryofluid, 0.12f);
|
||||
size = 3;
|
||||
}};
|
||||
|
||||
|
|
@ -991,7 +992,7 @@ public class Blocks implements ContentList{
|
|||
alwaysUnlocked = true;
|
||||
|
||||
health = 1100;
|
||||
itemCapacity = 4000;
|
||||
itemCapacity = 5000;
|
||||
size = 3;
|
||||
}};
|
||||
|
||||
|
|
@ -999,15 +1000,15 @@ public class Blocks implements ContentList{
|
|||
requirements(Category.effect, () -> false, ItemStack.with(Items.titanium, 4000, Items.silicon, 2000));
|
||||
|
||||
health = 2000;
|
||||
itemCapacity = 7000;
|
||||
itemCapacity = 8000;
|
||||
size = 4;
|
||||
}};
|
||||
|
||||
coreNucleus = new CoreBlock("core-nucleus"){{
|
||||
requirements(Category.effect, () -> false, ItemStack.with(Items.titanium, 8000, Items.silicon, 4000, Items.surgealloy, 2000));
|
||||
|
||||
health = 3000;
|
||||
itemCapacity = 10000;
|
||||
health = 4000;
|
||||
itemCapacity = 12000;
|
||||
size = 5;
|
||||
}};
|
||||
|
||||
|
|
@ -1102,7 +1103,7 @@ public class Blocks implements ContentList{
|
|||
|
||||
lancer = new ChargeTurret("lancer"){{
|
||||
requirements(Category.turret, ItemStack.with(Items.copper, 50, Items.lead, 100, Items.silicon, 90));
|
||||
range = 135f;
|
||||
range = 155f;
|
||||
chargeTime = 50f;
|
||||
chargeMaxDelay = 30f;
|
||||
chargeEffects = 7;
|
||||
|
|
@ -1130,7 +1131,7 @@ public class Blocks implements ContentList{
|
|||
shootCone = 40f;
|
||||
rotatespeed = 8f;
|
||||
powerUsed = 1f / 2f;
|
||||
consumes.powerBuffered(50f);
|
||||
consumes.powerBuffered(80f);
|
||||
range = 80f;
|
||||
shootEffect = Fx.lightningShoot;
|
||||
heatColor = Color.RED;
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ public class Bullets implements ContentList{
|
|||
Color[] colors = {Pal.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Pal.lancerLaser, Color.WHITE};
|
||||
float[] tscales = {1f, 0.7f, 0.5f, 0.2f};
|
||||
float[] lenscales = {1f, 1.1f, 1.13f, 1.14f};
|
||||
float length = 140f;
|
||||
float length = 160f;
|
||||
|
||||
{
|
||||
hitEffect = Fx.hitLancer;
|
||||
|
|
|
|||
|
|
@ -185,7 +185,9 @@ public class World implements ApplicationListener{
|
|||
|
||||
addDarkness(tiles);
|
||||
|
||||
EntityQuery.resizeTree(0, 0, tiles.length * tilesize, tiles[0].length * tilesize);
|
||||
int padding = 50;
|
||||
|
||||
EntityQuery.resizeTree(-padding * tilesize, -padding * tilesize, (tiles.length + padding) * tilesize, (tiles[0].length + padding) * tilesize);
|
||||
|
||||
generating = false;
|
||||
Events.fire(new WorldLoadEvent());
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import io.anuke.arc.math.Angles;
|
|||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import static io.anuke.mindustry.Vars.headless;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class ScorchDecal extends Decal{
|
||||
|
|
@ -14,6 +15,7 @@ public class ScorchDecal extends Decal{
|
|||
private static final TextureRegion[] regions = new TextureRegion[scorches];
|
||||
|
||||
public static void create(float x, float y){
|
||||
if(headless) return;
|
||||
if(regions[0] == null){
|
||||
for(int i = 0; i < regions.length; i++){
|
||||
regions[i] = Core.atlas.find("scorch" + (i + 1));
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ import static io.anuke.mindustry.Vars.unitGroups;
|
|||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public class Drone extends FlyingUnit implements BuilderTrait{
|
||||
protected static int timerRepairEffect = timerIndex++;
|
||||
|
||||
protected Item targetItem;
|
||||
protected Tile mineTile;
|
||||
protected Queue<BuildRequest> placeQueue = new Queue<>();
|
||||
|
|
|
|||
|
|
@ -96,12 +96,14 @@ public class Block extends BlockStorage{
|
|||
/** Whether this block consumes touchDown events when tapped. */
|
||||
public boolean consumesTap;
|
||||
/** The color of this block when displayed on the minimap or map preview.
|
||||
* Do not set manually! This is overriden when loading.*/
|
||||
* Do not set manually! This is overriden when loading for most blocks.*/
|
||||
public Color color = new Color(0, 0, 0, 1);
|
||||
/**Whether units target this block.*/
|
||||
public boolean targetable = true;
|
||||
/**Whether the overdrive core has any effect on this block.*/
|
||||
public boolean canOverdrive = true;
|
||||
/**Whether the icon region has an outline added.*/
|
||||
public boolean outlineIcon = false;
|
||||
|
||||
/**Cost of constructing this block.*/
|
||||
public ItemStack[] buildRequirements = new ItemStack[]{};
|
||||
|
|
@ -380,11 +382,6 @@ public class Block extends BlockStorage{
|
|||
public void update(Tile tile){
|
||||
}
|
||||
|
||||
/**Called when this tile is randomly updated. This only happens on a client, and should be used for effects only.
|
||||
* TODO currently unimplemented*/
|
||||
public void randomUpdate(Tile tile){
|
||||
}
|
||||
|
||||
public boolean isAccessible(){
|
||||
return (hasItems && itemCapacity > 0);
|
||||
}
|
||||
|
|
@ -402,11 +399,10 @@ public class Block extends BlockStorage{
|
|||
float power = 0f;
|
||||
|
||||
if(hasItems){
|
||||
float scaling = inventoryScaling(tile);
|
||||
for(Item item : content.items()){
|
||||
int amount = tile.entity.items.get(item);
|
||||
explosiveness += item.explosiveness * amount * scaling;
|
||||
flammability += item.flammability * amount * scaling;
|
||||
explosiveness += item.explosiveness * amount;
|
||||
flammability += item.flammability * amount;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -441,11 +437,6 @@ public class Block extends BlockStorage{
|
|||
}
|
||||
}
|
||||
|
||||
/**Returns scaled # of inventories in this block.*/
|
||||
public float inventoryScaling(Tile tile){
|
||||
return 1f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flammability of the tile. Used for fire calculations.
|
||||
* Takes flammability of floor liquid into account.
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
package io.anuke.mindustry.world.blocks;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.mindustry.entities.Effects;
|
||||
import io.anuke.mindustry.entities.Effects.Effect;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.Draw;
|
||||
import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Geometry;
|
||||
import io.anuke.arc.math.geom.Point2;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.content.StatusEffects;
|
||||
import io.anuke.mindustry.entities.Effects.Effect;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.Liquid;
|
||||
import io.anuke.mindustry.type.StatusEffect;
|
||||
|
|
@ -98,13 +96,6 @@ public class Floor extends Block{
|
|||
return new TextureRegion[]{Core.atlas.find(Core.atlas.has(name) ? name : name + "1")};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomUpdate(Tile tile){
|
||||
if(tile.block() == Blocks.air){
|
||||
Effects.effect(updateEffect, tile.worldx() + Mathf.range(tilesize/2f), tile.worldy() + Mathf.range(tilesize/2f));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public interface SelectionTrait{
|
|||
int i = 0;
|
||||
|
||||
for(Item item : items){
|
||||
if(!data.isUnlocked(item)) continue;
|
||||
if(!data.isUnlocked(item) && world.isZone()) continue;
|
||||
|
||||
ImageButton button = cont.addImageButton("white", "clear-toggle", 24, () -> {}).group(group).get();
|
||||
button.changed(() -> consumer.accept(button.isChecked() ? item : null));
|
||||
|
|
|
|||
|
|
@ -62,8 +62,7 @@ public abstract class Turret extends Block{
|
|||
protected Vector2 tr = new Vector2();
|
||||
protected Vector2 tr2 = new Vector2();
|
||||
|
||||
protected TextureRegion baseRegion;
|
||||
protected TextureRegion heatRegion;
|
||||
protected TextureRegion baseRegion, heatRegion;
|
||||
|
||||
protected BiConsumer<Tile, TurretEntity> drawer = (tile, entity) ->
|
||||
Draw.rect(region, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
|
||||
|
|
@ -83,6 +82,7 @@ public abstract class Turret extends Block{
|
|||
layer = Layer.turret;
|
||||
group = BlockGroup.turrets;
|
||||
flags = EnumSet.of(BlockFlag.turret);
|
||||
outlineIcon = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -94,6 +94,7 @@ public abstract class Turret extends Block{
|
|||
public void load(){
|
||||
super.load();
|
||||
|
||||
region = Core.atlas.find(name);
|
||||
baseRegion = Core.atlas.find("block-" + size);
|
||||
heatRegion = Core.atlas.find(name + "-heat");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public class MassDriver extends Block{
|
|||
protected Effect smokeEffect = Fx.shootBigSmoke2;
|
||||
protected Effect recieveEffect = Fx.mineBig;
|
||||
protected float shake = 3f;
|
||||
protected final static float powerPercentageUsed = 1.0f;
|
||||
protected float powerPercentageUsed = 0.95f;
|
||||
protected TextureRegion turretRegion;
|
||||
|
||||
public MassDriver(String name){
|
||||
|
|
@ -59,6 +59,7 @@ public class MassDriver extends Block{
|
|||
layer = Layer.turret;
|
||||
hasPower = true;
|
||||
consumes.powerBuffered(30f);
|
||||
outlineIcon = true;
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.server, forward = true)
|
||||
|
|
@ -79,7 +80,7 @@ public class MassDriver extends Block{
|
|||
|
||||
entity.reload = 1f;
|
||||
|
||||
entity.power.satisfaction -= Math.min(entity.power.satisfaction, powerPercentageUsed);
|
||||
entity.power.satisfaction -= Math.min(entity.power.satisfaction, driver.powerPercentageUsed);
|
||||
|
||||
DriverBulletData data = Pools.obtain(DriverBulletData.class, DriverBulletData::new);
|
||||
data.from = entity;
|
||||
|
|
@ -117,7 +118,7 @@ public class MassDriver extends Block{
|
|||
public void load(){
|
||||
super.load();
|
||||
|
||||
turretRegion = Core.atlas.find(name + "-turret");
|
||||
turretRegion = Core.atlas.find(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -44,13 +44,14 @@ public class RepairPoint extends Block{
|
|||
layer2 = Layer.laser;
|
||||
hasPower = true;
|
||||
consumePower = consumes.powerBuffered(20f);
|
||||
outlineIcon = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
|
||||
topRegion = Core.atlas.find(name + "-turret");
|
||||
topRegion = Core.atlas.find(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -90,6 +91,11 @@ public class RepairPoint extends Block{
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] generateIcons(){
|
||||
return new TextureRegion[]{Core.atlas.find(name), Core.atlas.find(name + "-turret")};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Tile tile){
|
||||
RepairPointEntity entity = tile.entity();
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class UnitFactory extends Block{
|
|||
protected float produceTime = 1000f;
|
||||
protected float launchVelocity = 0f;
|
||||
protected TextureRegion topRegion;
|
||||
protected int maxSpawn = 4;
|
||||
protected int maxSpawn = 2;
|
||||
|
||||
public UnitFactory(String name){
|
||||
super(name);
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ public class ServerControl implements ApplicationListener{
|
|||
info("Status: &rserver closed");
|
||||
}else{
|
||||
info("Status:");
|
||||
info(" &lyPlaying on map &fi{0}&fb &lb/&ly Wave {1} &lb/&ly {2} &lb/&ly {3}", Strings.capitalize(world.getMap().name), state.wave);
|
||||
info(" &lyPlaying on map &fi{0}&fb &lb/&ly Wave {1}", Strings.capitalize(world.getMap().name), state.wave);
|
||||
|
||||
if(!state.rules.waves){
|
||||
info("&ly {0} enemies.", unitGroups[Team.red.ordinal()].size());
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@ public class ApplicationTests{
|
|||
void spawnWaves(){
|
||||
world.loadMap(testMap);
|
||||
logic.runWave();
|
||||
//force trigger delayed spawns
|
||||
Time.setDeltaProvider(() -> 1000f);
|
||||
Time.update();
|
||||
Time.update();
|
||||
Time.setDeltaProvider(() -> 1f);
|
||||
unitGroups[waveTeam.ordinal()].updateEvents();
|
||||
assertFalse(unitGroups[waveTeam.ordinal()].isEmpty());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,21 @@ package io.anuke.mindustry;
|
|||
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Log;
|
||||
import io.anuke.mindustry.type.UnitType;
|
||||
import io.anuke.mindustry.ImagePacker.GenRegion;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.Mech;
|
||||
import io.anuke.mindustry.type.UnitType;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Block.Icon;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
import io.anuke.mindustry.world.blocks.OreBlock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import static io.anuke.mindustry.Vars.content;
|
||||
import static io.anuke.mindustry.Vars.tilesize;
|
||||
|
||||
|
|
@ -21,6 +26,7 @@ public class Generators {
|
|||
|
||||
ImagePacker.generate("block-icons", () -> {
|
||||
Image colors = new Image(256, 1);
|
||||
Color outlineColor = new Color(0, 0, 0, 0.3f);
|
||||
|
||||
for(Block block : content.blocks()){
|
||||
TextureRegion[] regions = block.getGeneratedIcons();
|
||||
|
|
@ -47,6 +53,45 @@ public class Generators {
|
|||
scaled.save(block.name + "-icon-" + icon.name());
|
||||
}
|
||||
|
||||
if(block.outlineIcon){
|
||||
int radius = 3;
|
||||
GenRegion region = (GenRegion)regions[regions.length-1];
|
||||
Image base = ImagePacker.get(region);
|
||||
Image out = new Image(region.getWidth(), region.getHeight());
|
||||
for(int x = 0; x < out.width(); x++){
|
||||
for(int y = 0; y < out.height(); y++){
|
||||
|
||||
Color color = base.getColor(x, y);
|
||||
if(color.a >= 0.01f){
|
||||
out.draw(x, y, color);
|
||||
}else{
|
||||
boolean found = false;
|
||||
outer:
|
||||
for(int rx = -radius; rx <= radius; rx++){
|
||||
for(int ry = -radius; ry <= radius; ry++){
|
||||
if(Mathf.dst(rx, ry) <= radius && base.getColor(rx + x, ry + y).a > 0.01f){
|
||||
found = true;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
out.draw(x, y, outlineColor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
Files.delete(region.path);
|
||||
}catch(IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
out.save(block.name);
|
||||
}
|
||||
|
||||
Color average = new Color();
|
||||
for(int x = 0; x < image.width(); x++){
|
||||
for(int y = 0; y < image.height(); y++){
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ class Image {
|
|||
}
|
||||
|
||||
Color getColor(int x, int y){
|
||||
if(!Structs.inBounds(x, y, width(), height())) return color.set(0, 0, 0, 0);
|
||||
int i = image.getRGB(x, y);
|
||||
Color.argb8888ToColor(color, i);
|
||||
return color;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import javax.imageio.ImageIO;
|
|||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class ImagePacker{
|
||||
|
|
@ -38,7 +39,7 @@ public class ImagePacker{
|
|||
fname = fname.substring(0, fname.length() - 4);
|
||||
|
||||
BufferedImage image = ImageIO.read(path.toFile());
|
||||
GenRegion region = new GenRegion(fname){
|
||||
GenRegion region = new GenRegion(fname, path){
|
||||
|
||||
@Override
|
||||
public int getX(){
|
||||
|
|
@ -73,7 +74,7 @@ public class ImagePacker{
|
|||
@Override
|
||||
public AtlasRegion find(String name){
|
||||
if(!regionCache.containsKey(name)){
|
||||
GenRegion region = new GenRegion(name);
|
||||
GenRegion region = new GenRegion(name, null);
|
||||
region.invalid = true;
|
||||
return region;
|
||||
}
|
||||
|
|
@ -136,9 +137,11 @@ public class ImagePacker{
|
|||
static class GenRegion extends AtlasRegion{
|
||||
String name;
|
||||
boolean invalid;
|
||||
Path path;
|
||||
|
||||
GenRegion(String name){
|
||||
GenRegion(String name, Path path){
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
static void validate(TextureRegion region){
|
||||
|
|
|
|||