Compare commits

..

31 Commits
v41 ... v46

Author SHA1 Message Date
Anuken
6b3329845b Fixed onConstructFinish() not syncing rotation 2018-07-05 16:49:29 -04:00
Anuken
fc07c5efa6 Fixed carry entity chain reaction 2018-07-05 16:01:12 -04:00
Anuken
23188df276 Fixed slowdown when FPS got lower than TPS 2018-07-05 15:40:31 -04:00
Anuken
5e66dfcc36 Minor optimization 2018-07-05 15:23:20 -04:00
Anuken
bc36c57f9f Improved enemy drop tables 2018-07-05 14:09:46 -04:00
Anuken
5efa7d551e Fixed enemies dropping duplicate items 2018-07-05 14:05:11 -04:00
Anuken
8c6cda0d97 Fixed pathfinding using water / Fixed mech factories moving too fast 2018-07-05 13:38:42 -04:00
Anuken
4fa165bc6a Updated uCore 2018-07-05 13:23:31 -04:00
Anuken
da6695ceee Decreased flyer speed, damage, moved start wave / Wave time increase 2018-07-05 13:20:54 -04:00
Anuken
1043a5bb0d Fixed ghosts appearing in player list 2018-07-05 12:30:29 -04:00
Anuken
c7625278b5 Fixed autosave not triggering, offset player resource bars 2018-07-05 12:19:57 -04:00
Anuken
b9ef88951e Massive amount of bugfixes, too many to list in commit log 2018-07-05 12:05:53 -04:00
Anuken
4a32706c5a Misc bugfixes / Unified build/break block system 2018-07-04 23:52:21 -04:00
Anuken
cc92edfa58 Fixed item drop duplicates, invincible players 2018-07-04 15:22:59 -04:00
Anuken
071d0fbbf7 Fixed player names not displaying 2018-07-04 15:01:29 -04:00
Anuken
6ff44fddd0 Fixed plastanium compressor 2018-07-04 13:42:15 -04:00
Anuken
8760e99c43 Fixed dedicated server crash 2018-07-04 13:18:50 -04:00
Anuken
423f5f16bb Fixed extremely glitchy player pickup 2018-07-04 12:31:09 -04:00
Anuken
1c8c702afd Made flying units appear later in waves 2018-07-04 11:58:41 -04:00
Anuken
d2a739262d Added admin wave sending / Admin system fixes / Build fix 2018-07-04 11:57:04 -04:00
Anuken
76d0285e3f Added freebuild / Power system fix / Net packet pool fix / Crash fixes 2018-07-04 11:38:59 -04:00
Anuken
12f444750c Fixed custom client inversion 2018-07-03 20:03:51 -04:00
Anuken
5fdf7e3a12 Updated annotations module 2018-07-03 19:58:24 -04:00
Anuken
60deb9205b uCore update / Kick packet priority / Custom client lock / Net fixes 2018-07-03 19:48:29 -04:00
Anuken
359a392cca Fixed various bugs and crashes 2018-07-03 15:53:06 -04:00
Anuken
4d600f973a Added max power output display to item power generator 2018-07-03 13:20:01 -04:00
Anuken
4a626bc920 Various net crashes fixed 2018-07-03 13:13:02 -04:00
Anuken
3597860074 Additional crashes/synchronizations fixed 2018-07-03 12:57:22 -04:00
Anuken
68d2c30c38 More bugfixes 2018-07-03 12:48:20 -04:00
Anuken
e210d9c08e Fixed server verifying mobile player positions 2018-07-03 12:29:14 -04:00
Anuken
c9b77e4e23 Crash fixes 2018-07-03 12:27:37 -04:00
110 changed files with 1705 additions and 1538 deletions

View File

@@ -28,6 +28,8 @@ public class Annotations {
boolean unreliable() default false; boolean unreliable() default false;
/**The simple class name where this method is placed.*/ /**The simple class name where this method is placed.*/
String in() default "Call"; String in() default "Call";
/**Priority of this event.*/
PacketPriority priority() default PacketPriority.normal;
} }
/**Specifies that this method will be used to write classes of the type returned by {@link #value()}.<br> /**Specifies that this method will be used to write classes of the type returned by {@link #value()}.<br>
@@ -48,6 +50,15 @@ public class Annotations {
Class<?> value(); Class<?> value();
} }
public enum PacketPriority {
/**Gets put in a queue and processed if not connected.*/
normal,
/**Gets handled immediately, regardless of connection status.*/
high,
/**Does not get handled unless client is connected.*/
low
}
/**A set of two booleans, one specifying server and one specifying client.*/ /**A set of two booleans, one specifying server and one specifying client.*/
public enum Loc { public enum Loc {
/**Method can only be invoked on the client from the server.*/ /**Method can only be invoked on the client from the server.*/

View File

@@ -1,6 +1,7 @@
package io.anuke.annotations; package io.anuke.annotations;
import io.anuke.annotations.Annotations.Loc; import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.PacketPriority;
import io.anuke.annotations.Annotations.Variant; import io.anuke.annotations.Annotations.Variant;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
@@ -26,9 +27,11 @@ public class MethodEntry {
public final int id; public final int id;
/**The element method associated with this entry.*/ /**The element method associated with this entry.*/
public final ExecutableElement element; public final ExecutableElement element;
/**The assigned packet priority. Only used in clients.*/
public final PacketPriority priority;
public MethodEntry(String className, String targetMethod, Loc where, Variant target, public MethodEntry(String className, String targetMethod, Loc where, Variant target,
Loc local, boolean unreliable, boolean forward, int id, ExecutableElement element) { Loc local, boolean unreliable, boolean forward, int id, ExecutableElement element, PacketPriority priority) {
this.className = className; this.className = className;
this.forward = forward; this.forward = forward;
this.targetMethod = targetMethod; this.targetMethod = targetMethod;
@@ -38,6 +41,7 @@ public class MethodEntry {
this.id = id; this.id = id;
this.element = element; this.element = element;
this.unreliable = unreliable; this.unreliable = unreliable;
this.priority = priority;
} }
@Override @Override

View File

@@ -114,7 +114,7 @@ public class RemoteMethodAnnotationProcessor extends AbstractProcessor {
//create and add entry //create and add entry
MethodEntry method = new MethodEntry(entry.name, Utils.getMethodName(element), annotation.targets(), annotation.variants(), MethodEntry method = new MethodEntry(entry.name, Utils.getMethodName(element), annotation.targets(), annotation.variants(),
annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement) element); annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement) element, annotation.priority());
entry.methods.add(method); entry.methods.add(method);
methods.add(method); methods.add(method);

View File

@@ -117,7 +117,7 @@ public class RemoteReadGenerator {
if(entry.forward && entry.where.isServer && needsPlayer){ if(entry.forward && entry.where.isServer && needsPlayer){
//call forwarded method //call forwarded method
readBlock.addStatement(packageName + "." + entry.className + "." + entry.element.getSimpleName() + readBlock.addStatement(packageName + "." + entry.className + "." + entry.element.getSimpleName() +
"__forward(player.clientid" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")"); "__forward(player.con.id" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")");
} }
readBlock.nextControlFlow("catch (java.lang.Exception e)"); readBlock.nextControlFlow("catch (java.lang.Exception e)");

View File

@@ -132,6 +132,8 @@ public class RemoteWriteGenerator {
method.addStatement("$1N packet = $2N.obtain($1N.class)", "io.anuke.mindustry.net.Packets.InvokePacket", "com.badlogic.gdx.utils.Pools"); method.addStatement("$1N packet = $2N.obtain($1N.class)", "io.anuke.mindustry.net.Packets.InvokePacket", "com.badlogic.gdx.utils.Pools");
//assign buffer //assign buffer
method.addStatement("packet.writeBuffer = TEMP_BUFFER"); method.addStatement("packet.writeBuffer = TEMP_BUFFER");
//assign priority
method.addStatement("packet.priority = (byte)" + methodEntry.priority.ordinal());
//assign method ID //assign method ID
method.addStatement("packet.type = (byte)" + methodEntry.id); method.addStatement("packet.type = (byte)" + methodEntry.id);
//rewind buffer //rewind buffer

View File

@@ -27,7 +27,7 @@ allprojects {
gdxVersion = '1.9.8' gdxVersion = '1.9.8'
roboVMVersion = '2.3.0' roboVMVersion = '2.3.0'
aiVersion = '1.8.1' aiVersion = '1.8.1'
uCoreVersion = 'bc1c2e0848' uCoreVersion = '2241e5402e'
getVersionString = { getVersionString = {
String buildVersion = getBuildVersion() String buildVersion = getBuildVersion()

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

View File

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 220 B

View File

@@ -1,7 +1,6 @@
text.about=Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[] text.about=Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]
text.credits=Credits text.credits=Credits
text.discord=Join the mindustry discord! text.discord=Join the mindustry discord!
text.changes=[SCARLET]Attention!\n[]Some important game mechanics have been changed.\n\n- [accent]Teleporters[] now use power.\n- [accent]Smelteries[] and [accent]crucibles[] now have a maximum item capacity.\n- [accent]Crucibles[] now require coal as fuel.
text.link.discord.description=the official Mindustry discord chatroom text.link.discord.description=the official Mindustry discord chatroom
text.link.github.description=Game source code text.link.github.description=Game source code
text.link.dev-builds.description=Unstable development builds text.link.dev-builds.description=Unstable development builds
@@ -76,6 +75,7 @@ text.server.kicked.recentKick=You have been kicked recently.\nWait before connec
text.server.kicked.nameInUse=There is someone with that name\nalready on this server. text.server.kicked.nameInUse=There is someone with that name\nalready on this server.
text.server.kicked.nameEmpty=Your name must contain at least one character or number. text.server.kicked.nameEmpty=Your name must contain at least one character or number.
text.server.kicked.idInUse=You are already on this server! Connecting with two accounts is not permitted. text.server.kicked.idInUse=You are already on this server! Connecting with two accounts is not permitted.
text.server.kicked.customClient=This server does not support custom builds. Download an official version.
text.server.connected={0} has joined. text.server.connected={0} has joined.
text.server.disconnected={0} has disconnected. text.server.disconnected={0} has disconnected.
text.nohost=Can't host server on a custom map! text.nohost=Can't host server on a custom map!
@@ -150,7 +150,7 @@ text.save.rename=Rename
text.save.rename.text=New name: text.save.rename.text=New name:
text.selectslot=Select a save. text.selectslot=Select a save.
text.slot=[accent]Slot {0} text.slot=[accent]Slot {0}
text.save.corrupted=[orange]Save file corrupted or invalid! text.save.corrupted=[orange]Save file corrupted or invalid!\nIf you have just updated your game, this is probably a change in the save format and [scarlet]not[] a bug.
text.empty=<empty> text.empty=<empty>
text.on=On text.on=On
text.off=Off text.off=Off
@@ -265,23 +265,16 @@ text.upgrades=Upgrades
text.purchased=[LIME]Created! text.purchased=[LIME]Created!
text.weapons=Weapons text.weapons=Weapons
text.paused=Paused text.paused=Paused
text.respawn=Respawning in text.yes=Yes
text.no=No
text.info.title=[accent]Info text.info.title=[accent]Info
text.error.title=[crimson]An error has occured text.error.title=[crimson]An error has occured
text.error.crashmessage=[SCARLET]An unexpected error has occured, which would have caused a crash.\n[]Please report the exact circumstances under which this error occured to the developer: \n[ORANGE]anukendev@gmail.com[] text.error.crashmessage=[SCARLET]An unexpected error has occured, which would have caused a crash.\n[]Please report the exact circumstances under which this error occured to the developer: \n[ORANGE]anukendev@gmail.com[]
text.error.crashtitle=An error has occured text.error.crashtitle=An error has occured
text.mode.break=Break mode: {0}
text.mode.place=Place mode: {0}
placemode.hold.name=line
placemode.areadelete.name=area
placemode.touchdelete.name=touch
placemode.holddelete.name=hold
placemode.none.name=none
placemode.touch.name=touch
placemode.cursor.name=cursor
text.blocks.blockinfo=Block Info text.blocks.blockinfo=Block Info
text.blocks.powercapacity=Power Capacity text.blocks.powercapacity=Power Capacity
text.blocks.powershot=Power/Shot text.blocks.powershot=Power/Shot
text.blocks.targetsair=Targets Air
text.blocks.itemspeed=Units Moved text.blocks.itemspeed=Units Moved
text.blocks.shootrange=Range text.blocks.shootrange=Range
text.blocks.size=Size text.blocks.size=Size
@@ -540,3 +533,4 @@ block.liquid-tank.name=Liquid Tank
block.liquid-junction.name=Liquid Junction block.liquid-junction.name=Liquid Junction
block.bridge-conduit.name=Bridge Conduit block.bridge-conduit.name=Bridge Conduit
block.rotary-pump.name=Rotary Pump block.rotary-pump.name=Rotary Pump
block.nuclear-reactor.name=Nuclear Reactor

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View File

@@ -38,7 +38,7 @@ public class Vars{
//respawn time in frames //respawn time in frames
public static final float respawnduration = 60*4; public static final float respawnduration = 60*4;
//time between waves in frames (on normal mode) //time between waves in frames (on normal mode)
public static final float wavespace = 60*60*1.5f; public static final float wavespace = 60*60*2f;
//waves can last no longer than 3 minutes, otherwise the next one spawns //waves can last no longer than 3 minutes, otherwise the next one spawns
public static final float maxwavespace = 60*60*4f; public static final float maxwavespace = 60*60*4f;

View File

@@ -84,7 +84,7 @@ public class Pathfinder {
} }
private boolean passable(Tile tile, Team team){ private boolean passable(Tile tile, Team team){
return (tile.getWallID() == 0 && tile.cliffs == 0 && !tile.floor().solid && !(tile.floor().isLiquid && (tile.floor().damageTaken > 0 || tile.floor().drownTime > 0))) return (tile.getWallID() == 0 && !tile.floor().isLiquid && tile.cliffs == 0 && !tile.floor().solid && !(tile.floor().isLiquid && (tile.floor().damageTaken > 0 || tile.floor().drownTime > 0)))
|| (tile.breakable() && (tile.getTeam() != team)) || !tile.solid(); || (tile.breakable() && (tile.getTeam() != team)) || !tile.solid();
} }

View File

@@ -21,11 +21,13 @@ public class Items implements ContentList{
tungsten = new Item("tungsten", Color.valueOf("a0b0c8")) {{ tungsten = new Item("tungsten", Color.valueOf("a0b0c8")) {{
type = ItemType.material; type = ItemType.material;
hardness = 1; hardness = 1;
cost = 0.75f;
}}; }};
lead = new Item("lead", Color.valueOf("8e85a2")) {{ lead = new Item("lead", Color.valueOf("8e85a2")) {{
type = ItemType.material; type = ItemType.material;
hardness = 1; hardness = 1;
cost = 0.6f;
}}; }};
coal = new Item("coal", Color.valueOf("272727")) {{ coal = new Item("coal", Color.valueOf("272727")) {{
@@ -41,6 +43,7 @@ public class Items implements ContentList{
titanium = new Item("titanium", Color.valueOf("8da1e3")) {{ titanium = new Item("titanium", Color.valueOf("8da1e3")) {{
type = ItemType.material; type = ItemType.material;
hardness = 3; hardness = 3;
cost = 1.1f;
}}; }};
thorium = new Item("thorium", Color.valueOf("f9a3c7")) {{ thorium = new Item("thorium", Color.valueOf("f9a3c7")) {{
@@ -48,20 +51,24 @@ public class Items implements ContentList{
explosiveness = 0.1f; explosiveness = 0.1f;
hardness = 4; hardness = 4;
radioactivity = 0.5f; radioactivity = 0.5f;
cost = 1.2f;
}}; }};
silicon = new Item("silicon", Color.valueOf("53565c")) {{ silicon = new Item("silicon", Color.valueOf("53565c")) {{
type = ItemType.material; type = ItemType.material;
cost = 0.9f;
}}; }};
plastanium = new Item("plastanium", Color.valueOf("e9ead3")) {{ plastanium = new Item("plastanium", Color.valueOf("e9ead3")) {{
type = ItemType.material; type = ItemType.material;
flammability = 0.1f; flammability = 0.1f;
explosiveness = 0.1f; explosiveness = 0.1f;
cost = 1.5f;
}}; }};
phasematter = new Item("phase-matter", Color.valueOf("f4ba6e")) {{ phasematter = new Item("phase-matter", Color.valueOf("f4ba6e")) {{
type = ItemType.material; type = ItemType.material;
cost = 1.5f;
}}; }};
surgealloy = new Item("surge-alloy", Color.valueOf("b4d5c7")) {{ surgealloy = new Item("surge-alloy", Color.valueOf("b4d5c7")) {{

View File

@@ -101,6 +101,8 @@ public class Recipes implements ContentList{
new Recipe(power, PowerBlocks.solarPanel, new ItemStack(Items.lead, 20), new ItemStack(Items.silicon, 30)); new Recipe(power, PowerBlocks.solarPanel, new ItemStack(Items.lead, 20), new ItemStack(Items.silicon, 30));
new Recipe(power, PowerBlocks.largeSolarPanel, new ItemStack(Items.lead, 200), new ItemStack(Items.silicon, 290), new ItemStack(Items.phasematter, 30)); new Recipe(power, PowerBlocks.largeSolarPanel, new ItemStack(Items.lead, 200), new ItemStack(Items.silicon, 290), new ItemStack(Items.phasematter, 30));
//generators - other
new Recipe(power, PowerBlocks.nuclearReactor, new ItemStack(Items.lead, 600), new ItemStack(Items.silicon, 400), new ItemStack(Items.carbide, 300), new ItemStack(Items.thorium, 300));
//new Recipe(distribution, StorageBlocks.core, new ItemStack(Items.carbide, 50)); //new Recipe(distribution, StorageBlocks.core, new ItemStack(Items.carbide, 50));
new Recipe(distribution, StorageBlocks.unloader, new ItemStack(Items.carbide, 40), new ItemStack(Items.silicon, 50)); new Recipe(distribution, StorageBlocks.unloader, new ItemStack(Items.carbide, 40), new ItemStack(Items.silicon, 50));
@@ -108,8 +110,8 @@ public class Recipes implements ContentList{
new Recipe(distribution, StorageBlocks.vault, new ItemStack(Items.carbide, 500), new ItemStack(Items.thorium, 350)); new Recipe(distribution, StorageBlocks.vault, new ItemStack(Items.carbide, 500), new ItemStack(Items.thorium, 350));
//DRILLS, PRODUCERS //DRILLS, PRODUCERS
new Recipe(production, ProductionBlocks.tungstenDrill, new ItemStack(Items.tungsten, 30)); new Recipe(production, ProductionBlocks.tungstenDrill, new ItemStack(Items.tungsten, 25));
new Recipe(production, ProductionBlocks.carbideDrill, new ItemStack(Items.tungsten, 60), new ItemStack(Items.carbide, 60)); new Recipe(production, ProductionBlocks.carbideDrill, new ItemStack(Items.tungsten, 50), new ItemStack(Items.carbide, 60));
new Recipe(production, ProductionBlocks.laserdrill, new ItemStack(Items.tungsten, 90), new ItemStack(Items.carbide, 110), new ItemStack(Items.silicon, 70), new ItemStack(Items.titanium, 80)); new Recipe(production, ProductionBlocks.laserdrill, new ItemStack(Items.tungsten, 90), new ItemStack(Items.carbide, 110), new ItemStack(Items.silicon, 70), new ItemStack(Items.titanium, 80));
new Recipe(production, ProductionBlocks.waterextractor, new ItemStack(Items.tungsten, 50), new ItemStack(Items.carbide, 50), new ItemStack(Items.lead, 40)); new Recipe(production, ProductionBlocks.waterextractor, new ItemStack(Items.tungsten, 50), new ItemStack(Items.carbide, 50), new ItemStack(Items.lead, 40));

View File

@@ -125,7 +125,7 @@ public class StatusEffects implements ContentList {
overdrive = new StatusEffect(6f) { overdrive = new StatusEffect(6f) {
{ {
armorMultiplier = 0.95f; armorMultiplier = 0.95f;
speedMultiplier = 1.4f; speedMultiplier = 1.05f;
damageMultiplier = 1.4f; damageMultiplier = 1.4f;
} }

View File

@@ -42,7 +42,7 @@ public class UnitTypes implements ContentList {
vtol = new UnitType("vtol", Vtol.class, Vtol::new){{ vtol = new UnitType("vtol", Vtol.class, Vtol::new){{
speed = 0.3f; speed = 0.3f;
maxVelocity = 2.1f; maxVelocity = 1.9f;
drag = 0.01f; drag = 0.01f;
isFlying = true; isFlying = true;
}}; }};
@@ -50,7 +50,7 @@ public class UnitTypes implements ContentList {
monsoon = new UnitType("monsoon", Monsoon.class, Monsoon::new){{ monsoon = new UnitType("monsoon", Monsoon.class, Monsoon::new){{
health = 230; health = 230;
speed = 0.2f; speed = 0.2f;
maxVelocity = 1.5f; maxVelocity = 1.4f;
drag = 0.01f; drag = 0.01f;
isFlying = true; isFlying = true;
weapon = Weapons.bomber; weapon = Weapons.bomber;

View File

@@ -35,7 +35,7 @@ public class Weapons implements ContentList {
chainBlaster = new Weapon("chain-blaster") {{ chainBlaster = new Weapon("chain-blaster") {{
length = 1.5f; length = 1.5f;
reload = 20f; reload = 30f;
roundrobin = true; roundrobin = true;
ejectEffect = ShootFx.shellEjectSmall; ejectEffect = ShootFx.shellEjectSmall;
setAmmo(AmmoTypes.bulletLead, AmmoTypes.bulletCarbide, AmmoTypes.bulletTungsten, AmmoTypes.bulletSilicon, AmmoTypes.bulletThorium); setAmmo(AmmoTypes.bulletLead, AmmoTypes.bulletCarbide, AmmoTypes.bulletTungsten, AmmoTypes.bulletSilicon, AmmoTypes.bulletThorium);

View File

@@ -32,7 +32,6 @@ public class Blocks extends BlockList implements ContentList{
for(int i = 1; i <= 6; i ++){ for(int i = 1; i <= 6; i ++){
new BuildBlock("build" + i); new BuildBlock("build" + i);
new BreakBlock("break" + i);
} }
space = new Floor("space") {{ space = new Floor("space") {{

View File

@@ -52,9 +52,10 @@ public class CraftingBlocks extends BlockList implements ContentList {
flameColor = Color.valueOf("ffef99"); flameColor = Color.valueOf("ffef99");
}}; }};
plastaniumCompressor = new PlasteelCompressor("plastanium-compressor") {{ plastaniumCompressor = new PlastaniumCompressor("plastanium-compressor") {{
inputLiquid = Liquids.oil; inputLiquid = Liquids.oil;
inputItem = new ItemStack(Items.carbide, 1); inputItem = new ItemStack(Items.titanium, 2);
hasItems = true;
liquidUse = 0.3f; liquidUse = 0.3f;
liquidCapacity = 60f; liquidCapacity = 60f;
powerUse = 0.5f; powerUse = 0.5f;

View File

@@ -13,7 +13,7 @@ public class PowerBlocks extends BlockList implements ContentList {
@Override @Override
public void load() { public void load() {
combustionGenerator = new BurnerGenerator("combustion-generator") {{ combustionGenerator = new BurnerGenerator("combustion-generator") {{
powerOutput = 0.06f; powerOutput = 0.09f;
powerCapacity = 40f; powerCapacity = 40f;
itemDuration = 40f; itemDuration = 40f;
}}; }};
@@ -22,14 +22,17 @@ public class PowerBlocks extends BlockList implements ContentList {
maxLiquidGenerate = 0.5f; maxLiquidGenerate = 0.5f;
powerPerLiquid = 0.08f; powerPerLiquid = 0.08f;
powerCapacity = 40f; powerCapacity = 40f;
powerPerLiquid = 0.25f;
generateEffect = BlockFx.redgeneratespark; generateEffect = BlockFx.redgeneratespark;
size = 2; size = 2;
}}; }};
turbineGenerator = new TurbineGenerator("turbine-generator") {{ turbineGenerator = new TurbineGenerator("turbine-generator") {{
powerOutput = 0.15f; powerOutput = 0.28f;
powerCapacity = 40f; powerCapacity = 40f;
itemDuration = 30f; itemDuration = 30f;
powerPerLiquid = 0.7f;
auxLiquidUse = 0.05f;
size = 2; size = 2;
}}; }};
@@ -50,7 +53,8 @@ public class PowerBlocks extends BlockList implements ContentList {
nuclearReactor = new NuclearReactor("nuclear-reactor") {{ nuclearReactor = new NuclearReactor("nuclear-reactor") {{
size = 3; size = 3;
health = 600; health = 700;
powerMultiplier = 0.8f;
}}; }};
fusionReactor = new FusionReactor("fusion-reactor") {{ fusionReactor = new FusionReactor("fusion-reactor") {{

View File

@@ -18,7 +18,7 @@ public class ProductionBlocks extends BlockList implements ContentList {
public void load() { public void load() {
tungstenDrill = new Drill("tungsten-drill") {{ tungstenDrill = new Drill("tungsten-drill") {{
tier = 2; tier = 2;
drillTime = 360; drillTime = 340;
}}; }};
carbideDrill = new Drill("carbide-drill") {{ carbideDrill = new Drill("carbide-drill") {{

View File

@@ -21,11 +21,12 @@ public class TurretBlocks extends BlockList implements ContentList {
ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletLead, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletSilicon}; ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletLead, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletSilicon};
reload = 25f; reload = 25f;
restitution = 0.03f; restitution = 0.03f;
range = 80f; range = 90f;
shootCone = 15f; shootCone = 15f;
ammoUseEffect = ShootFx.shellEjectSmall; ammoUseEffect = ShootFx.shellEjectSmall;
health = 80; health = 80;
inaccuracy = 3f; inaccuracy = 2f;
rotatespeed = 10f;
}}; }};
/* /*
scatter = new BurstTurret("scatter") {{ scatter = new BurstTurret("scatter") {{
@@ -52,10 +53,10 @@ public class TurretBlocks extends BlockList implements ContentList {
scorch = new LiquidTurret("scorch") {{ scorch = new LiquidTurret("scorch") {{
ammoTypes = new AmmoType[]{AmmoTypes.basicFlame}; ammoTypes = new AmmoType[]{AmmoTypes.basicFlame};
recoil = 0f; recoil = 0f;
reload = 5f; reload = 4f;
shootCone = 50f; shootCone = 50f;
ammoUseEffect = ShootFx.shellEjectSmall; ammoUseEffect = ShootFx.shellEjectSmall;
health = 140; health = 160;
drawer = (tile, entity) -> Draw.rect(entity.target != null ? name + "-shoot" : name, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90); drawer = (tile, entity) -> Draw.rect(entity.target != null ? name + "-shoot" : name, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
}}; }};
@@ -82,16 +83,17 @@ public class TurretBlocks extends BlockList implements ContentList {
}}; }};
lancer = new LaserTurret("lancer") {{ lancer = new LaserTurret("lancer") {{
range = 70f; range = 90f;
chargeTime = 70f; chargeTime = 60f;
chargeMaxDelay = 30f; chargeMaxDelay = 30f;
chargeEffects = 7; chargeEffects = 7;
shootType = AmmoTypes.lancerLaser; shootType = AmmoTypes.lancerLaser;
recoil = 2f; recoil = 2f;
reload = 130f; reload = 100f;
cooldown = 0.03f; cooldown = 0.03f;
powerUsed = 20f; powerUsed = 20f;
powerCapacity = 60f; powerCapacity = 60f;
shootShake = 2f;
shootEffect = ShootFx.lancerLaserShoot; shootEffect = ShootFx.lancerLaserShoot;
smokeEffect = ShootFx.lancerLaserShootSmoke; smokeEffect = ShootFx.lancerLaserShootSmoke;
chargeEffect = ShootFx.lancerLaserCharge; chargeEffect = ShootFx.lancerLaserCharge;
@@ -130,7 +132,7 @@ public class TurretBlocks extends BlockList implements ContentList {
salvo = new BurstTurret("salvo") {{ salvo = new BurstTurret("salvo") {{
size = 2; size = 2;
range = 110f; range = 120f;
ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletThorium, AmmoTypes.bulletSilicon}; ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletThorium, AmmoTypes.bulletSilicon};
reload = 40f; reload = 40f;
restitution = 0.03f; restitution = 0.03f;

View File

@@ -12,8 +12,8 @@ import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.entities.bullet.LiquidBulletType; import io.anuke.mindustry.entities.bullet.LiquidBulletType;
import io.anuke.mindustry.entities.effect.Fire; import io.anuke.mindustry.entities.effect.Fire;
import io.anuke.mindustry.entities.effect.ItemDrop;
import io.anuke.mindustry.entities.effect.Lightning; import io.anuke.mindustry.entities.effect.Lightning;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.ContentList; import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
@@ -96,7 +96,7 @@ public class TurretBullets extends BulletList implements ContentList {
Color[] colors = {Palette.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Palette.lancerLaser, Color.WHITE}; Color[] colors = {Palette.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Palette.lancerLaser, Color.WHITE};
float[] tscales = {1f, 0.7f, 0.5f, 0.2f}; float[] tscales = {1f, 0.7f, 0.5f, 0.2f};
float[] lenscales = {1f, 1.1f, 1.13f, 1.14f}; float[] lenscales = {1f, 1.1f, 1.13f, 1.14f};
float length = 70f; float length = 90f;
{ {
hiteffect = BulletFx.hitLancer; hiteffect = BulletFx.hitLancer;
@@ -258,7 +258,7 @@ public class TurretBullets extends BulletList implements ContentList {
if(amountDropped > 0){ if(amountDropped > 0){
float angle = b.angle() + Mathf.range(100f); float angle = b.angle() + Mathf.range(100f);
float vs = Mathf.random(0f, 4f); float vs = Mathf.random(0f, 4f);
CallEntity.createItemDrop(Item.getByID(i), amountDropped, b.x, b.y, Angles.trnsx(angle, vs), Angles.trnsy(angle, vs)); ItemDrop.create(Item.getByID(i), amountDropped, b.x, b.y, Angles.trnsx(angle, vs), Angles.trnsy(angle, vs));
} }
} }
} }

View File

@@ -1,6 +1,9 @@
package io.anuke.mindustry.core; package io.anuke.mindustry.core;
import com.badlogic.gdx.utils.*; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectSet;
import com.badlogic.gdx.utils.OrderedMap;
import com.badlogic.gdx.utils.OrderedSet;
import io.anuke.mindustry.content.*; import io.anuke.mindustry.content.*;
import io.anuke.mindustry.content.blocks.*; import io.anuke.mindustry.content.blocks.*;
import io.anuke.mindustry.content.bullets.*; import io.anuke.mindustry.content.bullets.*;
@@ -28,6 +31,7 @@ public class ContentLoader {
private static boolean loaded = false; private static boolean loaded = false;
private static ObjectSet<Array<? extends Content>> contentSet = new OrderedSet<>(); private static ObjectSet<Array<? extends Content>> contentSet = new OrderedSet<>();
private static OrderedMap<String, Array<Content>> contentMap = new OrderedMap<>(); private static OrderedMap<String, Array<Content>> contentMap = new OrderedMap<>();
private static ObjectSet<Consumer<Content>> initialization = new ObjectSet<>();
private static ContentList[] content = { private static ContentList[] content = {
//effects //effects
new BlockFx(), new BlockFx(),
@@ -132,11 +136,15 @@ public class ContentLoader {
/**Initializes all content with the specified function.*/ /**Initializes all content with the specified function.*/
public static void initialize(Consumer<Content> callable){ public static void initialize(Consumer<Content> callable){
if(initialization.contains(callable)) return;
for(Array<? extends Content> arr : contentSet){ for(Array<? extends Content> arr : contentSet){
for(Content content : arr){ for(Content content : arr){
callable.accept(content); callable.accept(content);
} }
} }
initialization.add(callable);
} }
public static void dispose(){ public static void dispose(){

View File

@@ -13,10 +13,10 @@ import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.game.Content; import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.ContentDatabase; import io.anuke.mindustry.game.ContentDatabase;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.input.MobileInput;
import io.anuke.mindustry.input.DefaultKeybinds; import io.anuke.mindustry.input.DefaultKeybinds;
import io.anuke.mindustry.input.DesktopInput; import io.anuke.mindustry.input.DesktopInput;
import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.input.MobileInput;
import io.anuke.mindustry.io.Map; import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.io.Saves; import io.anuke.mindustry.io.Saves;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
@@ -162,7 +162,7 @@ public class Control extends Module{
} }
public void addPlayer(int index){ public void addPlayer(int index){
if(players.length < index + 1){ if(players.length != index + 1){
Player[] old = players; Player[] old = players;
players = new Player[index + 1]; players = new Player[index + 1];
System.arraycopy(old, 0, players, 0, old.length); System.arraycopy(old, 0, players, 0, old.length);
@@ -285,8 +285,11 @@ public class Control extends Module{
@Override @Override
public void dispose(){ public void dispose(){
Platform.instance.onGameExit(); Platform.instance.onGameExit();
ContentLoader.dispose();
Net.dispose(); Net.dispose();
ui.editor.dispose(); ui.editor.dispose();
inputs = new InputHandler[]{};
players = new Player[]{};
} }
@Override @Override
@@ -311,7 +314,7 @@ public class Control extends Module{
if(!Settings.has("4.0-warning")){ if(!Settings.has("4.0-warning")){
Settings.putBool("4.0-warning", true); Settings.putBool("4.0-warning", true);
Timers.runTask(5f, () -> { Timers.run(5f, () -> {
FloatingDialog dialog = new FloatingDialog("[orange]WARNING![]"); FloatingDialog dialog = new FloatingDialog("[orange]WARNING![]");
dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f); dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f);
dialog.content().add("The beta version you are about to play should be considered very unstable, and is [accent]not representative of the final 4.0 release.[]\n\n " + dialog.content().add("The beta version you are about to play should be considered very unstable, and is [accent]not representative of the final 4.0 release.[]\n\n " +
@@ -321,6 +324,18 @@ public class Control extends Module{
}); });
} }
if(!Settings.has("4.0-no-sound")){
Settings.putBool("4.0-no-sound", true);
Timers.run(4f, () -> {
FloatingDialog dialog = new FloatingDialog("[orange]Attention![]");
dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f);
dialog.content().add("You might have noticed that 4.0 does not have any sound.\nThis is [orange]intentional![] Sound will be added in a later update.\n\n[LIGHT_GRAY](now stop reporting this as a bug)").wrap().width(500f);
dialog.show();
});
}
} }
/**Called from main logic thread.*/ /**Called from main logic thread.*/
@@ -355,7 +370,7 @@ public class Control extends Module{
} }
//check unlocks every 2 seconds //check unlocks every 2 seconds
if(!state.mode.infiniteResources && !state.mode.disableWaveTimer && Timers.get("timerCheckUnlock", 120)){ if(!state.mode.infiniteResources && Timers.get("timerCheckUnlock", 120)){
checkUnlockableBlocks(); checkUnlockableBlocks();
//save if the db changed, but don't save unlocks //save if the db changed, but don't save unlocks

View File

@@ -12,8 +12,6 @@ public class GameState{
public int wave = 1; public int wave = 1;
public float wavetime; public float wavetime;
public float extrawavetime;
public int enemies = 0;
public boolean gameOver = false; public boolean gameOver = false;
public GameMode mode = GameMode.waves; public GameMode mode = GameMode.waves;
public Difficulty difficulty = Difficulty.normal; public Difficulty difficulty = Difficulty.normal;

View File

@@ -70,9 +70,7 @@ public class Logic extends Module {
public void reset(){ public void reset(){
state.wave = 1; state.wave = 1;
state.extrawavetime = maxwavespace * state.difficulty.maxTimeScaling;
state.wavetime = wavespace * state.difficulty.timeScaling; state.wavetime = wavespace * state.difficulty.timeScaling;
state.enemies = 0;
state.gameOver = false; state.gameOver = false;
state.teams = new TeamInfo(); state.teams = new TeamInfo();
state.teams.add(Team.blue, true); state.teams.add(Team.blue, true);
@@ -89,7 +87,6 @@ public class Logic extends Module {
state.spawner.spawnEnemies(); state.spawner.spawnEnemies();
state.wave ++; state.wave ++;
state.wavetime = wavespace * state.difficulty.timeScaling; state.wavetime = wavespace * state.difficulty.timeScaling;
state.extrawavetime = maxwavespace * state.difficulty.maxTimeScaling;
Events.fire(WaveEvent.class); Events.fire(WaveEvent.class);
} }
@@ -133,15 +130,10 @@ public class Logic extends Module {
if(!state.is(State.paused) || Net.active()){ if(!state.is(State.paused) || Net.active()){
if(!state.mode.disableWaveTimer){ if(!state.mode.disableWaveTimer){
state.wavetime -= Timers.delta();
if(state.enemies <= 0){
if(!world.getMap().name.equals("tutorial")) state.wavetime -= Timers.delta();
}else{
state.extrawavetime -= Timers.delta();
}
} }
if(!Net.client() && (state.wavetime <= 0 || state.extrawavetime <= 0)){ if(!Net.client() && state.wavetime <= 0){
runWave(); runWave();
} }

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.core;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.Base64Coder; import com.badlogic.gdx.utils.Base64Coder;
import com.badlogic.gdx.utils.IntSet; import com.badlogic.gdx.utils.IntSet;
import io.anuke.annotations.Annotations.PacketPriority;
import io.anuke.annotations.Annotations.Remote; import io.anuke.annotations.Annotations.Remote;
import io.anuke.annotations.Annotations.Variant; import io.anuke.annotations.Annotations.Variant;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
@@ -218,11 +219,12 @@ public class NetClient extends Module {
new Random().nextBytes(bytes); new Random().nextBytes(bytes);
String result = new String(Base64Coder.encode(bytes)); String result = new String(Base64Coder.encode(bytes));
Settings.putString("usid-" + ip, result); Settings.putString("usid-" + ip, result);
Settings.save();
return result; return result;
} }
} }
@Remote(variants = Variant.one) @Remote(variants = Variant.one, priority = PacketPriority.high)
public static void onKick(KickReason reason){ public static void onKick(KickReason reason){
netClient.disconnectQuietly(); netClient.disconnectQuietly();
state.set(State.menu); state.set(State.menu);
@@ -247,7 +249,7 @@ public class NetClient extends Module {
playerGroup.removeByID(playerid); playerGroup.removeByID(playerid);
} }
@Remote(variants = Variant.one, unreliable = true) @Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true)
public static void onSnapshot(byte[] chunk, int snapshotID, short chunkID, short totalLength, int base){ public static void onSnapshot(byte[] chunk, int snapshotID, short chunkID, short totalLength, int base){
if(NetServer.showSnapshotSize) Log.info("Recieved snapshot: len {0} ID {1} chunkID {2} totalLength {3} base {4} client-base {5}", chunk.length, snapshotID, chunkID, totalLength, base, netClient.lastSnapshotBaseID); if(NetServer.showSnapshotSize) Log.info("Recieved snapshot: len {0} ID {1} chunkID {2} totalLength {3} base {4} client-base {5}", chunk.length, snapshotID, chunkID, totalLength, base, netClient.lastSnapshotBaseID);
@@ -301,7 +303,7 @@ public class NetClient extends Module {
length = snapshot.length; length = snapshot.length;
netClient.lastSnapshotBase = Arrays.copyOf(snapshot, snapshot.length); netClient.lastSnapshotBase = Arrays.copyOf(snapshot, snapshot.length);
} else { //otherwise, last snapshot must not be null, decode it } else { //otherwise, last snapshot must not be null, decode it
if(NetServer.showSnapshotSize) Log.info("Base size: {0} Path size: {1}", netClient.lastSnapshotBase.length, snapshot.length); if(NetServer.showSnapshotSize) Log.info("Base size: {0} Patch size: {1}", netClient.lastSnapshotBase.length, snapshot.length);
netClient.decoder.init(netClient.lastSnapshotBase, snapshot); netClient.decoder.init(netClient.lastSnapshotBase, snapshot);
result = netClient.decoder.decode(); result = netClient.decoder.decode();
length = netClient.decoder.getDecodedLength(); length = netClient.decoder.getDecodedLength();
@@ -351,7 +353,9 @@ public class NetClient extends Module {
if(entity == null){ if(entity == null){
entity = (SyncTrait) TypeTrait.getTypeByID(typeID).get(); //create entity from supplier entity = (SyncTrait) TypeTrait.getTypeByID(typeID).get(); //create entity from supplier
entity.resetID(id); entity.resetID(id);
add = true; if(!netClient.isEntityUsed(entity.getID())){
add = true;
}
} }
//read the entity //read the entity
@@ -362,7 +366,7 @@ public class NetClient extends Module {
throw new RuntimeException("Error reading entity of type '"+ group.getType() + "': Read length mismatch [write=" + readLength + ", read=" + (netClient.byteStream.position() - position - 1)+ "]"); throw new RuntimeException("Error reading entity of type '"+ group.getType() + "': Read length mismatch [write=" + readLength + ", read=" + (netClient.byteStream.position() - position - 1)+ "]");
} }
if(add && !netClient.isEntityUsed(entity.getID())){ if(add){
entity.add(); entity.add();
netClient.addRemovedEntity(entity.getID()); netClient.addRemovedEntity(entity.getID());
} }

View File

@@ -74,6 +74,7 @@ public class NetServer extends Module{
if(player != null){ if(player != null){
onDisconnect(player); onDisconnect(player);
} }
connections.remove(id);
}); });
Net.handleServer(ConnectPacket.class, (id, packet) -> { Net.handleServer(ConnectPacket.class, (id, packet) -> {
@@ -97,6 +98,11 @@ public class NetServer extends Module{
return; return;
} }
if(packet.version == -1 && Version.build != -1 && !admins.allowsCustomClients()){
kick(id, KickReason.customClient);
return;
}
boolean preventDuplicates = headless; boolean preventDuplicates = headless;
if(preventDuplicates) { if(preventDuplicates) {
@@ -137,7 +143,7 @@ public class NetServer extends Module{
Player player = new Player(); Player player = new Player();
player.isAdmin = admins.isAdmin(uuid, packet.usid); player.isAdmin = admins.isAdmin(uuid, packet.usid);
player.clientid = id; player.con = Net.getConnection(id);
player.usid = packet.usid; player.usid = packet.usid;
player.name = packet.name; player.name = packet.name;
player.uuid = uuid; player.uuid = uuid;
@@ -169,7 +175,7 @@ public class NetServer extends Module{
NetConnection connection = Net.getConnection(id); NetConnection connection = Net.getConnection(id);
if(player == null || connection == null || packet.snapid < connection.lastRecievedClientSnapshot) return; if(player == null || connection == null || packet.snapid < connection.lastRecievedClientSnapshot) return;
boolean verifyPosition = !player.isDead() && !debug && headless; boolean verifyPosition = !player.isDead() && !debug && headless && !player.mech.flying && player.getCarrier() == null;
if(connection.lastRecievedClientTime == 0) connection.lastRecievedClientTime = TimeUtils.millis() - 16; if(connection.lastRecievedClientTime == 0) connection.lastRecievedClientTime = TimeUtils.millis() - 16;
@@ -185,6 +191,10 @@ public class NetServer extends Module{
player.setMineTile(packet.mining); player.setMineTile(packet.mining);
player.isBoosting = packet.boosting; player.isBoosting = packet.boosting;
player.isShooting = packet.shooting; player.isShooting = packet.shooting;
player.getPlaceQueue().clear();
if(packet.currentRequest != null){
player.getPlaceQueue().addLast(packet.currentRequest);
}
vector.set(packet.x - player.getInterpolator().target.x, packet.y - player.getInterpolator().target.y); vector.set(packet.x - player.getInterpolator().target.x, packet.y - player.getInterpolator().target.y);
@@ -322,9 +332,9 @@ public class NetServer extends Module{
//iterate through each player //iterate through each player
for (Player player : connections.values()) { for (Player player : connections.values()) {
NetConnection connection = Net.getConnection(player.clientid); NetConnection connection = player.con;
if(connection == null || !connection.isConnected()){ if(!connection.isConnected()){
//player disconnected, ignore them //player disconnected, ignore them
onDisconnect(player); onDisconnect(player);
return; return;
@@ -406,18 +416,25 @@ public class NetServer extends Module{
byte[] bytes = syncStream.toByteArray(); byte[] bytes = syncStream.toByteArray();
connection.lastSentRawSnapshot = bytes;
if(connection.currentBaseID == -1){ if(connection.currentBaseID == -1){
//assign to last sent snapshot so that there is only ever one unique snapshot with ID 0
if(connection.lastSentSnapshot != null){
bytes = connection.lastSentSnapshot;
}else{
connection.lastSentRawSnapshot = bytes;
connection.lastSentSnapshot = bytes;
}
if(showSnapshotSize) Log.info("Sent raw snapshot: {0} bytes.", bytes.length); if(showSnapshotSize) Log.info("Sent raw snapshot: {0} bytes.", bytes.length);
///Nothing to diff off of in this case, send the whole thing, but increment the counter ///Nothing to diff off of in this case, send the whole thing
connection.lastSentSnapshot = bytes;
sendSplitSnapshot(connection.id, bytes, 0, -1); sendSplitSnapshot(connection.id, bytes, 0, -1);
}else{ }else{
connection.lastSentRawSnapshot = bytes;
//send diff, otherwise //send diff, otherwise
byte[] diff = ByteDeltaEncoder.toDiff(new ByteMatcherHash(connection.currentBaseSnapshot, bytes), encoder); byte[] diff = ByteDeltaEncoder.toDiff(new ByteMatcherHash(connection.currentBaseSnapshot, bytes), encoder);
if(showSnapshotSize) Log.info("Shrank snapshot: {0} -> {1}, Base {2} ID {3}", bytes.length, diff.length, connection.currentBaseID, connection.lastSentSnapshotID); if(showSnapshotSize) Log.info("Shrank snapshot: {0} -> {1}, Base {2} ID {3} base length = {4}", bytes.length, diff.length, connection.currentBaseID, connection.currentBaseID + 1, connection.currentBaseSnapshot.length);
sendSplitSnapshot(connection.id, diff, connection.lastSentSnapshotID + 1, connection.currentBaseID); sendSplitSnapshot(connection.id, diff, connection.currentBaseID + 1, connection.currentBaseID);
connection.lastSentSnapshot = diff; connection.lastSentSnapshot = diff;
connection.lastSentSnapshotID = connection.currentBaseID + 1; connection.lastSentSnapshotID = connection.currentBaseID + 1;
connection.lastSentBase = connection.currentBaseID; connection.lastSentBase = connection.currentBaseID;
@@ -432,7 +449,6 @@ public class NetServer extends Module{
/**Sends a raw byte[] snapshot to a client, splitting up into chunks when needed.*/ /**Sends a raw byte[] snapshot to a client, splitting up into chunks when needed.*/
private static void sendSplitSnapshot(int userid, byte[] bytes, int snapshotID, int base){ private static void sendSplitSnapshot(int userid, byte[] bytes, int snapshotID, int base){
if(bytes.length < maxSnapshotSize){ if(bytes.length < maxSnapshotSize){
if(showSnapshotSize) Log.info("Raw send() snapshot call: {0} bytes, sID {1}", bytes.length, snapshotID);
Call.onSnapshot(userid, bytes, snapshotID, (short)0, (short)bytes.length, base); Call.onSnapshot(userid, bytes, snapshotID, (short)0, (short)bytes.length, base);
}else{ }else{
int remaining = bytes.length; int remaining = bytes.length;
@@ -457,11 +473,11 @@ public class NetServer extends Module{
} }
} }
private static void onDisconnect(Player player){ public static void onDisconnect(Player player){
Call.sendMessage("[accent]" + player.name + " has disconnected."); Call.sendMessage("[accent]" + player.name + " has disconnected.");
Call.onPlayerDisconnect(player.id); Call.onPlayerDisconnect(player.id);
player.remove(); player.remove();
netServer.connections.remove(player.clientid); netServer.connections.remove(player.con.id);
} }
@Remote(targets = Loc.client, called = Loc.server) @Remote(targets = Loc.client, called = Loc.server)
@@ -469,27 +485,32 @@ public class NetServer extends Module{
if(!player.isAdmin){ if(!player.isAdmin){
Log.err("ACCESS DENIED: Player {0} / {1} attempted to perform admin action without proper security access.", Log.err("ACCESS DENIED: Player {0} / {1} attempted to perform admin action without proper security access.",
player.name, Net.getConnection(player.clientid).address); player.name, player.con.address);
return; return;
} }
if(other == null || other.isAdmin){ if(other == null || (other.isAdmin && other != player)){ //fun fact: this means you can ban yourself
Log.err("{0} attempted to perform admin action on nonexistant or admin player.", player.name); Log.err("{0} attempted to perform admin action on nonexistant or admin player.", player.name);
return; return;
} }
String ip = Net.getConnection(other.clientid).address; String ip = player.con.address;
if(action == AdminAction.ban){ if(action == AdminAction.wave) {
//no verification is done, so admins can hypothetically spam waves
//not a real issue, because server owners may want to do just that
state.wavetime = 0f;
}else if(action == AdminAction.ban){
netServer.admins.banPlayerIP(ip); netServer.admins.banPlayerIP(ip);
netServer.kick(other.clientid, KickReason.banned); netServer.kick(other.con.id, KickReason.banned);
Log.info("&lc{0} has banned {1}.", player.name, other.name); Log.info("&lc{0} has banned {1}.", player.name, other.name);
}else if(action == AdminAction.kick){ }else if(action == AdminAction.kick){
netServer.kick(other.clientid, KickReason.kick); netServer.kick(other.con.id, KickReason.kick);
Log.info("&lc{0} has kicked {1}.", player.name, other.name); Log.info("&lc{0} has kicked {1}.", player.name, other.name);
}else if(action == AdminAction.trace){ }else if(action == AdminAction.trace){
if(player.clientid != -1) { //TODO
Call.onTraceInfo(player.clientid, netServer.admins.getTraceByID(other.uuid)); if(player.con != null) {
Call.onTraceInfo(player.con.id, netServer.admins.getTraceByID(other.uuid));
}else{ }else{
NetClient.onTraceInfo(netServer.admins.getTraceByID(other.uuid)); NetClient.onTraceInfo(netServer.admins.getTraceByID(other.uuid));
} }
@@ -500,7 +521,7 @@ public class NetServer extends Module{
@Remote(targets = Loc.client) @Remote(targets = Loc.client)
public static void connectConfirm(Player player){ public static void connectConfirm(Player player){
player.add(); player.add();
Net.getConnection(player.clientid).hasConnected = true; player.con.hasConnected = true;
Call.sendMessage("[accent]" + player.name + " has connected."); Call.sendMessage("[accent]" + player.name + " has connected.");
Log.info("&y{0} has connected.", player.name); Log.info("&y{0} has connected.", player.name);
} }

View File

@@ -192,10 +192,7 @@ public class Renderer extends RendererModule{
batch.setProjectionMatrix(camera.combined); batch.setProjectionMatrix(camera.combined);
if(pixelate) Graphics.surface(pixelSurface, false);
Graphics.surface(pixelSurface, false);
else
batch.begin();
drawPadding(); drawPadding();
@@ -222,6 +219,7 @@ public class Renderer extends RendererModule{
Graphics.endShaders(); Graphics.endShaders();
} }
drawAllTeams(false); drawAllTeams(false);
blocks.skipLayer(Layer.turret); blocks.skipLayer(Layer.turret);
@@ -236,18 +234,21 @@ public class Renderer extends RendererModule{
drawAndInterpolate(playerGroup, p -> true, Player::drawBuildRequests); drawAndInterpolate(playerGroup, p -> true, Player::drawBuildRequests);
overlays.drawTop(); overlays.drawTop();
if(pixelate) Graphics.flushSurface();
Graphics.flushSurface();
if(showPaths && debug) drawDebug(); if(showPaths && debug) drawDebug();
drawAndInterpolate(playerGroup, p -> !p.isLocal && !p.isDead(), Player::drawName);
batch.end(); batch.end();
if(showFog){ if(showFog){
fog.draw(); fog.draw();
} }
batch.begin();
EntityDraw.setClip(false);
drawAndInterpolate(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName);
EntityDraw.setClip(true);
batch.end();
} }
private void drawAllTeams(boolean flying){ private void drawAllTeams(boolean flying){
@@ -257,7 +258,7 @@ public class Renderer extends RendererModule{
if(group.count(p -> p.isFlying() == flying) + if(group.count(p -> p.isFlying() == flying) +
playerGroup.count(p -> p.isFlying() == flying && p.getTeam() == team) == 0 && flying) continue; playerGroup.count(p -> p.isFlying() == flying && p.getTeam() == team) == 0 && flying) continue;
drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying, Unit::drawUnder); drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawUnder);
drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawUnder); drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawUnder);
Shaders.outline.color.set(team.color); Shaders.outline.color.set(team.color);
@@ -265,13 +266,13 @@ public class Renderer extends RendererModule{
Graphics.beginShaders(Shaders.outline); Graphics.beginShaders(Shaders.outline);
Graphics.shader(Shaders.mix, true); Graphics.shader(Shaders.mix, true);
drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying); drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead());
drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team); drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team);
Graphics.shader(); Graphics.shader();
blocks.drawTeamBlocks(Layer.turret, team); blocks.drawTeamBlocks(Layer.turret, team);
Graphics.endShaders(); Graphics.endShaders();
drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying, Unit::drawOver); drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawOver);
drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawOver); drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawOver);
} }
} }
@@ -306,11 +307,18 @@ public class Renderer extends RendererModule{
} }
} }
//TODO extremely hacky
if(t instanceof Player && ((Player) t).getCarry() != null && ((Player) t).getCarry() instanceof Player && ((Player) ((Player) t).getCarry()).isLocal){
((Player) t).x = ((Player) t).getCarry().getX();
((Player) t).y = ((Player) t).getCarry().getY();
}
drawer.accept(t); drawer.accept(t);
t.setX(lastx);
t.setY(lasty);
if(threads.doInterpolate() && threads.isEnabled()) { if(threads.doInterpolate() && threads.isEnabled()) {
t.setX(lastx);
t.setY(lasty);
if (t instanceof SolidTrait) { if (t instanceof SolidTrait) {
((SolidTrait) t).setRotation(lastrot); ((SolidTrait) t).setRotation(lastrot);

View File

@@ -17,7 +17,8 @@ public class ThreadHandler {
private final Array<Runnable> toRun = new Array<>(); private final Array<Runnable> toRun = new Array<>();
private final ThreadProvider impl; private final ThreadProvider impl;
private float delta = 1f; private float delta = 1f;
private long frame = 0; private float smoothDelta = 1f;
private long frame = 0, lastDeltaUpdate;
private float framesSinceUpdate; private float framesSinceUpdate;
private boolean enabled; private boolean enabled;
@@ -29,7 +30,7 @@ public class ThreadHandler {
Timers.setDeltaProvider(() -> { Timers.setDeltaProvider(() -> {
float result = impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f; float result = impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f;
return Math.min(Float.isNaN(result) ? 1f : result, 12f); return Math.min(Float.isNaN(result) ? 1f : result, 15f);
}); });
} }
@@ -62,7 +63,7 @@ public class ThreadHandler {
} }
public int getTPS(){ public int getTPS(){
return (int)(60/delta); return (int)(60/smoothDelta);
} }
public long getFrameID(){ public long getFrameID(){
@@ -138,8 +139,6 @@ public class ThreadHandler {
long elapsed = TimeUtils.nanosToMillis(TimeUtils.timeSinceNanos(time)); long elapsed = TimeUtils.nanosToMillis(TimeUtils.timeSinceNanos(time));
long target = (long) ((1000) / 60f); long target = (long) ((1000) / 60f);
delta = Math.max(elapsed, target) / 1000f * 60f;
if (elapsed < target) { if (elapsed < target) {
impl.sleep(target - elapsed); impl.sleep(target - elapsed);
} }
@@ -151,6 +150,14 @@ public class ThreadHandler {
rendered = false; rendered = false;
} }
long actuallyElapsed = TimeUtils.nanosToMillis(TimeUtils.timeSinceNanos(time));
delta = Math.max(actuallyElapsed, target) / 1000f * 60f;
if(TimeUtils.timeSinceMillis(lastDeltaUpdate) > 1000){
lastDeltaUpdate = TimeUtils.millis();
smoothDelta = delta;
}
frame ++; frame ++;
framesSinceUpdate = 0; framesSinceUpdate = 0;
} }

View File

@@ -92,8 +92,6 @@ public class UI extends SceneModule{
Settings.setErrorHandler(()-> Timers.run(1f, ()-> showError("[crimson]Failed to access local storage.\nSettings will not be saved."))); Settings.setErrorHandler(()-> Timers.run(1f, ()-> showError("[crimson]Failed to access local storage.\nSettings will not be saved.")));
Settings.defaults("pixelate", true);
Dialog.closePadR = -1; Dialog.closePadR = -1;
Dialog.closePadT = 5; Dialog.closePadT = 5;

View File

@@ -100,6 +100,10 @@ public class World extends Module{
return tiles[0].length; return tiles[0].length;
} }
public int toPacked(int x, int y){
return x + y *width();
}
public Tile tile(int packed){ public Tile tile(int packed){
return tiles == null ? null : tile(packed % width(), packed / width()); return tiles == null ? null : tile(packed % width(), packed / width());
} }
@@ -112,6 +116,10 @@ public class World extends Module{
return tiles[x][y]; return tiles[x][y];
} }
public Tile rawTile(int x, int y){
return tiles[x][y];
}
public Tile tileWorld(float x, float y){ public Tile tileWorld(float x, float y){
return tile(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize)); return tile(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize));
} }

View File

@@ -44,6 +44,12 @@ public class MapEditor{
this.brushSize = 1; this.brushSize = 1;
this.tags = tags; this.tags = tags;
for (int x = 0; x < map.width(); x++) {
for (int y = 0; y < map.height(); y++) {
map.write(x, y, DataPosition.floor, (byte)Blocks.stone.id);
}
}
drawBlock = Blocks.stone; drawBlock = Blocks.stone;
renderer.resize(map.width(), map.height()); renderer.resize(map.width(), map.height());
} }
@@ -262,6 +268,11 @@ public class MapEditor{
public void resize(int width, int height){ public void resize(int width, int height){
map = new MapTileData(width, height); map = new MapTileData(width, height);
for (int x = 0; x < map.width(); x++) {
for (int y = 0; y < map.height(); y++) {
map.write(x, y, DataPosition.floor, (byte)Blocks.stone.id);
}
}
renderer.resize(width, height); renderer.resize(width, height);
} }
} }

View File

@@ -9,12 +9,14 @@ import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import io.anuke.mindustry.content.blocks.StorageBlocks;
import io.anuke.mindustry.core.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.Map; import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.io.MapIO; import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.io.MapMeta; import io.anuke.mindustry.io.MapMeta;
import io.anuke.mindustry.io.MapTileData; import io.anuke.mindustry.io.MapTileData;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
@@ -367,8 +369,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
public void updateSelectedBlock(){ public void updateSelectedBlock(){
Block block = editor.getDrawBlock(); Block block = editor.getDrawBlock();
int i = 0; int i = 0;
for(Block test : Block.all()){ for(int j = 0; j < Block.all().size; j ++){
if(block == test){ if(block.id == j){
blockgroup.getButtons().get(i).setChecked(true); blockgroup.getButtons().get(i).setChecked(true);
break; break;
} }
@@ -586,6 +588,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
for(Block block : Block.all()){ for(Block block : Block.all()){
TextureRegion[] regions = block.getCompactIcon(); TextureRegion[] regions = block.getCompactIcon();
if((block.synthetic() && (Recipe.getByResult(block) == null || !control.database().isUnlocked(Recipe.getByResult(block)))) && !debug && block != StorageBlocks.core) continue;
if(regions.length == 0 || regions[0] == Draw.region("jjfgj")) continue; if(regions.length == 0 || regions[0] == Draw.region("jjfgj")) continue;

View File

@@ -61,7 +61,7 @@ public class MapRenderer implements Disposable{
while(it.hasNext){ while(it.hasNext){
int i = it.next(); int i = it.next();
int x = i % width; int x = i % width;
int y = i / height; int y = i / width;
render(x, y); render(x, y);
} }
updates.clear(); updates.clear();

View File

@@ -18,6 +18,7 @@ import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Trail; import io.anuke.mindustry.graphics.Trail;
import io.anuke.mindustry.net.In; import io.anuke.mindustry.net.In;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
@@ -29,10 +30,7 @@ import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.trait.SolidTrait; import io.anuke.ucore.entities.trait.SolidTrait;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.*;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.ThreadQueue;
import io.anuke.ucore.util.Timer;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
@@ -57,15 +55,15 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
public float boostHeat; public float boostHeat;
public Color color = new Color(); public Color color = new Color();
public Mech mech; public Mech mech;
public int spawner;
public int clientid = -1; public NetConnection con;
public int playerIndex = 0; public int playerIndex = 0;
public boolean isLocal = false; public boolean isLocal = false;
public Timer timer = new Timer(4); public Timer timer = new Timer(4);
public TargetTrait target; public TargetTrait target;
public TargetTrait moveTarget; public TargetTrait moveTarget;
private boolean respawning;
private float walktime; private float walktime;
private Queue<BuildRequest> placeQueue = new ThreadQueue<>(); private Queue<BuildRequest> placeQueue = new ThreadQueue<>();
private Tile mining; private Tile mining;
@@ -177,6 +175,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
return mech.weapon.getAmmoType(item) != null && inventory.canAcceptAmmo(mech.weapon.getAmmoType(item)); return mech.weapon.getAmmoType(item) != null && inventory.canAcceptAmmo(mech.weapon.getAmmoType(item));
} }
@Override
public void added() {
baseRotation = 90f;
}
@Override @Override
public void addAmmo(Item item) { public void addAmmo(Item item) {
inventory.addAmmo(mech.weapon.getAmmoType(item)); inventory.addAmmo(mech.weapon.getAmmoType(item));
@@ -201,7 +204,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
public void damage(float amount){ public void damage(float amount){
CallEntity.onPlayerDamage(this, calculateDamage(amount)); CallEntity.onPlayerDamage(this, calculateDamage(amount));
if(health <= 0 && !dead && isLocal){ if(health <= 0 && !dead){
CallEntity.onPlayerDeath(this); CallEntity.onPlayerDeath(this);
} }
} }
@@ -224,7 +227,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
if(player == null) return; if(player == null) return;
player.dead = true; player.dead = true;
player.respawning = false;
player.placeQueue.clear(); player.placeQueue.clear();
player.dropCarry(); player.dropCarry();
@@ -250,6 +252,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
@Override @Override
public void removed() { public void removed() {
dropCarryLocal(); dropCarryLocal();
TileEntity core = getClosestCore();
if(core != null && ((CoreEntity)core).currentUnit == this){
((CoreEntity)core).currentUnit = null;
}
} }
@Override @Override
@@ -432,12 +439,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
if(isDead()){ if(isDead()){
isBoosting = false; isBoosting = false;
boostHeat = 0f; boostHeat = 0f;
CoreEntity entity = (CoreEntity)getClosestCore(); updateRespawning();
if (!respawning && entity != null) {
entity.trySetPlayer(this);
}
return; return;
}else{
spawner = -1;
} }
if(!isLocal){ if(!isLocal){
@@ -445,6 +450,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
updateBuilding(this); //building happens even with non-locals updateBuilding(this); //building happens even with non-locals
status.update(this); //status effect updating also happens with non locals for effect purposes status.update(this); //status effect updating also happens with non locals for effect purposes
if(getCarrier() != null){
x = getCarrier().getX();
y = getCarrier().getY();
}
if(Net.server()){ if(Net.server()){
updateShooting(); //server simulates player shooting updateShooting(); //server simulates player shooting
} }
@@ -526,11 +536,16 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
movement.limit(speed); movement.limit(speed);
velocity.add(movement); if(getCarrier() == null){
velocity.add(movement);
float prex = x, prey = y; float prex = x, prey = y;
updateVelocityStatus(mech.drag, 10f); updateVelocityStatus(mech.drag, 10f);
moved = distanceTo(prex, prey) > 0.01f; moved = distanceTo(prex, prey) > 0.01f;
}else{
velocity.setZero();
x = Mathf.lerpDelta(x, getCarrier().getX(), 0.1f);
y = Mathf.lerpDelta(y, getCarrier().getY(), 0.1f);
}
if(!isShooting()){ if(!isShooting()){
if(!movement.isZero()) { if(!movement.isZero()) {
@@ -604,7 +619,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
if (target == null) { if (target == null) {
isShooting = false; isShooting = false;
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange()); target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
} else { } else if(target.isValid()){
//rotate toward and shoot the target //rotate toward and shoot the target
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f); rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f);
@@ -643,7 +658,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
inventory.clear(); inventory.clear();
placeQueue.clear(); placeQueue.clear();
dead = true; dead = true;
respawning = false;
trail.clear(); trail.clear();
health = maxHealth(); health = maxHealth();
@@ -654,12 +668,21 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
return isShooting && inventory.hasAmmo() && (!isBoosting || mech.flying); return isShooting && inventory.hasAmmo() && (!isBoosting || mech.flying);
} }
public void setRespawning(){ public void updateRespawning(){
respawning = true;
if (spawner != -1 && world.tile(spawner) != null && world.tile(spawner).entity instanceof SpawnerTrait) {
((SpawnerTrait) world.tile(spawner).entity).updateSpawning(this);
}else{
CoreEntity entity = (CoreEntity)getClosestCore();
if(entity != null){
this.spawner = entity.tile.id();
}
}
} }
public void setRespawning(boolean respawning){ public void beginRespawning(SpawnerTrait spawner){
this.respawning = respawning; this.spawner = spawner.getTile().packedPosition();
this.dead = true;
} }
@Override @Override
@@ -691,12 +714,18 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
public void readSave(DataInput stream) throws IOException { public void readSave(DataInput stream) throws IOException {
boolean local = stream.readBoolean(); boolean local = stream.readBoolean();
if(local){ if(local && !headless){
byte mechid = stream.readByte(); byte mechid = stream.readByte();
int index = stream.readByte(); int index = stream.readByte();
players[index].readSaveSuper(stream); players[index].readSaveSuper(stream);
players[index].mech = Upgrade.getByID(mechid); players[index].mech = Upgrade.getByID(mechid);
players[index].dead = false; players[index].dead = false;
}else if(local){
byte mechid = stream.readByte();
stream.readByte();
readSaveSuper(stream);
mech = Upgrade.getByID(mechid);
dead = false;
} }
} }
@@ -716,6 +745,9 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
buffer.writeByte(mech.id); buffer.writeByte(mech.id);
buffer.writeBoolean(isBoosting); buffer.writeBoolean(isBoosting);
buffer.writeInt(mining == null ? -1 : mining.packedPosition()); buffer.writeInt(mining == null ? -1 : mining.packedPosition());
buffer.writeInt(spawner);
writeBuilding(buffer);
} }
@Override @Override
@@ -729,6 +761,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
mech = Upgrade.getByID(buffer.readByte()); mech = Upgrade.getByID(buffer.readByte());
boolean boosting = buffer.readBoolean(); boolean boosting = buffer.readBoolean();
int mine = buffer.readInt(); int mine = buffer.readInt();
spawner = buffer.readInt();
readBuilding(buffer, !isLocal);
interpolator.read(lastx, lasty, x, y, time, rotation); interpolator.read(lastx, lasty, x, y, time, rotation);
rotation = lastrot; rotation = lastrot;

View File

@@ -6,6 +6,7 @@ import io.anuke.mindustry.entities.traits.Saveable;
import io.anuke.mindustry.type.StatusEffect; import io.anuke.mindustry.type.StatusEffect;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Pooling; import io.anuke.ucore.util.Pooling;
import io.anuke.ucore.util.ThreadArray;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
@@ -14,9 +15,9 @@ import java.io.IOException;
/**Class for controlling status effects on an entity.*/ /**Class for controlling status effects on an entity.*/
public class StatusController implements Saveable{ public class StatusController implements Saveable{
private static final StatusEntry globalResult = new StatusEntry(); private static final StatusEntry globalResult = new StatusEntry();
private static final Array<StatusEntry> removals = new Array<>(); private static final Array<StatusEntry> removals = new ThreadArray<>();
private Array<StatusEntry> statuses = new Array<>(); private Array<StatusEntry> statuses = new ThreadArray<>();
private float speedMultiplier; private float speedMultiplier;
private float damageMultiplier; private float damageMultiplier;

View File

@@ -131,6 +131,10 @@ public class TileEntity extends BaseEntity implements TargetTrait {
} }
} }
public Tile getTile(){
return tile;
}
@Override @Override
public Team getTeam() { public Team getTeam() {
return tile.getTeam(); return tile.getTeam();

View File

@@ -132,6 +132,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
@Override @Override
public void readSave(DataInput stream) throws IOException { public void readSave(DataInput stream) throws IOException {
byte team = stream.readByte(); byte team = stream.readByte();
boolean dead = stream.readBoolean();
float x = stream.readFloat(); float x = stream.readFloat();
float y = stream.readFloat(); float y = stream.readFloat();
byte xv = stream.readByte(); byte xv = stream.readByte();
@@ -141,6 +142,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
this.status.readSave(stream); this.status.readSave(stream);
this.inventory.readSave(stream); this.inventory.readSave(stream);
this.dead = dead;
this.team = Team.all[team]; this.team = Team.all[team];
this.health = health; this.health = health;
this.x = x; this.x = x;
@@ -151,6 +153,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
public void writeSave(DataOutput stream, boolean net) throws IOException { public void writeSave(DataOutput stream, boolean net) throws IOException {
stream.writeByte(team.ordinal()); stream.writeByte(team.ordinal());
stream.writeBoolean(isDead());
stream.writeFloat(net ? interpolator.target.x : x); stream.writeFloat(net ? interpolator.target.x : x);
stream.writeFloat(net ? interpolator.target.y : y); stream.writeFloat(net ? interpolator.target.y : y);
stream.writeByte((byte)(Mathf.clamp(velocity.x, -maxAbsVelocity, maxAbsVelocity) * velocityPercision)); stream.writeByte((byte)(Mathf.clamp(velocity.x, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
@@ -196,7 +199,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
public void avoidOthers(float avoidRange){ public void avoidOthers(float avoidRange){
EntityPhysics.getNearby(getGroup(), x, y, avoidRange*2f, t -> { EntityPhysics.getNearby(getGroup(), x, y, avoidRange*2f, t -> {
if(t == this || (t instanceof Unit && (((Unit) t).isDead() || (((Unit) t).isFlying() != isFlying())))) return; if(t == this || (t instanceof Unit && (((Unit) t).isDead() || (((Unit) t).isFlying() != isFlying()) || ((Unit) t).getCarrier() == this) || getCarrier() == t)) return;
float dst = distanceTo(t); float dst = distanceTo(t);
if(dst > avoidRange) return; if(dst > avoidRange) return;
velocity.add(moveVector.set(x, y).sub(t.getX(), t.getY()).setLength(1f * (1f - (dst / avoidRange)))); velocity.add(moveVector.set(x, y).sub(t.getX(), t.getY()).setLength(1f * (1f - (dst / avoidRange))));

View File

@@ -43,7 +43,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
/**Start a fire on the tile. If there already is a file there, refreshes its lifetime.*/ /**Start a fire on the tile. If there already is a file there, refreshes its lifetime.*/
public static void create(Tile tile){ public static void create(Tile tile){
if(Net.client()) return; //not clientside. if(Net.client() || tile == null) return; //not clientside.
Fire fire = map.get(tile.packedPosition()); Fire fire = map.get(tile.packedPosition());
@@ -62,7 +62,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
/**Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing.*/ /**Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing.*/
public static void extinguish(Tile tile, float intensity) { public static void extinguish(Tile tile, float intensity) {
if (map.containsKey(tile.packedPosition())) { if (tile != null && map.containsKey(tile.packedPosition())) {
map.get(tile.packedPosition()).time += intensity * Timers.delta(); map.get(tile.packedPosition()).time += intensity * Timers.delta();
} }
} }

View File

@@ -28,7 +28,6 @@ import io.anuke.ucore.entities.trait.TimeTrait;
import io.anuke.ucore.entities.trait.VelocityTrait; import io.anuke.ucore.entities.trait.VelocityTrait;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Pooling;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
@@ -48,7 +47,7 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
private float sinktime; private float sinktime;
public static ItemDrop create(Item item, int amount, float x, float y, float angle){ public static ItemDrop create(Item item, int amount, float x, float y, float angle){
ItemDrop drop = Pooling.obtain(ItemDrop.class); ItemDrop drop = new ItemDrop();
drop.item = item; drop.item = item;
drop.amount = amount; drop.amount = amount;
drop.velocity.set(4f, 0f).rotate(angle); drop.velocity.set(4f, 0f).rotate(angle);
@@ -58,8 +57,7 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
return drop; return drop;
} }
@Remote(called = Loc.server, in = In.entities) public static void create(Item item, int amount, float x, float y, float velocityX, float velocityY){
public static void createItemDrop(Item item, int amount, float x, float y, float velocityX, float velocityY){
create(item, amount, x, y, 0).getVelocity().set(velocityX, velocityY); create(item, amount, x, y, 0).getVelocity().set(velocityX, velocityY);
} }
@@ -70,6 +68,9 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
Effects.effect(UnitFx.pickup, drop); Effects.effect(UnitFx.pickup, drop);
} }
itemGroup.removeByID(itemid); itemGroup.removeByID(itemid);
if(Net.client()){
netClient.addRemovedEntity(itemid);
}
} }
/**Internal use only!*/ /**Internal use only!*/
@@ -188,11 +189,6 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
} }
} }
@Override
public void removed() {
Pooling.free(this);
}
@Override @Override
public void reset() { public void reset() {
time = 0f; time = 0f;

View File

@@ -8,32 +8,36 @@ import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.gen.CallEntity; import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Build; import io.anuke.mindustry.world.Build;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BreakBlock;
import io.anuke.mindustry.world.blocks.BreakBlock.BreakEntity;
import io.anuke.mindustry.world.blocks.BuildBlock; import io.anuke.mindustry.world.blocks.BuildBlock;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.trait.Entity;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill; import io.anuke.ucore.graphics.Fill;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.graphics.Shapes; import io.anuke.ucore.graphics.Shapes;
import io.anuke.ucore.util.*; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import static io.anuke.mindustry.Vars.tilesize; import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
/**Interface for units that build, break or mine things.*/ /**Interface for units that build, break or mine things.*/
public interface BuilderTrait { public interface BuilderTrait extends Entity{
//these are not instance variables! //these are not instance variables!
Translator[] tmptr = {new Translator(), new Translator(), new Translator(), new Translator()}; Translator[] tmptr = {new Translator(), new Translator(), new Translator(), new Translator()};
float placeDistance = 140f; float placeDistance = 140f;
@@ -54,6 +58,49 @@ public interface BuilderTrait {
/**Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all.*/ /**Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all.*/
float getBuildPower(Tile tile); float getBuildPower(Tile tile);
default void writeBuilding(DataOutput output) throws IOException{
BuildRequest request = getCurrentRequest();
if(request != null){
output.writeByte(request.remove ? 1 : 0);
output.writeInt(world.toPacked(request.x, request.y));
if(!request.remove){
output.writeByte(request.recipe.id);
output.writeByte(request.rotation);
}
}else{
output.writeByte(-1);
}
}
default void readBuilding(DataInput input) throws IOException{
readBuilding(input, true);
}
default void readBuilding(DataInput input, boolean applyChanges) throws IOException{
synchronized (getPlaceQueue()) {
if(applyChanges) getPlaceQueue().clear();
byte type = input.readByte();
if (type != -1) {
int position = input.readInt();
BuildRequest request;
if (type == 1) { //remove
request = new BuildRequest(position % world.width(), position / world.width());
} else { //place
byte recipe = input.readByte();
byte rotation = input.readByte();
request = new BuildRequest(position % world.width(), position / world.width(), rotation, Recipe.getByID(recipe));
}
if(applyChanges){
getPlaceQueue().addLast(request);
}
}
}
}
/**Return whether this builder's place queue contains items.*/ /**Return whether this builder's place queue contains items.*/
default boolean isBuilding(){ default boolean isBuilding(){
return getPlaceQueue().size != 0; return getPlaceQueue().size != 0;
@@ -77,11 +124,7 @@ public interface BuilderTrait {
/**Clears the placement queue.*/ /**Clears the placement queue.*/
default void clearBuilding(){ default void clearBuilding(){
if(this instanceof Player) { getPlaceQueue().clear();
CallBlocks.onBuildDeselect((Player) this);
}else{
getPlaceQueue().clear();
}
} }
/**Add another build requests to the tail of the queue, if it doesn't exist there yet.*/ /**Add another build requests to the tail of the queue, if it doesn't exist there yet.*/
@@ -99,7 +142,9 @@ public interface BuilderTrait {
/**Return the build requests currently active, or the one at the top of the queue. /**Return the build requests currently active, or the one at the top of the queue.
* May return null.*/ * May return null.*/
default BuildRequest getCurrentRequest(){ default BuildRequest getCurrentRequest(){
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first(); synchronized (getPlaceQueue()) {
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first();
}
} }
/**Update building mechanism for this unit. /**Update building mechanism for this unit.
@@ -117,75 +162,40 @@ public interface BuilderTrait {
setMineTile(null); setMineTile(null);
} }
TileEntity core = unit.getClosestCore();
//if there is no core to build with, stop building!
if(core == null){
return;
}
Tile tile = world.tile(current.x, current.y); Tile tile = world.tile(current.x, current.y);
if(unit.distanceTo(tile) > placeDistance || //out of range, skip it if (!(tile.block() instanceof BuildBlock)) {
(current.lastEntity != null && current.lastEntity.isDead())) { //build/destroy request has died, skip it if(!current.remove && Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)) {
getPlaceQueue().removeFirst(); Build.beginPlace(unit.getTeam(), current.x, current.y, current.recipe, current.rotation);
}else if(current.remove){ }else if(current.remove && Build.validBreak(unit.getTeam(), current.x, current.y)){
Build.beginBreak(unit.getTeam(), current.x, current.y);
if (!(tile.block() instanceof BreakBlock)) { //check if haven't started placing
if(Build.validBreak(unit.getTeam(), current.x, current.y)){
//if it's valid, place it
if(!current.requested && unit instanceof Player){
CallBlocks.breakBlock((Player)unit, unit.getTeam(), current.x, current.y);
current.requested = true;
}
}else{
//otherwise, skip it
getPlaceQueue().removeFirst();
}
}else{ }else{
TileEntity core = unit.getClosestCore(); getPlaceQueue().removeFirst();
return;
//if there is no core to build with, stop building!
if(core == null){
return;
}
//otherwise, update it.
BreakEntity entity = tile.entity();
current.lastEntity = entity;
entity.addProgress(core, unit, 1f / entity.breakTime * Timers.delta() * getBuildPower(tile));
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
getCurrentRequest().progress = entity.progress();
}
}else{
if (!(tile.block() instanceof BuildBlock)) { //check if haven't started placing
if(Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)){
//if it's valid, place it
if(!current.requested && unit instanceof Player){
CallBlocks.placeBlock((Player)unit, unit.getTeam(), current.x, current.y, current.recipe, current.rotation);
current.requested = true;
}
}else{
//otherwise, skip it
getPlaceQueue().removeFirst();
}
}else{
TileEntity core = unit.getClosestCore();
//if there is no core to build with, stop building!
if(core == null){
return;
}
//otherwise, update it.
BuildEntity entity = tile.entity();
current.lastEntity = entity;
entity.addProgress(core.items, 1f / entity.recipe.cost * Timers.delta() * getBuildPower(tile));
if(unit instanceof Player){
entity.lastBuilder = (Player)unit;
}
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
getCurrentRequest().progress = entity.progress();
} }
} }
//otherwise, update it.
BuildEntity entity = tile.entity();
//deconstructing is 2x as fast
if(current.remove){
entity.deconstruct(unit, core, 2f / entity.buildCost * Timers.delta() * getBuildPower(tile));
}else{
entity.construct(unit, core, 1f / entity.buildCost * Timers.delta() * getBuildPower(tile));
}
if(unit.distanceTo(tile) <= placeDistance){
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
}
current.progress = entity.progress();
} }
/**Do not call directly.*/ /**Do not call directly.*/
@@ -231,7 +241,11 @@ public interface BuilderTrait {
Tile tile = world.tile(request.x, request.y); Tile tile = world.tile(request.x, request.y);
Draw.color(unit.distanceTo(tile) > placeDistance || request.remove ? Palette.remove : Palette.accent); if(unit.distanceTo(tile) > placeDistance){
return;
}
Draw.color(Palette.accent);
float focusLen = 3.8f + Mathf.absin(Timers.time(), 1.1f, 0.6f); float focusLen = 3.8f + Mathf.absin(Timers.time(), 1.1f, 0.6f);
float px = unit.x + Angles.trnsx(unit.rotation, focusLen); float px = unit.x + Angles.trnsx(unit.rotation, focusLen);
float py = unit.y + Angles.trnsy(unit.rotation, focusLen); float py = unit.y + Angles.trnsy(unit.rotation, focusLen);
@@ -300,9 +314,6 @@ public interface BuilderTrait {
public final Recipe recipe; public final Recipe recipe;
public final boolean remove; public final boolean remove;
public boolean requested;
public TileEntity lastEntity;
public float progress; public float progress;
/**This creates a build request.*/ /**This creates a build request.*/

View File

@@ -32,14 +32,14 @@ public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
CallEntity.setCarryOf(this instanceof Player ? (Player)this : null, this, unit); CallEntity.setCarryOf(this instanceof Player ? (Player)this : null, this, unit);
} }
@Remote(called = Loc.server, targets = Loc.both, forward = true, in = In.entities) @Remote(called = Loc.both, targets = Loc.both, forward = true, in = In.entities)
static void dropSelf(Player player){ static void dropSelf(Player player){
if(player.getCarrier() != null){ if(player.getCarrier() != null){
player.getCarrier().dropCarry(); player.getCarrier().dropCarry();
} }
} }
@Remote(called = Loc.server, targets = Loc.both, forward = true, in = In.entities) @Remote(called = Loc.both, targets = Loc.both, forward = true, in = In.entities)
static void setCarryOf(Player player, CarryTrait trait, CarriableTrait unit){ static void setCarryOf(Player player, CarryTrait trait, CarriableTrait unit){
if(player != null){ //when a server recieves this called from a player, set the carrier to the player. if(player != null){ //when a server recieves this called from a player, set the carrier to the player.
trait = player; trait = player;

View File

@@ -0,0 +1,10 @@
package io.anuke.mindustry.entities.traits;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.world.Tile;
public interface SpawnerTrait {
Tile getTile();
void updateSpawning(Unit unit);
float getSpawnProgress();
}

View File

@@ -12,6 +12,7 @@ import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.effect.ScorchDecal; import io.anuke.mindustry.entities.effect.ScorchDecal;
import io.anuke.mindustry.entities.traits.ShooterTrait; import io.anuke.mindustry.entities.traits.ShooterTrait;
import io.anuke.mindustry.entities.traits.SpawnerTrait;
import io.anuke.mindustry.entities.traits.TargetTrait; import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.TeamInfo.TeamData; import io.anuke.mindustry.game.TeamInfo.TeamData;
@@ -52,7 +53,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
protected boolean isWave; protected boolean isWave;
protected Squad squad; protected Squad squad;
protected int spawner = -1; protected int spawner;
/**Initialize the type and team of this unit. Only call once!*/ /**Initialize the type and team of this unit. Only call once!*/
public void init(UnitType type, Team team){ public void init(UnitType type, Team team){
@@ -62,8 +63,8 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
this.team = team; this.team = team;
} }
public void setSpawner(UnitFactoryEntity spawner) { public void setSpawner(Tile tile) {
this.spawner = spawner.tile.packedPosition(); this.spawner = tile.packedPosition();
} }
public UnitType getType() { public UnitType getType() {
@@ -92,6 +93,19 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
((TileEntity)target).tile.block().flags.contains(flag); ((TileEntity)target).tile.block().flags.contains(flag);
} }
public void updateRespawning(){
if(spawner == -1) return;
Tile tile = world.tile(spawner);
if(tile != null && tile.entity != null){
if(tile.entity instanceof SpawnerTrait){
((SpawnerTrait) tile.entity).updateSpawning(this);
}
}else{
spawner = -1;
}
}
public void setState(UnitState state){ public void setState(UnitState state){
this.state.set(state); this.state.set(state);
} }
@@ -162,6 +176,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
} }
} }
@Override
public boolean isValid() {
return super.isValid() && isAdded();
}
@Override @Override
public Timer getTimer() { public Timer getTimer() {
return timer; return timer;
@@ -249,6 +268,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
if(hitTime < 0) hitTime = 0; if(hitTime < 0) hitTime = 0;
if(isDead()){
updateRespawning();
return;
}
if(Net.client()){ if(Net.client()){
interpolate(); interpolate();
status.update(this); status.update(this);
@@ -317,7 +341,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
hitboxTile.setSize(type.hitsizeTile); hitboxTile.setSize(type.hitsizeTile);
state.set(getStartState()); state.set(getStartState());
heal(); health(maxHealth());
} }
@Override @Override
@@ -348,6 +372,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
public void write(DataOutput data) throws IOException{ public void write(DataOutput data) throws IOException{
super.writeSave(data); super.writeSave(data);
data.writeByte(type.id); data.writeByte(type.id);
data.writeInt(spawner);
} }
@Override @Override
@@ -355,6 +380,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
float lastx = x, lasty = y, lastrot = rotation; float lastx = x, lasty = y, lastrot = rotation;
super.readSave(data); super.readSave(data);
this.type = UnitType.getByID(data.readByte()); this.type = UnitType.getByID(data.readByte());
this.spawner = data.readInt();
interpolator.read(lastx, lasty, x, y, time, rotation); interpolator.read(lastx, lasty, x, y, time, rotation);
rotation = lastrot; rotation = lastrot;
@@ -368,7 +394,9 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
public static void onUnitDeath(BaseUnit unit){ public static void onUnitDeath(BaseUnit unit){
if(unit == null) return; if(unit == null) return;
UnitDrops.dropItems(unit); if(Net.server() || !Net.active()){
UnitDrops.dropItems(unit);
}
float explosiveness = 2f + (unit.inventory.hasItem() ? unit.inventory.getItem().item.explosiveness * unit.inventory.getItem().amount : 0f); float explosiveness = 2f + (unit.inventory.hasItem() ? unit.inventory.getItem().item.explosiveness * unit.inventory.getItem().amount : 0f);
float flammability = (unit.inventory.hasItem() ? unit.inventory.getItem().item.flammability * unit.inventory.getItem().amount : 0f); float flammability = (unit.inventory.hasItem() ? unit.inventory.getItem().item.flammability * unit.inventory.getItem().amount : 0f);

View File

@@ -22,7 +22,6 @@ import static io.anuke.mindustry.Vars.world;
public abstract class FlyingUnit extends BaseUnit implements CarryTrait{ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
protected static Translator vec = new Translator(); protected static Translator vec = new Translator();
protected static float maxAim = 30f;
protected static float wobblyness = 0.6f; protected static float wobblyness = 0.6f;
protected Trail trail = new Trail(8); protected Trail trail = new Trail(8);

View File

@@ -2,7 +2,7 @@ package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Items; import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.gen.CallEntity; import io.anuke.mindustry.entities.effect.ItemDrop;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
@@ -21,9 +21,9 @@ public class UnitDrops {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
for(Item item : dropTable){ for(Item item : dropTable){
if(Mathf.chance(0.2)){ if(Mathf.chance(0.03)){
int amount = Mathf.random(1, 5); int amount = Mathf.random(20, 40);
CallEntity.createItemDrop(item, amount, unit.x + Mathf.range(2f), unit.y + Mathf.range(2f), ItemDrop.create(item, amount, unit.x + Mathf.range(2f), unit.y + Mathf.range(2f),
unit.getVelocity().x + Mathf.range(3f), unit.getVelocity().y + Mathf.range(3f)); unit.getVelocity().x + Mathf.range(3f), unit.getVelocity().y + Mathf.range(3f));
} }
} }

View File

@@ -18,7 +18,6 @@ import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack; import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BuildBlock; import io.anuke.mindustry.world.blocks.BuildBlock;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
@@ -206,26 +205,19 @@ public class Drone extends FlyingUnit implements BuilderTrait {
public void write(DataOutput data) throws IOException { public void write(DataOutput data) throws IOException {
super.write(data); super.write(data);
data.writeInt(mineTile == null ? -1 : mineTile.packedPosition()); data.writeInt(mineTile == null ? -1 : mineTile.packedPosition());
data.writeInt(placeQueue.size == 0 ? -1 : world.tile(placeQueue.last().x, placeQueue.last().y).packedPosition()); writeBuilding(data);
data.writeByte(placeQueue.size == 0 ? -1 : placeQueue.last().recipe.id);
} }
@Override @Override
public void read(DataInput data, long time) throws IOException { public void read(DataInput data, long time) throws IOException {
super.read(data, time); super.read(data, time);
int mined = data.readInt(); int mined = data.readInt();
int pp = data.readInt();
byte rid = data.readByte(); readBuilding(data);
if(mined != -1){ if(mined != -1){
mineTile = world.tile(mined); mineTile = world.tile(mined);
} }
if(pp != -1){
Tile tile = world.tile(pp);
placeQueue.clear();
placeQueue.addLast(new BuildRequest(tile.x, tile.y, tile.getRotation(), Recipe.getByID(rid)));
}
} }
public final UnitState public final UnitState

View File

@@ -4,12 +4,13 @@ import io.anuke.ucore.util.Bundles;
public enum GameMode{ public enum GameMode{
waves, waves,
sandbox{ //disabled for technical reasons
/*sandbox{
{ {
infiniteResources = true; infiniteResources = true;
disableWaveTimer = true; disableWaveTimer = true;
} }
}, },*/
freebuild{ freebuild{
{ {
disableWaveTimer = true; disableWaveTimer = true;

View File

@@ -17,8 +17,8 @@ public class WaveCreator{
}}, }},
new SpawnGroup(UnitTypes.vtol){{ new SpawnGroup(UnitTypes.vtol){{
begin = 4; begin = 12;
end = 6; end = 14;
}}, }},
new SpawnGroup(UnitTypes.scout){{ new SpawnGroup(UnitTypes.scout){{
@@ -29,7 +29,7 @@ public class WaveCreator{
}}, }},
new SpawnGroup(UnitTypes.titan){{ new SpawnGroup(UnitTypes.titan){{
begin = 8; begin = 9;
spacing = 3; spacing = 3;
unitScaling = 2; unitScaling = 2;
@@ -71,7 +71,7 @@ public class WaveCreator{
}}, }},
new SpawnGroup(UnitTypes.vtol){{ new SpawnGroup(UnitTypes.vtol){{
begin = 14; begin = 16;
unitScaling = 2; unitScaling = 2;
spacing = 2; spacing = 2;
@@ -100,7 +100,7 @@ public class WaveCreator{
}}, }},
new SpawnGroup(UnitTypes.scout){{ new SpawnGroup(UnitTypes.scout){{
begin = 25; begin = 35;
spacing = 3; spacing = 3;
unitAmount = 4; unitAmount = 4;
groupAmount = 2; groupAmount = 2;
@@ -120,9 +120,10 @@ public class WaveCreator{
}}, }},
new SpawnGroup(UnitTypes.monsoon){{ new SpawnGroup(UnitTypes.monsoon){{
begin = 35; begin = 40;
ammoItem = Items.blastCompound; ammoItem = Items.blastCompound;
unitAmount = 2; unitAmount = 2;
spacing = 2;
unitScaling = 3; unitScaling = 3;
max = 8; max = 8;
}}, }},

View File

@@ -59,36 +59,42 @@ public class BlockRenderer{
Graphics.surface(renderer.effectSurface); Graphics.surface(renderer.effectSurface);
for(int x = -rangex - expandr; x <= rangex + expandr; x++){ int avgx = Mathf.scl(camera.position.x, tilesize);
for(int y = -rangey - expandr; y <= rangey + expandr; y++){ int avgy = Mathf.scl(camera.position.y, tilesize);
int worldx = Mathf.scl(camera.position.x, tilesize) + x;
int worldy = Mathf.scl(camera.position.y, tilesize) + y;
boolean expanded = (x < -rangex || x > rangex || y < -rangey || y > rangey);
Tile tile = world.tile(worldx, worldy); int minx = Math.max(avgx - rangex - expandr, 0);
int miny = Math.max(avgy - rangey - expandr, 0);
int maxx = Math.min(world.width() - 1, avgx + rangex + expandr);
int maxy = Math.min(world.height() - 1, avgy+ rangey + expandr);
if(tile != null){ for(int x = minx; x <= maxx; x++){
Block block = tile.block(); for(int y = miny; y <= maxy; y++){
boolean expanded = (Math.abs(x - avgx) > rangex || Math.abs(y - avgy) > rangey);
if(!expanded && block != Blocks.air && world.isAccessible(worldx, worldy)){ synchronized (Tile.tileSetLock) {
synchronized (Tile.tileSetLock) { Tile tile = world.rawTile(x, y);
if (tile != null) {
Block block = tile.block();
if (!expanded && block != Blocks.air && world.isAccessible(x, y)) {
tile.block().drawShadow(tile); tile.block().drawShadow(tile);
} }
}
if(!(block instanceof StaticBlock)){ if (!(block instanceof StaticBlock)) {
if(block != Blocks.air){ if (block != Blocks.air) {
if(!expanded){ if (!expanded) {
addRequest(tile, Layer.block); addRequest(tile, Layer.block);
}
if(block.expanded || !expanded){
if(block.layer != null && block.isLayer(tile)){
addRequest(tile, block.layer);
} }
if(block.layer2 != null && block.isLayer2(tile)){ if (block.expanded || !expanded) {
addRequest(tile, block.layer2); if (block.layer != null && block.isLayer(tile)) {
addRequest(tile, block.layer);
}
if (block.layer2 != null && block.isLayer2(tile)) {
addRequest(tile, block.layer2);
}
} }
} }
} }

View File

@@ -123,14 +123,12 @@ public class FogRenderer implements Disposable{
Graphics.shader(); Graphics.shader();
Graphics.begin(); Graphics.begin();
Core.batch.draw(renderer.pixelSurface.texture(), px, py + vh, vw, -vh); Core.batch.draw(renderer.pixelSurface.texture(), px, py + vh, vw, -vh);
Graphics.end(); Graphics.end();
if(Core.batch instanceof ClipSpriteBatch){ if(Core.batch instanceof ClipSpriteBatch){
((ClipSpriteBatch) Core.batch).enableClip(true); ((ClipSpriteBatch) Core.batch).enableClip(true);
} }
} }
public Texture getTexture(){ public Texture getTexture(){

View File

@@ -6,16 +6,17 @@ import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.content.blocks.Blocks;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.game.TeamInfo.TeamData; import io.anuke.mindustry.game.TeamInfo.TeamData;
import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockBar; import io.anuke.mindustry.world.meta.BlockBar;
import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.function.Callable; import io.anuke.ucore.function.Callable;
import io.anuke.ucore.graphics.CapStyle;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill; import io.anuke.ucore.graphics.Fill;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
@@ -66,9 +67,7 @@ public class OverlayRenderer {
Tile tile = world.tileWorld(vec.x, vec.y); Tile tile = world.tileWorld(vec.x, vec.y);
if (tile != null && tile.block() != Blocks.air) { if (tile != null && tile.block() != Blocks.air) {
Tile target = tile; Tile target = tile.target();
if (tile.isLinked())
target = tile.getLinked();
if (showBlockDebug && target.entity != null) { if (showBlockDebug && target.entity != null) {
Draw.color(Color.RED); Draw.color(Color.RED);
@@ -93,49 +92,51 @@ public class OverlayRenderer {
Draw.reset(); Draw.reset();
} }
if (target.entity != null) {
int[] values = {0, 0};
Tile t = target;
boolean[] doDraw = {false};
Callable drawbars = () -> {
for (BlockBar bar : t.block().bars.list()) {
//TODO fix.
float offset = Mathf.sign(bar.top) * (t.block().size / 2f * tilesize + 2f + (bar.top ? values[0] : values[1]));
float value = bar.value.get(t);
if (MathUtils.isEqual(value, -1f)) continue;
if(doDraw[0]){
drawBar(bar.type.color, t.drawx(), t.drawy() + offset, value);
}
if (bar.top)
values[0]++;
else
values[1]++;
}
};
drawbars.run();
if(values[0] > 0){
drawEncloser(target.drawx(), target.drawy() + target.block().size * tilesize/2f + 2f + values[0]/2f - 0.5f + (values[0] > 2 ? 0.5f : 0), values[0]);
}
if(values[1] > 0){
drawEncloser(target.drawx(), target.drawy() - target.block().size * tilesize/2f - 2f - values[1]/2f - 0.5f, values[1]);
}
doDraw[0] = true;
values[0] = 0;
values[1] = 1;
drawbars.run();
}
synchronized (Tile.tileSetLock) { synchronized (Tile.tileSetLock) {
Block block = target.block();
TileEntity entity = target.entity;
if (entity != null) {
int[] values = {0, 0};
boolean[] doDraw = {false};
Callable drawbars = () -> {
for (BlockBar bar : block.bars.list()) {
float offset = Mathf.sign(bar.top) * (block.size / 2f * tilesize + 2f + (bar.top ? values[0] : values[1]));
float value = bar.value.get(target);
if (MathUtils.isEqual(value, -1f)) continue;
if(doDraw[0]){
drawBar(bar.type.color, target.drawx(), target.drawy() + offset, value);
}
if (bar.top)
values[0]++;
else
values[1]++;
}
};
drawbars.run();
if(values[0] > 0){
drawEncloser(target.drawx(), target.drawy() + block.size * tilesize/2f + 2f, values[0]);
}
if(values[1] > 0){
drawEncloser(target.drawx(), target.drawy() - block.size * tilesize/2f - 2f - values[1], values[1]);
}
doDraw[0] = true;
values[0] = 0;
values[1] = 1;
drawbars.run();
}
target.block().drawSelect(target); target.block().drawSelect(target);
} }
} }
@@ -180,7 +181,7 @@ public class OverlayRenderer {
y = (int)(y + 0.0001f); y = (int)(y + 0.0001f);
} }
drawEncloser(x, y - 8f, 2f); drawEncloser(x, y - 9f, 2f);
drawBar(Palette.healthstats, x, y - 8f, unit.healthf()); drawBar(Palette.healthstats, x, y - 8f, unit.healthf());
drawBar(Palette.ammo, x, y - 9f, unit.getAmmoFraction()); drawBar(Palette.ammo, x, y - 9f, unit.getAmmoFraction());
} }
@@ -206,15 +207,11 @@ public class OverlayRenderer {
} }
void drawEncloser(float x, float y, float height){ void drawEncloser(float x, float y, float height){
x -= 0.5f;
y += 0.5f - (height-1f)/2f;
float len = 3; float len = 3;
Lines.stroke(2f + height);
Draw.color(Palette.bar); Draw.color(Palette.bar);
Lines.line(x - len - 0.5f, y, x + len + 1.5f, y, CapStyle.none); Fill.crect(x - len - 1, y - 1, len*2f + 2f, height + 2f);
Draw.color();
Draw.reset();
} }
} }

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.graphics; package io.anuke.mindustry.graphics;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.FloatArray;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill; import io.anuke.ucore.graphics.Fill;
@@ -9,14 +10,20 @@ import io.anuke.ucore.util.Mathf;
/**Class that renders a trail.*/ /**Class that renders a trail.*/
public class Trail { public class Trail {
private final static float maxJump = 15f;
private final int length; private final int length;
private final FloatArray points = new FloatArray(); private final FloatArray points = new FloatArray();
private float lastX, lastY;
public Trail(int length){ public Trail(int length){
this.length = length; this.length = length;
} }
public synchronized void update(float curx, float cury){ public synchronized void update(float curx, float cury){
if(Vector2.dst(curx, cury, lastX, lastY) >= maxJump){
points.clear();
}
points.add(curx, cury); points.add(curx, cury);
if(points.size > length*2) { if(points.size > length*2) {
@@ -24,6 +31,9 @@ public class Trail {
System.arraycopy(items, 2, items, 0, points.size - 2); System.arraycopy(items, 2, items, 0, points.size - 2);
points.size -= 2; points.size -= 2;
} }
lastX = curx;
lastY = cury;
} }
public synchronized void clear(){ public synchronized void clear(){

View File

@@ -19,9 +19,9 @@ public class DefaultKeybinds {
new Category("General"), new Category("General"),
"move_x", new Axis(Input.A, Input.D), "move_x", new Axis(Input.A, Input.D),
"move_y", new Axis(Input.S, Input.W), "move_y", new Axis(Input.S, Input.W),
"select", Input.MOUSE_LEFT, //"select", Input.MOUSE_LEFT,
"break", Input.MOUSE_RIGHT, //"break", Input.MOUSE_RIGHT,
"shoot", Input.MOUSE_LEFT, //"shoot", Input.MOUSE_LEFT,
"rotate", new Axis(Input.SCROLL), "rotate", new Axis(Input.SCROLL),
"dash", Input.SHIFT_LEFT, "dash", Input.SHIFT_LEFT,
"drop_unit", Input.SHIFT_LEFT, "drop_unit", Input.SHIFT_LEFT,
@@ -39,8 +39,7 @@ public class DefaultKeybinds {
"chat_history_prev", Input.UP, "chat_history_prev", Input.UP,
"chat_history_next", Input.DOWN, "chat_history_next", Input.DOWN,
"chat_scroll", new Axis(Input.SCROLL), "chat_scroll", new Axis(Input.SCROLL),
"console", Input.GRAVE, "console", Input.GRAVE
"block_logs", Input.I
); );
KeyBinds.defaultSection(section, DeviceType.controller, KeyBinds.defaultSection(section, DeviceType.controller,
@@ -49,9 +48,9 @@ public class DefaultKeybinds {
"move_y", new Axis(Input.CONTROLLER_L_STICK_VERTICAL_AXIS), "move_y", new Axis(Input.CONTROLLER_L_STICK_VERTICAL_AXIS),
"cursor_x", new Axis(Input.CONTROLLER_R_STICK_HORIZONTAL_AXIS), "cursor_x", new Axis(Input.CONTROLLER_R_STICK_HORIZONTAL_AXIS),
"cursor_y", new Axis(Input.CONTROLLER_R_STICK_VERTICAL_AXIS), "cursor_y", new Axis(Input.CONTROLLER_R_STICK_VERTICAL_AXIS),
"select", Input.CONTROLLER_R_BUMPER, //"select", Input.CONTROLLER_R_BUMPER,
"break", Input.CONTROLLER_L_BUMPER, //"break", Input.CONTROLLER_L_BUMPER,
"shoot", Input.CONTROLLER_R_TRIGGER, //"shoot", Input.CONTROLLER_R_TRIGGER,
"dash", Input.CONTROLLER_Y, "dash", Input.CONTROLLER_Y,
"rotate_alt", new Axis(Input.CONTROLLER_DPAD_RIGHT, Input.CONTROLLER_DPAD_LEFT), "rotate_alt", new Axis(Input.CONTROLLER_DPAD_RIGHT, Input.CONTROLLER_DPAD_LEFT),
"rotate", new Axis(Input.CONTROLLER_A, Input.CONTROLLER_B), "rotate", new Axis(Input.CONTROLLER_A, Input.CONTROLLER_B),

View File

@@ -300,20 +300,20 @@ public class DesktopInput extends InputHandler{
if(player.playerIndex > 0){ if(player.playerIndex > 0){
controlling = true; controlling = true;
} }
/*
if(Inputs.keyTap(section,"select")){ if(Inputs.keyTap(section,"select")){
Inputs.getProcessor().touchDown((int)getMouseX(), (int)getMouseY(), player.playerIndex, Buttons.LEFT); Inputs.getProcessor().touchDown((int)getMouseX(), (int)getMouseY(), player.playerIndex, Buttons.LEFT);
} }
if(Inputs.keyRelease(section,"select")){ if(Inputs.keyRelease(section,"select")){
Inputs.getProcessor().touchUp((int)getMouseX(), (int)getMouseY(), player.playerIndex, Buttons.LEFT); Inputs.getProcessor().touchUp((int)getMouseX(), (int)getMouseY(), player.playerIndex, Buttons.LEFT);
} }*/
float xa = Inputs.getAxis(section, "cursor_x"); float xa = Inputs.getAxis(section, "cursor_x");
float ya = Inputs.getAxis(section, "cursor_y"); float ya = Inputs.getAxis(section, "cursor_y");
if(Math.abs(xa) > controllerMin || Math.abs(ya) > controllerMin) { if(Math.abs(xa) > controllerMin || Math.abs(ya) > controllerMin) {
float scl = Settings.getInt("sensitivity")/100f * Unit.dp.scl(1f); float scl = Settings.getInt("sensitivity", 100)/100f * Unit.dp.scl(1f);
controlx += xa*baseControllerSpeed*scl; controlx += xa*baseControllerSpeed*scl;
controly -= ya*baseControllerSpeed*scl; controly -= ya*baseControllerSpeed*scl;
controlling = true; controlling = true;

View File

@@ -220,7 +220,7 @@ public abstract class InputHandler extends InputAdapter{
if(tile.block().acceptStack(stack.item, stack.amount, tile, player) > 0 && tile.block().hasItems){ if(tile.block().acceptStack(stack.item, stack.amount, tile, player) > 0 && tile.block().hasItems){
CallBlocks.transferInventory(player, tile); CallBlocks.transferInventory(player, tile);
}else{ }else{
CallEntity.dropItem(player, player.angleTo(x, y)); CallEntity.dropItem(player.angleTo(x, y));
} }
} }
@@ -267,7 +267,7 @@ public abstract class InputHandler extends InputAdapter{
player.addBuildRequest(new BuildRequest(tile.x, tile.y)); player.addBuildRequest(new BuildRequest(tile.x, tile.y));
} }
@Remote(targets = Loc.both, called = Loc.server, in = In.entities) @Remote(targets = Loc.client, called = Loc.server, in = In.entities)
public static void dropItem(Player player, float angle){ public static void dropItem(Player player, float angle){
if(Net.server() && !player.inventory.hasItem()){ if(Net.server() && !player.inventory.hasItem()){
throw new ValidateException(player, "Player cannot drop an item."); throw new ValidateException(player, "Player cannot drop an item.");
@@ -279,10 +279,12 @@ public abstract class InputHandler extends InputAdapter{
@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks) @Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks)
public static void transferInventory(Player player, Tile tile){ public static void transferInventory(Player player, Tile tile){
if(Net.server() && (!player.inventory.hasItem() || player.isTransferring)){ if(Net.server() && (!player.inventory.hasItem() || player.isTransferring)) {
throw new ValidateException(player, "Player cannot transfer an item."); throw new ValidateException(player, "Player cannot transfer an item.");
} }
if(player == null) return;
player.isTransferring = true; player.isTransferring = true;
ItemStack stack = player.inventory.getItem(); ItemStack stack = player.inventory.getItem();

View File

@@ -337,8 +337,8 @@ public class MobileInput extends InputHandler implements GestureListener{
drawRequest(request); drawRequest(request);
//draw last placed request //draw last placed request
if(!request.remove && request == lastPlaced){ if(!request.remove && request == lastPlaced && request.recipe != null){
recipe.result.drawPlace(tile.x, tile.y, rotation, validPlace(tile.x, tile.y, recipe.result, rotation)); request.recipe.result.drawPlace(tile.x, tile.y, rotation, validPlace(tile.x, tile.y, request.recipe.result, rotation));
} }
} }
@@ -353,7 +353,7 @@ public class MobileInput extends InputHandler implements GestureListener{
if(tile != null){ if(tile != null){
//draw placing //draw placing
if(mode == placing) { if(mode == placing && recipe != null) {
NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tile.x, tile.y, true, maxLength, lineScale); NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tile.x, tile.y, true, maxLength, lineScale);
Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y); Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y);
@@ -465,7 +465,7 @@ public class MobileInput extends InputHandler implements GestureListener{
if (tile == null) return false; if (tile == null) return false;
if(mode == placing) { if(mode == placing && recipe != null) {
//normalize area //normalize area
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, 100); NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, 100);
@@ -599,6 +599,10 @@ public class MobileInput extends InputHandler implements GestureListener{
selection.clear(); selection.clear();
} }
if(lineMode && mode == placing && recipe == null){
lineMode = false;
}
//if there is no mode and there's a recipe, switch to placing //if there is no mode and there's a recipe, switch to placing
if(recipe != null && mode == none){ if(recipe != null && mode == none){
mode = placing; mode = placing;

View File

@@ -49,11 +49,14 @@ public class Saves {
if(time > Settings.getInt("saveinterval")*60) { if(time > Settings.getInt("saveinterval")*60) {
saving = true; saving = true;
exec.submit(() -> { Timers.run(2f, () -> {
SaveIO.saveToSlot(current.index); try {
current.meta = SaveIO.getData(current.index); SaveIO.saveToSlot(current.index);
current.meta = SaveIO.getData(current.index);
}catch (Exception e){
e.printStackTrace();
}
saving = false; saving = false;
return true;
}); });
time = 0; time = 0;

View File

@@ -144,6 +144,16 @@ public class TypeIO {
return world.tile(buffer.getInt()); return world.tile(buffer.getInt());
} }
@WriteClass(Block.class)
public static void writeBlock(ByteBuffer buffer, Block block){
buffer.put((byte)block.id);
}
@ReadClass(Block.class)
public static Block readBlock(ByteBuffer buffer){
return Block.getByID(buffer.get());
}
@WriteClass(KickReason.class) @WriteClass(KickReason.class)
public static void writeKick(ByteBuffer buffer, KickReason reason){ public static void writeKick(ByteBuffer buffer, KickReason reason){
buffer.put((byte)reason.ordinal()); buffer.put((byte)reason.ordinal());

View File

@@ -19,7 +19,6 @@ import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.trait.Entity; import io.anuke.ucore.entities.trait.Entity;
import io.anuke.ucore.util.Bits; import io.anuke.ucore.util.Bits;
import io.anuke.ucore.util.Log;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -51,7 +50,6 @@ public class Save16 extends SaveFileVersion {
state.difficulty = Difficulty.values()[difficulty]; state.difficulty = Difficulty.values()[difficulty];
state.mode = GameMode.values()[mode]; state.mode = GameMode.values()[mode];
state.enemies = 0; //TODO display enemies correctly!
state.wave = wave; state.wave = wave;
state.wavetime = wavetime; state.wavetime = wavetime;

View File

@@ -12,6 +12,7 @@ import io.anuke.mindustry.world.blocks.Rock;
import io.anuke.mindustry.world.blocks.StaticBlock; import io.anuke.mindustry.world.blocks.StaticBlock;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import static io.anuke.mindustry.Vars.headless;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
public class Administration { public class Administration {
@@ -41,6 +42,15 @@ public class Administration {
return Settings.getBool("antigrief"); return Settings.getBool("antigrief");
} }
public boolean allowsCustomClients(){
return Settings.getBool("allow-custom", !headless);
}
public void setCustomClients(boolean allowed){
Settings.putBool("allow-custom", allowed);
Settings.save();
}
public boolean isValidateReplace(){ public boolean isValidateReplace(){
return false; return false;
} }

View File

@@ -10,8 +10,6 @@ import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.core.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.net.Packet.ImportantPacket;
import io.anuke.mindustry.net.Packet.UnimportantPacket;
import io.anuke.mindustry.net.Packets.StreamBegin; import io.anuke.mindustry.net.Packets.StreamBegin;
import io.anuke.mindustry.net.Packets.StreamChunk; import io.anuke.mindustry.net.Packets.StreamChunk;
import io.anuke.mindustry.net.Streamable.StreamBuilder; import io.anuke.mindustry.net.Streamable.StreamBuilder;
@@ -33,7 +31,6 @@ public class Net{
private static boolean active; private static boolean active;
private static boolean clientLoaded; private static boolean clientLoaded;
private static Array<Object> packetQueue = new Array<>(); private static Array<Object> packetQueue = new Array<>();
private static ObjectMap<Class<?>, Consumer> listeners = new ObjectMap<>();
private static ObjectMap<Class<?>, Consumer> clientListeners = new ObjectMap<>(); private static ObjectMap<Class<?>, Consumer> clientListeners = new ObjectMap<>();
private static ObjectMap<Class<?>, BiConsumer<Integer, Object>> serverListeners = new ObjectMap<>(); private static ObjectMap<Class<?>, BiConsumer<Integer, Object>> serverListeners = new ObjectMap<>();
private static ClientProvider clientProvider; private static ClientProvider clientProvider;
@@ -148,11 +145,6 @@ public class Net{
Net.serverProvider = provider; Net.serverProvider = provider;
} }
/**Registers a common listener for when an object is recieved. Fired on both client and serve.r*/
public static <T> void handle(Class<T> type, Consumer<T> listener){
listeners.put(type, listener);
}
/**Registers a client listener for when an object is recieved.*/ /**Registers a client listener for when an object is recieved.*/
public static <T> void handleClient(Class<T> type, Consumer<T> listener){ public static <T> void handleClient(Class<T> type, Consumer<T> listener){
clientListeners.put(type, listener); clientListeners.put(type, listener);
@@ -180,16 +172,14 @@ public class Net{
streams.remove(builder.id); streams.remove(builder.id);
handleClientReceived(builder.build()); handleClientReceived(builder.build());
} }
}else if(clientListeners.get(object.getClass()) != null || }else if(clientListeners.get(object.getClass()) != null){
listeners.get(object.getClass()) != null){
if(clientLoaded || object instanceof ImportantPacket){ if(clientLoaded || ((object instanceof Packet) && ((Packet) object).isImportant())){
if(clientListeners.get(object.getClass()) != null) clientListeners.get(object.getClass()).accept(object); if(clientListeners.get(object.getClass()) != null) clientListeners.get(object.getClass()).accept(object);
if(listeners.get(object.getClass()) != null) listeners.get(object.getClass()).accept(object);
synchronized (packetPoolLock) { synchronized (packetPoolLock) {
Pooling.free(object); Pooling.free(object);
} }
}else if(!(object instanceof UnimportantPacket)){ }else if(!((object instanceof Packet) && ((Packet) object).isUnimportant())){
packetQueue.add(object); packetQueue.add(object);
Log.info("Queuing packet {0}.", ClassReflection.getSimpleName(object.getClass())); Log.info("Queuing packet {0}.", ClassReflection.getSimpleName(object.getClass()));
}else{ }else{
@@ -205,9 +195,8 @@ public class Net{
/**Call to handle a packet being recieved for the server.*/ /**Call to handle a packet being recieved for the server.*/
public static void handleServerReceived(int connection, Object object){ public static void handleServerReceived(int connection, Object object){
if(serverListeners.get(object.getClass()) != null || listeners.get(object.getClass()) != null){ if(serverListeners.get(object.getClass()) != null){
if(serverListeners.get(object.getClass()) != null) serverListeners.get(object.getClass()).accept(connection, object); if(serverListeners.get(object.getClass()) != null) serverListeners.get(object.getClass()).accept(connection, object);
if(listeners.get(object.getClass()) != null) listeners.get(object.getClass()).accept(object);
synchronized (packetPoolLock) { synchronized (packetPoolLock) {
Pooling.free(object); Pooling.free(object);
} }

View File

@@ -5,11 +5,16 @@ import com.badlogic.gdx.utils.Pool.Poolable;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
public interface Packet extends Poolable{ public interface Packet extends Poolable{
void read(ByteBuffer buffer); default void read(ByteBuffer buffer){}
void write(ByteBuffer buffer); default void write(ByteBuffer buffer){}
default void reset() {} default void reset() {}
interface ImportantPacket{} default boolean isImportant(){
interface UnimportantPacket{} return false;
}
default boolean isUnimportant(){
return false;
}
} }

View File

@@ -5,9 +5,9 @@ import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.io.Version; import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.net.Packet.ImportantPacket; import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.net.Packet.UnimportantPacket;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.io.IOUtils; import io.anuke.ucore.io.IOUtils;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
@@ -19,13 +19,23 @@ import static io.anuke.mindustry.Vars.world;
/**Class for storing all packets.*/ /**Class for storing all packets.*/
public class Packets { public class Packets {
public static class Connect implements ImportantPacket{ public static class Connect implements Packet{
public int id; public int id;
public String addressTCP; public String addressTCP;
@Override
public boolean isImportant() {
return true;
}
} }
public static class Disconnect implements ImportantPacket{ public static class Disconnect implements Packet{
public int id; public int id;
@Override
public boolean isImportant() {
return true;
}
} }
public static class WorldStream extends Streamable{ public static class WorldStream extends Streamable{
@@ -62,7 +72,7 @@ public class Packets {
} }
public static class InvokePacket implements Packet{ public static class InvokePacket implements Packet{
public byte type; public byte type, priority;
public ByteBuffer writeBuffer; public ByteBuffer writeBuffer;
public int writeLength; public int writeLength;
@@ -70,6 +80,7 @@ public class Packets {
@Override @Override
public void read(ByteBuffer buffer) { public void read(ByteBuffer buffer) {
type = buffer.get(); type = buffer.get();
priority = buffer.get();
writeLength = buffer.getShort(); writeLength = buffer.getShort();
byte[] bytes = new byte[writeLength]; byte[] bytes = new byte[writeLength];
buffer.get(bytes); buffer.get(bytes);
@@ -79,6 +90,7 @@ public class Packets {
@Override @Override
public void write(ByteBuffer buffer) { public void write(ByteBuffer buffer) {
buffer.put(type); buffer.put(type);
buffer.put(priority);
buffer.putShort((short)writeLength); buffer.putShort((short)writeLength);
writeBuffer.position(0); writeBuffer.position(0);
@@ -86,17 +98,20 @@ public class Packets {
buffer.put(writeBuffer.get()); buffer.put(writeBuffer.get());
} }
} }
}
public static class SnapshotPacket implements Packet, UnimportantPacket{
@Override @Override
public void read(ByteBuffer buffer) { public void reset() {
priority = 0;
} }
@Override @Override
public void write(ByteBuffer buffer) { public boolean isImportant() {
return priority == 1;
}
@Override
public boolean isUnimportant() {
return priority == 2;
} }
} }
@@ -109,6 +124,7 @@ public class Packets {
public float x, y, pointerX, pointerY, rotation, baseRotation, xv, yv; public float x, y, pointerX, pointerY, rotation, baseRotation, xv, yv;
public Tile mining; public Tile mining;
public boolean boosting, shooting; public boolean boosting, shooting;
public BuildRequest currentRequest;
@Override @Override
public void write(ByteBuffer buffer) { public void write(ByteBuffer buffer) {
@@ -132,6 +148,19 @@ public class Packets {
buffer.putShort((short)(player.baseRotation*2)); buffer.putShort((short)(player.baseRotation*2));
buffer.putInt(player.getMineTile() == null ? -1 : player.getMineTile().packedPosition()); buffer.putInt(player.getMineTile() == null ? -1 : player.getMineTile().packedPosition());
BuildRequest request = player.getCurrentRequest();
if(request != null){
buffer.put(request.remove ? (byte)1 : 0);
buffer.putInt(world.toPacked(request.x, request.y));
if(!request.remove){
buffer.put((byte)request.recipe.id);
buffer.put((byte)request.rotation);
}
}else{
buffer.put((byte)-1);
}
} }
@Override @Override
@@ -151,11 +180,26 @@ public class Packets {
rotation = buffer.getShort()/2f; rotation = buffer.getShort()/2f;
baseRotation = buffer.getShort()/2f; baseRotation = buffer.getShort()/2f;
mining = world.tile(buffer.getInt()); mining = world.tile(buffer.getInt());
byte type = buffer.get();
if (type != -1) {
int position = buffer.getInt();
if (type == 1) { //remove
currentRequest = new BuildRequest(position % world.width(), position / world.width());
} else { //place
byte recipe = buffer.get();
byte rotation = buffer.get();
currentRequest = new BuildRequest(position % world.width(), position / world.width(), rotation, Recipe.getByID(recipe));
}
}else{
currentRequest = null;
}
} }
} }
public enum KickReason{ public enum KickReason{
kick, invalidPassword, clientOutdated, serverOutdated, banned, gameover(true), recentKick, nameInUse, idInUse, fastShoot, nameEmpty; kick, invalidPassword, clientOutdated, serverOutdated, banned, gameover(true), recentKick, nameInUse, idInUse, fastShoot, nameEmpty, customClient;
public final boolean quiet; public final boolean quiet;
KickReason(){ quiet = false; } KickReason(){ quiet = false; }
@@ -166,7 +210,7 @@ public class Packets {
} }
public enum AdminAction{ public enum AdminAction{
kick, ban, trace kick, ban, trace, wave
} }
/**Marks the beginning of a stream.*/ /**Marks the beginning of a stream.*/

View File

@@ -13,7 +13,6 @@ public class Registrator {
WorldStream.class, WorldStream.class,
ConnectPacket.class, ConnectPacket.class,
ClientSnapshotPacket.class, ClientSnapshotPacket.class,
SnapshotPacket.class,
InvokePacket.class InvokePacket.class
}; };
private static ObjectIntMap<Class<?>> ids = new ObjectIntMap<>(); private static ObjectIntMap<Class<?>> ids = new ObjectIntMap<>();

View File

@@ -2,14 +2,13 @@ package io.anuke.mindustry.net;
import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.badlogic.gdx.utils.reflect.ReflectionException; import com.badlogic.gdx.utils.reflect.ReflectionException;
import io.anuke.mindustry.net.Packet.ImportantPacket;
import io.anuke.mindustry.net.Packets.StreamBegin; import io.anuke.mindustry.net.Packets.StreamBegin;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
public class Streamable implements ImportantPacket{ public class Streamable implements Packet{
public transient ByteArrayInputStream stream; public transient ByteArrayInputStream stream;
public static class StreamBuilder{ public static class StreamBuilder{
@@ -47,4 +46,9 @@ public class Streamable implements ImportantPacket{
return stream.size() >= total; return stream.size() >= total;
} }
} }
@Override
public boolean isImportant() {
return true;
}
} }

View File

@@ -12,9 +12,10 @@ import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
import io.anuke.ucore.util.ThreadArray;
public class Item implements Comparable<Item>, UnlockableContent{ public class Item implements Comparable<Item>, UnlockableContent{
private static final Array<Item> items = new Array<>(); private static final ThreadArray<Item> items = new ThreadArray<>();
public final int id; public final int id;
public final String name; public final String name;

View File

@@ -163,6 +163,8 @@ public class Weapon extends Upgrade {
AmmoType type = shooter.getInventory().getAmmo(); AmmoType type = shooter.getInventory().getAmmo();
if(type == null) return;
weapon.tr.trns(rotation + 180f, type.recoil); weapon.tr.trns(rotation + 180f, type.recoil);
shooter.getVelocity().add(weapon.tr); shooter.getVelocity().add(weapon.tr);

View File

@@ -2,8 +2,6 @@ package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.net.Administration.PlayerInfo; import io.anuke.mindustry.net.Administration.PlayerInfo;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection;
import io.anuke.ucore.scene.ui.ScrollPane; import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.ui.layout.Table;
@@ -46,9 +44,8 @@ public class AdminsDialog extends FloatingDialog {
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> { ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
netServer.admins.unAdminPlayer(info.id); netServer.admins.unAdminPlayer(info.id);
for(Player player : playerGroup.all()){ for(Player player : playerGroup.all()){
NetConnection c = Net.getConnection(player.clientid); if(player.con != null){
if(c != null){ player.isAdmin = false;
//CallClient.adminSet(player, false);
break; break;
} }
} }

View File

@@ -4,19 +4,20 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Align;
import io.anuke.mindustry.game.Difficulty; import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.EventType.ResizeEvent;
import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.io.Map; import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.ui.BorderImage; import io.anuke.mindustry.ui.BorderImage;
import io.anuke.ucore.core.Events;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.scene.event.Touchable; import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.ButtonGroup;
import io.anuke.ucore.scene.ui.ImageButton; import io.anuke.ucore.scene.ui.ImageButton;
import io.anuke.ucore.scene.ui.ScrollPane; import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.TextButton;
import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.utils.Cursors; import io.anuke.ucore.scene.utils.Cursors;
import io.anuke.ucore.scene.utils.Elements;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
@@ -42,7 +43,7 @@ public class LevelDialog extends FloatingDialog{
int maxwidth = (Gdx.graphics.getHeight() > Gdx.graphics.getHeight() ? 2 : 4); int maxwidth = (Gdx.graphics.getHeight() > Gdx.graphics.getHeight() ? 2 : 4);
/*Table selmode = new Table(); Table selmode = new Table();
ButtonGroup<TextButton> group = new ButtonGroup<>(); ButtonGroup<TextButton> group = new ButtonGroup<>();
selmode.add("$text.level.mode").padRight(15f); selmode.add("$text.level.mode").padRight(15f);
@@ -50,14 +51,13 @@ public class LevelDialog extends FloatingDialog{
TextButton[] b = {null}; TextButton[] b = {null};
b[0] = Elements.newButton("$mode." + mode.name() + ".name", "toggle", () -> state.mode = mode); b[0] = Elements.newButton("$mode." + mode.name() + ".name", "toggle", () -> state.mode = mode);
b[0].update(() -> b[0].setChecked(state.mode == mode)); b[0].update(() -> b[0].setChecked(state.mode == mode));
b[0].setDisabled(true);
group.add(b[0]); group.add(b[0]);
selmode.add(b[0]).size(130f, 54f); selmode.add(b[0]).size(130f, 54f);
} }
selmode.addButton("?", this::displayGameModeHelp).size(50f, 54f).padLeft(18f); selmode.addButton("?", this::displayGameModeHelp).size(50f, 54f).padLeft(18f);
content().add(selmode); content().add(selmode);
content().row();*/ content().row();
Difficulty[] ds = Difficulty.values(); Difficulty[] ds = Difficulty.values();

View File

@@ -181,6 +181,8 @@ public class BlockInventoryFragment extends Fragment {
@Remote(called = Loc.server, targets = Loc.both, in = In.blocks, forward = true) @Remote(called = Loc.server, targets = Loc.both, in = In.blocks, forward = true)
public static void requestItem(Player player, Tile tile, Item item, int amount){ public static void requestItem(Player player, Tile tile, Item item, int amount){
if(player == null) return;
int removed = tile.block().removeStack(tile, item, amount); int removed = tile.block().removeStack(tile, item, amount);
player.inventory.addItem(item, removed); player.inventory.addItem(item, removed);

View File

@@ -6,7 +6,6 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.type.Category; import io.anuke.mindustry.type.Category;
@@ -182,7 +181,7 @@ public class BlocksFragment extends Fragment{
//add actual recipes //add actual recipes
for (Recipe r : recipes) { for (Recipe r : recipes) {
if(r.debugOnly && !debug) continue; if((r.debugOnly && !debug) || (r.desktopOnly && mobile)) continue;
ImageButton image = new ImageButton(new TextureRegion(), "select"); ImageButton image = new ImageButton(new TextureRegion(), "select");
@@ -190,7 +189,7 @@ public class BlocksFragment extends Fragment{
Stack istack = new Stack(); Stack istack = new Stack();
for(TextureRegion region : regions){ for(TextureRegion region : regions){
Image u = new Image(region); Image u = new Image(region);
u.update(() -> u.setColor(image.isDisabled() ? Color.GRAY : Color.WHITE)); u.update(() -> u.setColor(istack.getColor()));
istack.add(u); istack.add(u);
} }
@@ -236,35 +235,21 @@ public class BlocksFragment extends Fragment{
} }
}); });
image.setDisabled(() -> {
TileEntity entity = players[0].getClosestCore();
if(entity == null) return true;
for(ItemStack s : r.requirements){
if(!entity.items.hasItem(s.item, Mathf.ceil(s.amount/2f))){
return true;
}
}
return false;
});
recipeTable.add(image).size(size + 8); recipeTable.add(image).size(size + 8);
image.update(() -> { image.update(() -> {
if(!image.isDisabled()) { image.setChecked(r == control.input(0).recipe);
for (Player player : players) { TileEntity entity = players[0].getClosestCore();
if (control.input(player.playerIndex).recipe == r) {
image.setChecked(true); if(entity == null) return;
return;
} for(ItemStack s : r.requirements){
if(!entity.items.hasItem(s.item, Mathf.ceil(s.amount))){
istack.setColor(Color.GRAY);
return;
} }
}/*else{ }
if(control.input(0).recipe == r){ istack.setColor(Color.WHITE);
control.input(0).recipe = null;
}
}*/
image.setChecked(false);
}); });
if (i % rows == rows - 1) { if (i % rows == rows - 1) {

View File

@@ -2,14 +2,14 @@ package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import io.anuke.mindustry.content.bullets.TurretBullets;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.bullet.Bullet; import io.anuke.mindustry.entities.effect.ItemDrop;
import io.anuke.mindustry.entities.units.BaseUnit; import io.anuke.mindustry.entities.units.BaseUnit;
import io.anuke.mindustry.entities.units.UnitType; import io.anuke.mindustry.entities.units.UnitType;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
@@ -59,13 +59,13 @@ public class DebugFragment extends Fragment {
row(); row();
new button("noclip", "toggle", () -> noclip = !noclip); new button("noclip", "toggle", () -> noclip = !noclip);
row(); row();
new button("fire", () -> { new button("items", () -> {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
Bullet.create(TurretBullets.fireball, player, player.x, player.y, Mathf.random(360f)); ItemDrop.create(Item.all().random(), 5, player.x, player.y, Mathf.random(360f));
} }
}); });
row(); row();
new button("team", "toggle", () -> player.toggleTeam()); new button("team", "toggle", player::toggleTeam);
row(); row();
new button("blocks", "toggle", () -> showBlockDebug = !showBlockDebug); new button("blocks", "toggle", () -> showBlockDebug = !showBlockDebug);
row(); row();
@@ -185,7 +185,7 @@ public class DebugFragment extends Fragment {
result.append(player.id); result.append(player.id);
result.append("\n"); result.append("\n");
result.append(" cid: "); result.append(" cid: ");
result.append(player.clientid); result.append(player.con == null ? -1 : player.con.id);
result.append("\n"); result.append("\n");
result.append(" dead: "); result.append(" dead: ");
result.append(player.isDead()); result.append(player.isDead());

View File

@@ -7,7 +7,10 @@ import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Scaling; import com.badlogic.gdx.utils.Scaling;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.Packets.AdminAction;
import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.IntFormat; import io.anuke.mindustry.ui.IntFormat;
import io.anuke.mindustry.ui.Minimap; import io.anuke.mindustry.ui.Minimap;
@@ -288,10 +291,11 @@ public class HudFragment extends Fragment{
} }
private String getEnemiesRemaining() { private String getEnemiesRemaining() {
if(state.enemies == 1) { int enemies = unitGroups[Team.red.ordinal()].size();
return Bundles.format("text.enemies.single", state.enemies); if(enemies == 1) {
return Bundles.format("text.enemies.single", enemies);
} else { } else {
return Bundles.format("text.enemies", state.enemies); return Bundles.format("text.enemies", enemies);
} }
} }
@@ -310,7 +314,7 @@ public class HudFragment extends Fragment{
row(); row();
new label(() -> state.enemies > 0 ? new label(() -> unitGroups[Team.red.ordinal()].size() > 0 && state.mode.disableWaveTimer ?
getEnemiesRemaining() : getEnemiesRemaining() :
(state.mode.disableWaveTimer) ? "$text.waiting" (state.mode.disableWaveTimer) ? "$text.waiting"
: timef.get((int) (state.wavetime / 60f))) : timef.get((int) (state.wavetime / 60f)))
@@ -329,14 +333,17 @@ public class HudFragment extends Fragment{
private void playButton(float uheight){ private void playButton(float uheight){
new imagebutton("icon-play", 30f, () -> { new imagebutton("icon-play", 30f, () -> {
state.wavetime = 0f; if(Net.client() && players[0].isAdmin){
Call.onAdminRequest(players[0], AdminAction.wave);
}else {
state.wavetime = 0f;
}
}).height(uheight).fillX().right().padTop(-8f).padBottom(-12f).padLeft(-15).padRight(-10).width(40f).update(l->{ }).height(uheight).fillX().right().padTop(-8f).padBottom(-12f).padLeft(-15).padRight(-10).width(40f).update(l->{
boolean vis = state.mode.disableWaveTimer && (Net.server() || !Net.active()); boolean vis = state.mode.disableWaveTimer && ((Net.server() || players[0].isAdmin) || !Net.active());
boolean paused = state.is(State.paused) || !vis; boolean paused = state.is(State.paused) || !vis;
l.setVisible(vis);
l.getStyle().imageUp = Core.skin.getDrawable(vis ? "icon-play" : "clear"); l.getStyle().imageUp = Core.skin.getDrawable(vis ? "icon-play" : "clear");
l.setTouchable(!paused ? Touchable.enabled : Touchable.disabled); l.setTouchable(!paused ? Touchable.enabled : Touchable.disabled);
}); }).visible(() -> state.mode.disableWaveTimer && ((Net.server() || players[0].isAdmin) || !Net.active()) && unitGroups[Team.red.ordinal()].size() == 0);
} }
} }

View File

@@ -70,9 +70,19 @@ public class PlayerListFragment extends Fragment{
if(!checkmap.containsKey(player) || checkmap.get(player, false) != player.isAdmin){ if(!checkmap.containsKey(player) || checkmap.get(player, false) != player.isAdmin){
rebuild = true; rebuild = true;
} }
checkmap.put(player, player.isAdmin);
} }
if(rebuild) rebuild(); for(Player player : checkmap.keys()){
if(!player.isAdded()){
rebuild = true;
}
}
if(rebuild){
rebuild();
checkmap.clear();
for(Player player : playerGroup.all()){
checkmap.put(player, player.isAdmin);
}
}
}); });
visible(() -> visible); visible(() -> visible);
@@ -87,7 +97,7 @@ public class PlayerListFragment extends Fragment{
float h = 74f; float h = 74f;
for(Player player : playerGroup.all()){ for(Player player : playerGroup.all()){
NetConnection connection = gwt ? null : Net.getConnection(player.clientid); NetConnection connection = gwt ? null : player.con;
if(connection == null && Net.server() && !player.isLocal) continue; if(connection == null && Net.server() && !player.isLocal) continue;

View File

@@ -198,6 +198,8 @@ public abstract class BaseBlock {
/**Try dumping a specific item near the tile.*/ /**Try dumping a specific item near the tile.*/
public boolean tryDump(Tile tile, Item todump){ public boolean tryDump(Tile tile, Item todump){
if(tile.entity == null || !hasItems) return false;
int size = tile.block().size; int size = tile.block().size;
GridPoint2[] nearby = Edges.getEdges(size); GridPoint2[] nearby = Edges.getEdges(size);
@@ -207,7 +209,8 @@ public abstract class BaseBlock {
Tile other; Tile other;
Tile in; Tile in;
for(Item item : Item.all()){ for(int ii = 0; ii < Item.all().size; ii ++){
Item item = Item.getByID(ii);
other = tile.getNearby(nearby[i]); other = tile.getNearby(nearby[i]);
in = tile.getNearby(Edges.getInsideEdges(size)[i]); in = tile.getNearby(Edges.getInsideEdges(size)[i]);

View File

@@ -235,8 +235,8 @@ public class Block extends BaseBlock implements Content{
//TODO make this easier to config. //TODO make this easier to config.
public void setBars(){ public void setBars(){
if(hasPower) bars.add(new io.anuke.mindustry.world.meta.BlockBar(BarType.power, true, tile -> tile.entity.power.amount / powerCapacity)); if(hasPower) bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.amount / powerCapacity));
if(hasLiquids) bars.add(new io.anuke.mindustry.world.meta.BlockBar(BarType.liquid, true, tile -> tile.entity.liquids.amount / liquidCapacity)); if(hasLiquids) bars.add(new BlockBar(BarType.liquid, true, tile -> tile.entity.liquids.amount / liquidCapacity));
if(hasItems) bars.add(new BlockBar(BarType.inventory, true, tile -> (float)tile.entity.items.totalItems() / itemCapacity)); if(hasItems) bars.add(new BlockBar(BarType.inventory, true, tile -> (float)tile.entity.items.totalItems() / itemCapacity));
} }
@@ -343,9 +343,9 @@ public class Block extends BaseBlock implements Content{
return 0; return 0;
}else{ }else{
float result = 0f; float result = 0f;
for(Item item : Item.all()){ for (int i = 0; i < Item.all().size; i++) {
int amount = tile.entity.items.getItem(item); int amount = tile.entity.items.items[i];
result += item.flammability*amount; result += Item.getByID(i).flammability*amount;
} }
if(hasLiquids){ if(hasLiquids){
result += tile.entity.liquids.amount * tile.entity.liquids.liquid.flammability/3f; result += tile.entity.liquids.amount * tile.entity.liquids.liquid.flammability/3f;
@@ -432,7 +432,7 @@ public class Block extends BaseBlock implements Content{
if(shadowRegions != null) { if(shadowRegions != null) {
Draw.rect(shadowRegions[(Mathf.randomSeed(tile.id(), 0, variants - 1))], tile.worldx(), tile.worldy()); Draw.rect(shadowRegions[(Mathf.randomSeed(tile.id(), 0, variants - 1))], tile.worldx(), tile.worldy());
}else{ }else if(shadowRegion != null){
Draw.rect(shadowRegion, tile.drawx(), tile.drawy()); Draw.rect(shadowRegion, tile.drawx(), tile.drawy());
} }
} }

View File

@@ -1,20 +1,11 @@
package io.anuke.mindustry.world; package io.anuke.mindustry.world;
import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Rectangle;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.content.blocks.Blocks;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.game.EventType.BlockBuildEvent; import io.anuke.mindustry.game.EventType.BlockBuildEvent;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.In;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.blocks.BreakBlock;
import io.anuke.mindustry.world.blocks.BreakBlock.BreakEntity;
import io.anuke.mindustry.world.blocks.BuildBlock;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
import io.anuke.ucore.core.Events; import io.anuke.ucore.core.Events;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
@@ -26,15 +17,10 @@ public class Build {
private static final Rectangle hitrect = new Rectangle(); private static final Rectangle hitrect = new Rectangle();
/**Returns block type that was broken, or null if unsuccesful.*/ /**Returns block type that was broken, or null if unsuccesful.*/
@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks) //@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks)
public static void breakBlock(Player player, Team team, int x, int y){ public static void beginBreak(Team team, int x, int y){
if(Net.server()){ if(!validBreak(team, x, y)){
if(!validBreak(team, x, y)){ return;
return;
}
team = player.getTeam();
//throw new ValidateException(player, "An invalid block has been broken.");
} }
Tile tile = world.tile(x, y); Tile tile = world.tile(x, y);
@@ -46,59 +32,38 @@ public class Build {
Block previous = tile.block(); Block previous = tile.block();
//remote players only Block sub = Block.getByName("build" + previous.size);
if(player != null && !player.isLocal){
player.getPlaceQueue().clear();
player.getPlaceQueue().addFirst(new BuildRequest(x, y));
}
Block sub = Block.getByName("break" + previous.size); tile.setBlock(sub);
tile.<BuildEntity>entity().setDeconstruct(previous);
tile.setTeam(team);
if(previous instanceof BuildBlock){ if (previous.isMultiblock()) {
BuildEntity build = tile.entity(); int offsetx = -(previous.size - 1) / 2;
int offsety = -(previous.size - 1) / 2;
tile.setBlock(sub); for (int dx = 0; dx < previous.size; dx++) {
tile.setTeam(team); for (int dy = 0; dy < previous.size; dy++) {
int worldx = dx + offsetx + x;
BreakEntity breake = tile.entity(); int worldy = dy + offsety + y;
breake.set(build.recipe.result); if (!(worldx == x && worldy == y)) {
breake.progress = 1.0 - build.progress; Tile toplace = world.tile(worldx, worldy);
}else { if (toplace != null) {
tile.setBlock(sub); toplace.setLinked((byte) (dx + offsetx), (byte) (dy + offsety));
tile.<BreakEntity>entity().set(previous); toplace.setTeam(team);
tile.setTeam(team);
if (previous.isMultiblock()) {
int offsetx = -(previous.size - 1) / 2;
int offsety = -(previous.size - 1) / 2;
for (int dx = 0; dx < previous.size; dx++) {
for (int dy = 0; dy < previous.size; dy++) {
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if (!(worldx == x && worldy == y)) {
Tile toplace = world.tile(worldx, worldy);
if (toplace != null) {
toplace.setLinked((byte) (dx + offsetx), (byte) (dy + offsety));
toplace.setTeam(team);
}
} }
} }
} }
} }
} }
} }
/**Places a BuildBlock at this location. Call validPlace first.*/ /**Places a BuildBlock at this location.*/
@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks) //@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks)
public static void placeBlock(Player player, Team team, int x, int y, Recipe recipe, int rotation){ public static void beginPlace(Team team, int x, int y, Recipe recipe, int rotation){
if(Net.server()){ if(!validPlace(team, x, y, recipe.result, rotation)){
if(!validPlace(team, x, y, recipe.result, rotation)){ return;
return;
}
team = player.getTeam();
//throw new ValidateException(player, "An invalid block has been placed.");
} }
Tile tile = world.tile(x, y); Tile tile = world.tile(x, y);
@@ -109,51 +74,33 @@ public class Build {
Block result = recipe.result; Block result = recipe.result;
Block previous = tile.block(); Block previous = tile.block();
//remote players only
if(player != null && !player.isLocal){
player.getPlaceQueue().clear();
player.getPlaceQueue().addFirst(new BuildRequest(x, y, rotation, recipe));
}
Block sub = Block.getByName("build" + result.size); Block sub = Block.getByName("build" + result.size);
if(previous instanceof BreakBlock){ tile.setBlock(sub, rotation);
BreakEntity breake = tile.entity(); tile.<BuildEntity>entity().setConstruct(previous, recipe);
tile.setTeam(team);
tile.setBlock(sub); if (result.isMultiblock()) {
tile.setTeam(team); int offsetx = -(result.size - 1) / 2;
int offsety = -(result.size - 1) / 2;
BuildEntity build = tile.entity(); for (int dx = 0; dx < result.size; dx++) {
build.set(breake.previous, recipe); for (int dy = 0; dy < result.size; dy++) {
build.progress = 1.0 - breake.progress; int worldx = dx + offsetx + x;
}else{ int worldy = dy + offsety + y;
tile.setBlock(sub, rotation); if (!(worldx == x && worldy == y)) {
tile.<BuildEntity>entity().set(previous, recipe); Tile toplace = world.tile(worldx, worldy);
tile.setTeam(team); if (toplace != null) {
toplace.setLinked((byte) (dx + offsetx), (byte) (dy + offsety));
if (result.isMultiblock()) { toplace.setTeam(team);
int offsetx = -(result.size - 1) / 2;
int offsety = -(result.size - 1) / 2;
for (int dx = 0; dx < result.size; dx++) {
for (int dy = 0; dy < result.size; dy++) {
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if (!(worldx == x && worldy == y)) {
Tile toplace = world.tile(worldx, worldy);
if (toplace != null) {
toplace.setLinked((byte) (dx + offsetx), (byte) (dy + offsety));
toplace.setTeam(team);
}
} }
} }
} }
} }
} }
Team fteam = team;
threads.runDelay(() -> Events.fire(BlockBuildEvent.class, fteam, tile)); threads.runDelay(() -> Events.fire(BlockBuildEvent.class, team, tile));
} }
/**Returns whether a tile can be placed at this location by this team.*/ /**Returns whether a tile can be placed at this location by this team.*/
@@ -199,13 +146,17 @@ public class Build {
return true; return true;
} }
if(!type.canPlaceOn(tile)){
return false;
}
int offsetx = -(type.size - 1) / 2; int offsetx = -(type.size - 1) / 2;
int offsety = -(type.size - 1) / 2; int offsety = -(type.size - 1) / 2;
for (int dx = 0; dx < type.size; dx++) { for (int dx = 0; dx < type.size; dx++) {
for (int dy = 0; dy < type.size; dy++) { for (int dy = 0; dy < type.size; dy++) {
Tile other = world.tile(x + dx + offsetx, y + dy + offsety); Tile other = world.tile(x + dx + offsetx, y + dy + offsety);
if (other == null || (other.block() != Blocks.air && !other.block().alwaysReplace) if (other == null || (other.block() != Blocks.air && !other.block().alwaysReplace)
|| !type.canPlaceOn(other) || other.cliffs != 0 || !other.floor().placeableOn || || other.cliffs != 0 || !other.floor().placeableOn ||
(tile.floor().liquidDrop != null && !type.floating)) { (tile.floor().liquidDrop != null && !type.floating)) {
return false; return false;
} }
@@ -226,7 +177,7 @@ public class Build {
public static boolean validBreak(Team team, int x, int y) { public static boolean validBreak(Team team, int x, int y) {
Tile tile = world.tile(x, y); Tile tile = world.tile(x, y);
return tile != null && !tile.block().unbreakable && !(tile.target().block() instanceof BreakBlock) return tile != null && !tile.block().unbreakable
&& (!tile.isLinked() || !tile.getLinked().block().unbreakable) && tile.breakable() && (tile.getTeam() == Team.none || tile.getTeam() == team); && (!tile.isLinked() || !tile.getLinked().block().unbreakable) && tile.breakable() && (tile.getTeam() == Team.none || tile.getTeam() == team);
} }
} }

View File

@@ -27,8 +27,9 @@ public class Tile implements PosTrait, TargetTrait {
public static final Object tileSetLock = new Object(); public static final Object tileSetLock = new Object();
/**Block ID data.*/ /**Block ID data.*/
private byte floor, wall; private Block wall;
/**Rotation, 0-3. Also used to store offload location.*/ private Floor floor;
/**Rotation, 0-3. Also used to store offload location for routers, in which case it can be any number.*/
private byte rotation; private byte rotation;
/**Team ordinal.*/ /**Team ordinal.*/
private byte team; private byte team;
@@ -52,15 +53,15 @@ public class Tile implements PosTrait, TargetTrait {
public Tile(int x, int y, byte floor, byte wall){ public Tile(int x, int y, byte floor, byte wall){
this(x, y); this(x, y);
this.floor = floor; this.floor = (Floor) Block.getByID(floor);
this.wall = wall; this.wall = Block.getByID(wall);
changed(); changed();
} }
public Tile(int x, int y, byte floor, byte wall, byte rotation, byte team, byte elevation){ public Tile(int x, int y, byte floor, byte wall, byte rotation, byte team, byte elevation){
this(x, y); this(x, y);
this.floor = floor; this.floor =(Floor) Block.getByID(floor);
this.wall = wall; this.wall = Block.getByID(wall);
this.rotation = rotation; this.rotation = rotation;
this.elevation = elevation; this.elevation = elevation;
changed(); changed();
@@ -72,11 +73,11 @@ public class Tile implements PosTrait, TargetTrait {
} }
public byte getWallID(){ public byte getWallID(){
return wall; return (byte)wall.id;
} }
public byte getFloorID(){ public byte getFloorID(){
return floor; return (byte)floor.id;
} }
/**Return relative rotation to a coordinate. Returns -1 if the coordinate is not near this tile.*/ /**Return relative rotation to a coordinate. Returns -1 if the coordinate is not near this tile.*/
@@ -129,11 +130,11 @@ public class Tile implements PosTrait, TargetTrait {
} }
public Floor floor(){ public Floor floor(){
return (Floor)Block.getByID(getFloorID()); return floor;
} }
public Block block(){ public Block block(){
return Block.getByID(getWallID()); return wall;
} }
public Team getTeam(){ public Team getTeam(){
@@ -161,7 +162,7 @@ public class Tile implements PosTrait, TargetTrait {
public void setBlock(Block type, int rotation){ public void setBlock(Block type, int rotation){
synchronized (tileSetLock) { synchronized (tileSetLock) {
if(rotation < 0) rotation = (-rotation + 2); if(rotation < 0) rotation = (-rotation + 2);
this.wall = (byte)type.id; this.wall = type;
this.link = 0; this.link = 0;
setRotation((byte) (rotation % 4)); setRotation((byte) (rotation % 4));
changed(); changed();
@@ -170,14 +171,14 @@ public class Tile implements PosTrait, TargetTrait {
public void setBlock(Block type){ public void setBlock(Block type){
synchronized (tileSetLock) { synchronized (tileSetLock) {
this.wall = (byte)type.id; this.wall = type;
this.link = 0; this.link = 0;
changed(); changed();
} }
} }
public void setFloor(Block type){ public void setFloor(Floor type){
this.floor = (byte)type.id; this.floor = type;
} }
public void setRotation(byte rotation){ public void setRotation(byte rotation){
@@ -254,6 +255,25 @@ public class Tile implements PosTrait, TargetTrait {
return tmpArray; return tmpArray;
} }
/**Returns the list of all tiles linked to this multiblock if it were this block, or an empty array if it's not a multiblock.
* This array contains all linked tiles, including this tile itself.*/
public synchronized Array<Tile> getLinkedTilesAs(Block block, Array<Tile> tmpArray){
tmpArray.clear();
if(block.isMultiblock()){
int offsetx = -(block.size-1)/2;
int offsety = -(block.size-1)/2;
for(int dx = 0; dx < block.size; dx ++){
for(int dy = 0; dy < block.size; dy ++){
Tile other = world.tile(x + dx + offsetx, y + dy + offsety);
tmpArray.add(other);
}
}
}else{
tmpArray.add(this);
}
return tmpArray;
}
/**Returns the block the multiblock is linked to, or null if it is not linked to any block.*/ /**Returns the block the multiblock is linked to, or null if it is not linked to any block.*/
public Tile getLinked(){ public Tile getLinked(){
if(link == 0){ if(link == 0){

View File

@@ -1,203 +0,0 @@
package io.anuke.mindustry.world.blocks;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.effect.RubbleDecal;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.input.CursorType;
import io.anuke.mindustry.net.In;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.BarType;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockBar;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.graphics.Draw;
import static io.anuke.mindustry.Vars.world;
public class BreakBlock extends Block {
private static final float decaySpeedScl = 6f;
public BreakBlock(String name) {
super(name);
solidifes = true;
update = true;
size = Integer.parseInt(name.charAt(name.length()-1) + "");
health = 1;
layer = Layer.placement;
consumesTap = true;
}
@Override
public boolean isSolidFor(Tile tile) {
BreakEntity entity = tile.entity();
return entity.previous == null || entity.previous.solid;
}
@Override
public CursorType getCursor(Tile tile) {
return CursorType.hand;
}
@Override
public void tapped(Tile tile, Player player) {
CallBlocks.onBreakSelect(player, tile);
}
@Override
public void setBars(){
bars.replace(new BlockBar(BarType.health, true, tile -> (float)tile.<BreakEntity>entity().progress));
}
@Override
public void onDestroyed(Tile tile){
Effects.effect(ExplosionFx.blockExplosionSmoke, tile);
if(!tile.floor().solid && !tile.floor().isLiquid){
RubbleDecal.create(tile.drawx(), tile.drawy(), size);
}
}
@Override
public void afterDestroyed(Tile tile, TileEntity e){
BreakEntity entity = (BreakEntity)e;
if(entity.previous.synthetic()){
tile.setBlock(entity.previous);
}
}
@Override
public void draw(Tile tile){
}
@Override
public void drawLayer(Tile tile) {
BreakEntity entity = tile.entity();
Shaders.blockbuild.color = Palette.remove;
for(TextureRegion region : entity.previous.getBlockIcon()){
Shaders.blockbuild.region = region;
Shaders.blockbuild.progress = (float)(1f-entity.progress); //progress reversed
Shaders.blockbuild.apply();
Draw.rect(region, tile.drawx(), tile.drawy(), entity.previous.rotate ? tile.getRotation() * 90 : 0);
Graphics.flush();
}
}
@Override
public void drawShadow(Tile tile) {
BreakEntity entity = tile.entity();
if(entity.previous instanceof BreakBlock || entity.previous == null || entity.previous.shadowRegion == null){
return;
}
entity.previous.drawShadow(tile);
}
@Override
public void update(Tile tile) {
BreakEntity entity = tile.entity();
if(entity.progress >= 1f){
CallBlocks.onBreakFinish(tile);
}else if(entity.progress < 0f){
CallBlocks.onBreakDeath(tile);
}
}
@Override
public TileEntity getEntity() {
return new BreakEntity();
}
@Remote(called = Loc.server, in = In.blocks)
public static void onBreakDeath(Tile tile){
BreakEntity entity = tile.entity();
Team team = tile.getTeam();
tile.setBlock(entity.previous);
tile.setTeam(team);
}
@Remote(called = Loc.server, in = In.blocks)
public static void onBreakFinish(Tile tile){
if(tile.entity instanceof BreakEntity){
BreakEntity entity = tile.entity();
Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), entity.previous.size);
}
world.removeBlock(tile);
}
@Remote(called = Loc.both, targets = Loc.both, in = In.blocks, forward = true)
public static void onBreakSelect(Player player, Tile tile){
if(player == null || !(tile.entity instanceof BreakEntity)) return;
player.getPlaceQueue().clear();
player.addBuildRequest(new BuildRequest(tile.x, tile.y));
}
public class BreakEntity extends TileEntity{
private double[] accumulator;
public double progress = 0;
public Block previous;
public float breakTime;
public void addProgress(TileEntity core, Unit unit, double add){
Recipe recipe = Recipe.getByResult(previous);
if(recipe != null) {
ItemStack[] requirements = recipe.requirements;
for (int i = 0; i < requirements.length; i++) {
accumulator[i] += requirements[i].amount * add / 2f; //add scaled amount progressed to the accumulator
int amount = (int) (accumulator[i]); //get amount
if (amount > 0) { //if it's positive, add it to the core
int accepting = core.tile.block().acceptStack(requirements[i].item, amount, core.tile, unit);
core.tile.block().handleStack(requirements[i].item, amount, core.tile, unit);
accumulator[i] -= accepting;
}
}
}
progress += add;
}
public float progress(){
return (float)progress;
}
public void set(Block previous){
this.previous = previous;
if(Recipe.getByResult(previous) != null){
this.accumulator = new double[Recipe.getByResult(previous).requirements.length];
this.breakTime = Recipe.getByResult(previous).cost;
}else{
this.breakTime = 20f;
}
}
}
}

View File

@@ -7,6 +7,7 @@ import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.content.fx.Fx; import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.effect.RubbleDecal; import io.anuke.mindustry.entities.effect.RubbleDecal;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest; import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
@@ -16,6 +17,7 @@ import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders; import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.input.CursorType; import io.anuke.mindustry.input.CursorType;
import io.anuke.mindustry.net.In; import io.anuke.mindustry.net.In;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.BarType; import io.anuke.mindustry.world.BarType;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
@@ -25,16 +27,15 @@ import io.anuke.mindustry.world.meta.BlockBar;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Mathf;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import static io.anuke.mindustry.Vars.threads; import static io.anuke.mindustry.Vars.*;
public class BuildBlock extends Block { public class BuildBlock extends Block {
private static final float decaySpeedScl = 6f;
public BuildBlock(String name) { public BuildBlock(String name) {
super(name); super(name);
update = true; update = true;
@@ -48,7 +49,7 @@ public class BuildBlock extends Block {
@Override @Override
public boolean isSolidFor(Tile tile) { public boolean isSolidFor(Tile tile) {
BuildEntity entity = tile.entity(); BuildEntity entity = tile.entity();
return entity.recipe.result.solid || entity.previous.solid; return entity == null || entity.recipe == null || entity.recipe.result.solid || entity.previous.solid;
} }
@Override @Override
@@ -58,7 +59,13 @@ public class BuildBlock extends Block {
@Override @Override
public void tapped(Tile tile, Player player) { public void tapped(Tile tile, Player player) {
CallBlocks.onBuildSelect(player, tile); BuildEntity entity = tile.entity();
//if the target is constructible, begin constructing
if(entity.recipe != null){
player.clearBuilding();
player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.getRotation(), entity.recipe));
}
} }
@Override @Override
@@ -79,7 +86,7 @@ public class BuildBlock extends Block {
public void afterDestroyed(Tile tile, TileEntity e){ public void afterDestroyed(Tile tile, TileEntity e){
BuildEntity entity = (BuildEntity)e; BuildEntity entity = (BuildEntity)e;
if(entity.previous.synthetic()){ if(entity.previous != null && entity.previous.synthetic()){
tile.setBlock(entity.previous); tile.setBlock(entity.previous);
} }
} }
@@ -88,10 +95,13 @@ public class BuildBlock extends Block {
public void draw(Tile tile){ public void draw(Tile tile){
BuildEntity entity = tile.entity(); BuildEntity entity = tile.entity();
if(entity.previous.synthetic()) { //When breaking, don't draw the previous block... since it's the thing you were breaking
for (TextureRegion region : entity.previous.getBlockIcon()) { if(entity.recipe != null && entity.previous == entity.recipe.result){
Draw.rect(region, tile.drawx(), tile.drawy(), entity.recipe.result.rotate ? tile.getRotation() * 90 : 0); return;
} }
for (TextureRegion region : entity.previous.getBlockIcon()) {
Draw.rect(region, tile.drawx(), tile.drawy(), entity.previous.rotate ? tile.getRotation() * 90 : 0);
} }
} }
@@ -101,12 +111,16 @@ public class BuildBlock extends Block {
Shaders.blockbuild.color = Palette.accent; Shaders.blockbuild.color = Palette.accent;
for(TextureRegion region : entity.recipe.result.getBlockIcon()){ Block target = entity.recipe == null ? entity.previous : entity.recipe.result;
if(target == null) return;
for(TextureRegion region : target.getBlockIcon()){
Shaders.blockbuild.region = region; Shaders.blockbuild.region = region;
Shaders.blockbuild.progress = (float)entity.progress; Shaders.blockbuild.progress = (float)entity.progress;
Shaders.blockbuild.apply(); Shaders.blockbuild.apply();
Draw.rect(region, tile.drawx(), tile.drawy(), entity.recipe.result.rotate ? tile.getRotation() * 90 : 0); Draw.rect(region, tile.drawx(), tile.drawy(), target.rotate ? tile.getRotation() * 90 : 0);
Graphics.flush(); Graphics.flush();
} }
@@ -116,26 +130,16 @@ public class BuildBlock extends Block {
public void drawShadow(Tile tile) { public void drawShadow(Tile tile) {
BuildEntity entity = tile.entity(); BuildEntity entity = tile.entity();
if(entity.recipe != null && entity.recipe.result != null){ if(entity.recipe != null){
entity.recipe.result.drawShadow(tile); entity.recipe.result.drawShadow(tile);
}else if(entity.previous != null){
entity.previous.drawShadow(tile);
} }
} }
@Override @Override
public void update(Tile tile) { public void update(Tile tile) {
BuildEntity entity = tile.entity();
if(entity.progress >= 1f){
CallBlocks.onBuildFinish(tile, entity.lastBuilder);
}else if(entity.progress < 0f){
CallBlocks.onBuildDeath(tile);
}
if(!entity.updated){
entity.progress -= 1f/entity.recipe.cost/decaySpeedScl;
}
entity.updated = false;
} }
@Override @Override
@@ -144,71 +148,86 @@ public class BuildBlock extends Block {
} }
@Remote(called = Loc.server, in = In.blocks) @Remote(called = Loc.server, in = In.blocks)
public static void onBuildDeath(Tile tile){ public static void onDeconstructFinish(Tile tile, Block block){
if(tile.entity == null) return; Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), block.size);
tile.entity.damage(tile.entity.health + 1); world.removeBlock(tile);
} }
@Remote(called = Loc.server, in = In.blocks) @Remote(called = Loc.server, in = In.blocks)
public static void onBuildFinish(Tile tile, Player lastBuilder){ public static void onConstructFinish(Tile tile, Block block, int builderID, byte rotation){
if(tile.entity == null) return;
BuildEntity entity = tile.entity();
Team team = tile.getTeam(); Team team = tile.getTeam();
tile.setBlock(entity.recipe.result); tile.setBlock(block);
tile.setTeam(team); tile.setTeam(team);
Effects.effect(Fx.placeBlock, tile.drawx(), tile.drawy(), entity.recipe.result.size); tile.setRotation(rotation);
Effects.effect(Fx.placeBlock, tile.drawx(), tile.drawy(), block.size);
//last builder was this local client player, call placed() //last builder was this local client player, call placed()
if(lastBuilder != null && lastBuilder.isLocal){ if(!headless && builderID == players[0].id){
//this is run delayed, since if this is called on the server, all clients need to recieve the onBuildFinish() //this is run delayed, since if this is called on the server, all clients need to recieve the onBuildFinish()
//event first before they can recieve the placed() event modification results //event first before they can recieve the placed() event modification results
threads.runDelay(() -> tile.block().placed(tile)); threads.runDelay(() -> tile.block().placed(tile));
} }
} }
@Remote(called = Loc.both, targets = Loc.both, in = In.blocks, forward = true)
public static void onBuildSelect(Player player, Tile tile){
if(player == null || !(tile.entity instanceof BuildEntity)) return;
BuildEntity entity = tile.entity();
player.getPlaceQueue().clear();
player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.getRotation(), entity.recipe));
}
@Remote(called = Loc.server, targets = Loc.both, in = In.blocks, forward = true)
public static void onBuildDeselect(Player player){
player.getPlaceQueue().clear();
}
public class BuildEntity extends TileEntity{ public class BuildEntity extends TileEntity{
/**The recipe of the block that is being constructed.
* If there is no recipe for this block, as is the case with rocks, 'previous' is used.*/
public Recipe recipe; public Recipe recipe;
public double progress = 0; public double progress = 0;
public double lastProgress; public double lastProgress;
public double buildCost;
/**The block that used to be here.
* If a non-recipe block is being deconstructed, this is the block that is being deconstructed.*/
public Block previous; public Block previous;
public Player lastBuilder;
private double[] accumulator; private double[] accumulator;
private boolean updated;
public void addProgress(InventoryModule inventory, double amount){ public void construct(Unit builder, TileEntity core, double amount){
double maxProgress = checkRequired(inventory, amount); double maxProgress = checkRequired(core.items, amount);
for (int i = 0; i < recipe.requirements.length; i++) { for (int i = 0; i < recipe.requirements.length; i++) {
accumulator[i] += recipe.requirements[i].amount*maxProgress; //add min amount progressed to the accumulator accumulator[i] += recipe.requirements[i].amount*maxProgress; //add min amount progressed to the accumulator
} }
maxProgress = checkRequired(inventory, maxProgress); maxProgress = checkRequired(core.items, maxProgress);
progress += maxProgress; progress = Mathf.clamp(progress + maxProgress);
lastProgress = maxProgress; lastProgress = maxProgress;
updated = true;
if(progress >= 1f){
CallBlocks.onConstructFinish(tile, recipe.result, builder.getID(), tile.getRotation());
}
} }
public double checkRequired(InventoryModule inventory, double amount){ public void deconstruct(Unit builder, TileEntity core, double amount){
Recipe recipe = Recipe.getByResult(previous);
if(recipe != null) {
ItemStack[] requirements = recipe.requirements;
for (int i = 0; i < requirements.length; i++) {
accumulator[i] += requirements[i].amount * amount / 2f; //add scaled amount progressed to the accumulator
int accumulated = (int) (accumulator[i]); //get amount
if (amount > 0) { //if it's positive, add it to the core
int accepting = core.tile.block().acceptStack(requirements[i].item, accumulated, core.tile, builder);
core.tile.block().handleStack(requirements[i].item, accumulated, core.tile, builder);
accumulator[i] -= accepting;
}
}
}
progress = Mathf.clamp(progress - amount);
if(progress <= 0){
CallBlocks.onDeconstructFinish(tile, recipe == null ? previous : recipe.result);
}
}
private double checkRequired(InventoryModule inventory, double amount){
double maxProgress = amount; double maxProgress = amount;
for(int i = 0; i < recipe.requirements.length; i ++){ for(int i = 0; i < recipe.requirements.length; i ++){
@@ -237,11 +256,23 @@ public class BuildBlock extends Block {
return (float)progress; return (float)progress;
} }
public void set(Block previous, Recipe recipe){ public void setConstruct(Block previous, Recipe recipe){
updated = true;
this.recipe = recipe; this.recipe = recipe;
this.previous = previous; this.previous = previous;
this.accumulator = new double[recipe.requirements.length]; this.accumulator = new double[recipe.requirements.length];
this.buildCost = recipe.cost;
}
public void setDeconstruct(Block previous){
this.previous = previous;
this.progress = 1f;
if(Recipe.getByResult(previous) != null){
this.recipe = Recipe.getByResult(previous);
this.accumulator = new double[Recipe.getByResult(previous).requirements.length];
this.buildCost = Recipe.getByResult(previous).cost;
}else{
this.buildCost = 20f; //default no-recipe build cost is 20
}
} }
@Override @Override

View File

@@ -17,7 +17,6 @@ import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Geometry; import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.headless;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
public class Floor extends Block{ public class Floor extends Block{
@@ -141,7 +140,7 @@ public class Floor extends Block{
Draw.rect(variantRegions[Mathf.randomSeed(tile.id(), 0, Math.max(0, variantRegions.length-1))], tile.worldx(), tile.worldy()); Draw.rect(variantRegions[Mathf.randomSeed(tile.id(), 0, Math.max(0, variantRegions.length-1))], tile.worldx(), tile.worldy());
if(tile.cliffs != 0){ if(tile.cliffs != 0 && cliffRegions != null){
for(int i = 0; i < 4; i ++){ for(int i = 0; i < 4; i ++){
if((tile.cliffs & (1 << i*2)) != 0) { if((tile.cliffs & (1 << i*2)) != 0) {
Draw.colorl(i > 1 ? 0.6f : 1f); Draw.colorl(i > 1 ? 0.6f : 1f);

View File

@@ -29,9 +29,9 @@ public class BurstTurret extends ItemTurret {
entity.recoil = recoil; entity.recoil = recoil;
tr.trns(entity.rotation, size * tilesize / 2, Mathf.range(xRand)); tr.trns(entity.rotation, size * tilesize / 2, Mathf.range(xRand));
useAmmo(tile);
bullet(tile, ammo.bullet, entity.rotation + Mathf.range(inaccuracy)); bullet(tile, ammo.bullet, entity.rotation + Mathf.range(inaccuracy));
effects(tile); effects(tile);
useAmmo(tile);
}); });
} }
} }

View File

@@ -41,6 +41,13 @@ public class ItemTurret extends CooledTurret {
return Math.min((int)((maxAmmo - entity.totalAmmo) / ammoMap.get(item).quantityMultiplier), amount); return Math.min((int)((maxAmmo - entity.totalAmmo) / ammoMap.get(item).quantityMultiplier), amount);
} }
@Override
public void handleStack(Item item, int amount, Tile tile, Unit source){
for (int i = 0; i < amount; i++) {
handleItem(item, tile, null);
}
}
//currently can't remove items from turrets. //currently can't remove items from turrets.
@Override @Override
public int removeStack(Tile tile, Item item, int amount) { public int removeStack(Tile tile, Item item, int amount) {

View File

@@ -110,6 +110,7 @@ public abstract class Turret extends Block{
stats.add(BlockStat.inaccuracy, (int)inaccuracy, StatUnit.degrees); stats.add(BlockStat.inaccuracy, (int)inaccuracy, StatUnit.degrees);
stats.add(BlockStat.reload, 60f/reload, StatUnit.seconds); stats.add(BlockStat.reload, 60f/reload, StatUnit.seconds);
stats.add(BlockStat.shots, shots, StatUnit.none); stats.add(BlockStat.shots, shots, StatUnit.none);
stats.add(BlockStat.targetsAir, targetAir);
} }
@Override @Override

View File

@@ -10,8 +10,8 @@ import io.anuke.mindustry.content.fx.ShootFx;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.bullet.Bullet; import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.effect.ItemDrop;
import io.anuke.mindustry.gen.CallBlocks; import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Layer; import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.net.In; import io.anuke.mindustry.net.In;
@@ -241,7 +241,7 @@ public class MassDriver extends Block {
if(amountDropped > 0){ if(amountDropped > 0){
float angle = Mathf.range(180f); float angle = Mathf.range(180f);
float vs = Mathf.random(0f, 4f); float vs = Mathf.random(0f, 4f);
CallEntity.createItemDrop(Item.getByID(i), amountDropped, bullet.x, bullet.y, Angles.trnsx(angle, vs), Angles.trnsy(angle, vs)); ItemDrop.create(Item.getByID(i), amountDropped, bullet.x, bullet.y, Angles.trnsx(angle, vs), Angles.trnsy(angle, vs));
} }
} }

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.world.blocks.power; package io.anuke.mindustry.world.blocks.power;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import io.anuke.mindustry.content.fx.BlockFx; import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
@@ -8,6 +9,7 @@ import io.anuke.mindustry.world.BarType;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockBar; import io.anuke.mindustry.world.meta.BlockBar;
import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.BlockStat;
import io.anuke.mindustry.world.meta.StatUnit;
import io.anuke.mindustry.world.meta.values.ItemFilterValue; import io.anuke.mindustry.world.meta.values.ItemFilterValue;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Effects.Effect; import io.anuke.ucore.core.Effects.Effect;
@@ -28,6 +30,7 @@ public abstract class ItemGenerator extends PowerGenerator {
protected Effect generateEffect = BlockFx.generatespark, explodeEffect = protected Effect generateEffect = BlockFx.generatespark, explodeEffect =
BlockFx.generatespark; BlockFx.generatespark;
protected Color heatColor = Color.valueOf("ff9b59"); protected Color heatColor = Color.valueOf("ff9b59");
protected TextureRegion topRegion;
public ItemGenerator(String name) { public ItemGenerator(String name) {
super(name); super(name);
@@ -35,11 +38,18 @@ public abstract class ItemGenerator extends PowerGenerator {
hasItems = true; hasItems = true;
} }
@Override
public void load() {
super.load();
topRegion = Draw.region(name + "-top");
}
@Override @Override
public void setStats() { public void setStats() {
super.setStats(); super.setStats();
stats.add(BlockStat.inputItems, new ItemFilterValue(item -> getItemEfficiency(item) >= minItemEfficiency)); stats.add(BlockStat.inputItems, new ItemFilterValue(item -> getItemEfficiency(item) >= minItemEfficiency));
stats.add(BlockStat.maxPowerGeneration, powerOutput * 60f, StatUnit.powerSecond);
} }
@Override @Override
@@ -59,7 +69,7 @@ public abstract class ItemGenerator extends PowerGenerator {
float alpha = (entity.items.totalItems() > 0 ? 1f : Mathf.clamp(entity.generateTime)); float alpha = (entity.items.totalItems() > 0 ? 1f : Mathf.clamp(entity.generateTime));
alpha = alpha * 0.7f + Mathf.absin(Timers.time(), 12f, 0.3f) * alpha; alpha = alpha * 0.7f + Mathf.absin(Timers.time(), 12f, 0.3f) * alpha;
Draw.alpha(alpha); Draw.alpha(alpha);
Draw.rect(name + "-top", tile.worldx(), tile.worldy()); Draw.rect(topRegion, tile.drawx(), tile.drawy());
Draw.reset(); Draw.reset();
} }
} }

View File

@@ -13,6 +13,7 @@ import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockBar; import io.anuke.mindustry.world.meta.BlockBar;
import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.BlockStat;
import io.anuke.mindustry.world.meta.StatUnit; import io.anuke.mindustry.world.meta.StatUnit;
import io.anuke.mindustry.world.meta.values.LiquidFilterValue;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
@@ -38,6 +39,7 @@ public class NuclearReactor extends PowerGenerator {
protected float heating = 0.009f; //heating per frame protected float heating = 0.009f; //heating per frame
protected float coolantPower = 0.015f; //how much heat decreases per coolant unit protected float coolantPower = 0.015f; //how much heat decreases per coolant unit
protected float smokeThreshold = 0.3f; //threshold at which block starts smoking protected float smokeThreshold = 0.3f; //threshold at which block starts smoking
protected float maxLiquidUse = 1f; //max liquid use per frame
protected int explosionRadius = 19; protected int explosionRadius = 19;
protected int explosionDamage = 135; protected int explosionDamage = 135;
protected float flashThreshold = 0.46f; //heat threshold at which the lights start flashing protected float flashThreshold = 0.46f; //heat threshold at which the lights start flashing
@@ -49,6 +51,7 @@ public class NuclearReactor extends PowerGenerator {
liquidCapacity = 50; liquidCapacity = 50;
powerCapacity = 80f; powerCapacity = 80f;
hasItems = true; hasItems = true;
hasLiquids = true;
} }
@Override @Override
@@ -62,6 +65,7 @@ public class NuclearReactor extends PowerGenerator {
public void setStats(){ public void setStats(){
super.setStats(); super.setStats();
stats.add(BlockStat.inputItem, generateItem); stats.add(BlockStat.inputItem, generateItem);
stats.add(BlockStat.inputLiquid, new LiquidFilterValue(liquid -> liquid.temperature <= 0.5f));
stats.add(BlockStat.maxPowerGeneration, powerMultiplier*60f, StatUnit.powerSecond); stats.add(BlockStat.maxPowerGeneration, powerMultiplier*60f, StatUnit.powerSecond);
} }
@@ -85,12 +89,12 @@ public class NuclearReactor extends PowerGenerator {
if(entity.liquids.liquid.temperature <= 0.5f){ //is coolant if(entity.liquids.liquid.temperature <= 0.5f){ //is coolant
float pow = coolantPower * entity.liquids.liquid.heatCapacity; //heat depleted per unit of liquid float pow = coolantPower * entity.liquids.liquid.heatCapacity; //heat depleted per unit of liquid
float maxUsed = Math.min(entity.liquids.amount, entity.heat / pow); //max that can be cooled in terms of liquid float maxUsed = Math.min(Math.min(entity.liquids.amount, entity.heat / pow), maxLiquidUse * Timers.delta()); //max that can be cooled in terms of liquid
entity.heat -= maxUsed * pow; entity.heat -= maxUsed * pow;
entity.liquids.amount -= maxUsed; entity.liquids.amount -= maxUsed;
}else{ //is heater }else{ //is heater
float heat = coolantPower * entity.liquids.liquid.heatCapacity / 4f; //heat created per unit of liquid float heat = coolantPower * entity.liquids.liquid.heatCapacity / 4f; //heat created per unit of liquid
float maxUsed = Math.min(entity.liquids.amount, (1f - entity.heat) / heat); //max liquid used float maxUsed = Math.min(Math.min(entity.liquids.amount, (1f - entity.heat) / heat), maxLiquidUse * Timers.delta()); //max liquid used
entity.heat += maxUsed * heat; entity.heat += maxUsed * heat;
entity.liquids.amount -= maxUsed; entity.liquids.amount -= maxUsed;
} }

View File

@@ -51,7 +51,12 @@ public class PowerDistributor extends PowerBlock {
} }
protected boolean shouldDistribute(Tile tile, Tile other) { protected boolean shouldDistribute(Tile tile, Tile other) {
return !(other.block() instanceof PowerGenerator) || other.entity.power.amount / other.block().powerCapacity < tile.entity.power.amount / powerCapacity; other = other.target();
//only generators can distribute to other generators
return (!(other.block() instanceof PowerGenerator) || tile.block() instanceof PowerGenerator)
&& other.entity != null
&& other.block().hasPower
&& other.entity.power.amount / other.block().powerCapacity < tile.entity.power.amount / powerCapacity;
} }
@Override @Override

View File

@@ -129,14 +129,14 @@ public class PowerNode extends PowerBlock{
Tile link = world.tile(x, y); Tile link = world.tile(x, y);
if(link != null) link = link.target(); if(link != null) link = link.target();
if(link != tile && linkValid(tile, link)){ if(link != tile && linkValid(tile, link, false)){
boolean linked = linked(tile, link); boolean linked = linked(tile, link);
Draw.color(linked ? Palette.place : Palette.breakInvalid); Draw.color(linked ? Palette.place : Palette.breakInvalid);
Lines.square(link.drawx(), link.drawy(), Lines.square(link.drawx(), link.drawy(),
link.block().size * tilesize / 2f + 1f + (linked ? 0f : Mathf.absin(Timers.time(), 4f, 1f))); link.block().size * tilesize / 2f + 1f + (linked ? 0f : Mathf.absin(Timers.time(), 4f, 1f)));
if(entity.links.size >= maxNodes && !linked){ if((entity.links.size >= maxNodes || (link.block() instanceof PowerNode && ((DistributorEntity)link.entity).links.size >= ((PowerNode)link.block()).maxNodes)) && !linked){
Draw.color(); Draw.color();
Draw.rect("cross-" + link.block().size, link.drawx(), link.drawy()); Draw.rect("cross-" + link.block().size, link.drawx(), link.drawy());
} }
@@ -190,11 +190,13 @@ public class PowerNode extends PowerBlock{
} }
protected boolean shouldDistribute(Tile tile, Tile other) { protected boolean shouldDistribute(Tile tile, Tile other) {
return other.entity.power.amount / other.block().powerCapacity <= tile.entity.power.amount / powerCapacity; return other.entity.power.amount / other.block().powerCapacity <= tile.entity.power.amount / powerCapacity &&
!(other.block() instanceof PowerGenerator); //do not distribute to power generators
} }
protected boolean shouldLeechPower(Tile tile, Tile other){ protected boolean shouldLeechPower(Tile tile, Tile other){
return !(other.block() instanceof PowerNode) return !(other.block() instanceof PowerNode)
&& other.block() instanceof PowerDistributor //only suck power from batteries and power generators
&& other.entity.power.amount / other.block().powerCapacity > tile.entity.power.amount / powerCapacity; && other.entity.power.amount / other.block().powerCapacity > tile.entity.power.amount / powerCapacity;
} }
@@ -242,6 +244,10 @@ public class PowerNode extends PowerBlock{
} }
protected boolean linkValid(Tile tile, Tile link){ protected boolean linkValid(Tile tile, Tile link){
return linkValid(tile, link, true);
}
protected boolean linkValid(Tile tile, Tile link, boolean checkMaxNodes){
if(!(tile != link && link != null && link.block().hasPower)) return false; if(!(tile != link && link != null && link.block().hasPower)) return false;
if(link.block() instanceof PowerNode){ if(link.block() instanceof PowerNode){
@@ -250,7 +256,7 @@ public class PowerNode extends PowerBlock{
return Vector2.dst(tile.drawx(), tile.drawy(), link.drawx(), link.drawy()) <= Math.max(laserRange * tilesize, return Vector2.dst(tile.drawx(), tile.drawy(), link.drawx(), link.drawy()) <= Math.max(laserRange * tilesize,
((PowerNode)link.block()).laserRange * tilesize) - tilesize/2f ((PowerNode)link.block()).laserRange * tilesize) - tilesize/2f
+ (link.block().size-1)*tilesize/2f + (tile.block().size-1)*tilesize/2f && + (link.block().size-1)*tilesize/2f + (tile.block().size-1)*tilesize/2f &&
(oe.links.size < ((PowerNode)link.block()).maxNodes || oe.links.contains(tile.packedPosition())); (!checkMaxNodes || (oe.links.size < ((PowerNode)link.block()).maxNodes || oe.links.contains(tile.packedPosition())));
}else{ }else{
return Vector2.dst(tile.drawx(), tile.drawy(), link.drawx(), link.drawy()) return Vector2.dst(tile.drawx(), tile.drawy(), link.drawx(), link.drawy())
<= laserRange * tilesize - tilesize/2f + (link.block().size-1)*tilesize; <= laserRange * tilesize - tilesize/2f + (link.block().size-1)*tilesize;
@@ -276,8 +282,9 @@ public class PowerNode extends PowerBlock{
return new DistributorEntity(); return new DistributorEntity();
} }
@Remote(targets = Loc.both, called = Loc.both, in = In.blocks, forward = true) @Remote(targets = Loc.both, called = Loc.server, in = In.blocks, forward = true)
public static void linkPowerDistributors(Player player, Tile tile, Tile other){ public static void linkPowerDistributors(Player player, Tile tile, Tile other){
DistributorEntity entity = tile.entity(); DistributorEntity entity = tile.entity();
if(!entity.links.contains(other.packedPosition())){ if(!entity.links.contains(other.packedPosition())){

View File

@@ -43,8 +43,8 @@ public class Drill extends Block{
protected Liquid inputLiquid = Liquids.water; protected Liquid inputLiquid = Liquids.water;
/**Whether the liquid is required to drill. If false, then it will be used as a speed booster.*/ /**Whether the liquid is required to drill. If false, then it will be used as a speed booster.*/
protected boolean liquidRequired = false; protected boolean liquidRequired = false;
/**How many times faster the drill will progress when booster by liquid.*/ /**How many times faster the drill will progress when boosted by liquid.*/
protected float liquidBoostIntensity = 1.3f; protected float liquidBoostIntensity = 1.6f;
/**Speed at which the drill speeds up.*/ /**Speed at which the drill speeds up.*/
protected float warmupSpeed = 0.02f; protected float warmupSpeed = 0.02f;
@@ -224,7 +224,7 @@ public class Drill extends Block{
@Override @Override
public boolean canPlaceOn(Tile tile) { public boolean canPlaceOn(Tile tile) {
if(isMultiblock()){ if(isMultiblock()){
for(Tile other : tile.getLinkedTiles(drawTiles)){ for(Tile other : tile.getLinkedTilesAs(this, drawTiles)){
if(isValid(other)){ if(isValid(other)){
return true; return true;
} }

View File

@@ -4,9 +4,9 @@ import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
public class PlasteelCompressor extends GenericCrafter { public class PlastaniumCompressor extends GenericCrafter {
public PlasteelCompressor(String name) { public PlastaniumCompressor(String name) {
super(name); super(name);
} }

View File

@@ -63,7 +63,7 @@ public class Pump extends LiquidBlock{
public boolean canPlaceOn(Tile tile) { public boolean canPlaceOn(Tile tile) {
if(isMultiblock()){ if(isMultiblock()){
Liquid last = null; Liquid last = null;
for(Tile other : tile.getLinkedTiles(drawTiles)){ for(Tile other : tile.getLinkedTilesAs(this, drawTiles)){
//can't place pump on block with multiple liquids //can't place pump on block with multiple liquids
if(last != null && other.floor().liquidDrop != last){ if(last != null && other.floor().liquidDrop != last){
return false; return false;

View File

@@ -95,7 +95,7 @@ public class SolidPump extends Pump {
@Override @Override
public boolean canPlaceOn(Tile tile) { public boolean canPlaceOn(Tile tile) {
if(isMultiblock()){ if(isMultiblock()){
for(Tile other : tile.getLinkedTiles(drawTiles)){ for(Tile other : tile.getLinkedTilesAs(this, drawTiles)){
if(isValid(other)){ if(isValid(other)){
return true; return true;
} }

View File

@@ -11,6 +11,7 @@ import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.traits.SpawnerTrait;
import io.anuke.mindustry.entities.units.BaseUnit; import io.anuke.mindustry.entities.units.BaseUnit;
import io.anuke.mindustry.entities.units.UnitType; import io.anuke.mindustry.entities.units.UnitType;
import io.anuke.mindustry.gen.CallBlocks; import io.anuke.mindustry.gen.CallBlocks;
@@ -21,6 +22,7 @@ import io.anuke.mindustry.net.In;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemType; import io.anuke.mindustry.type.ItemType;
import io.anuke.mindustry.world.BarType;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockFlag; import io.anuke.mindustry.world.meta.BlockFlag;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
@@ -64,6 +66,13 @@ public class CoreBlock extends StorageBlock {
flags = EnumSet.of(BlockFlag.resupplyPoint, BlockFlag.target); flags = EnumSet.of(BlockFlag.resupplyPoint, BlockFlag.target);
} }
@Override
public void setBars() {
super.setBars();
bars.remove(BarType.inventory);
}
@Override @Override
public void load() { public void load() {
super.load(); super.load();
@@ -132,7 +141,7 @@ public class CoreBlock extends StorageBlock {
@Override @Override
public boolean acceptItem(Item item, Tile tile, Tile source) { public boolean acceptItem(Item item, Tile tile, Tile source) {
return tile.entity.items.items[item.id]< itemCapacity && item.type == ItemType.material; return tile.entity.items.items[item.id] < itemCapacity && item.type == ItemType.material;
} }
@Override @Override
@@ -192,14 +201,13 @@ public class CoreBlock extends StorageBlock {
} }
} }
if(!found) { if(!found){
BaseUnit unit = droneType.create(tile.getTeam()); BaseUnit unit = droneType.create(tile.getTeam());
entity.droneID = unit.id; unit.setSpawner(tile);
unit.setDead(true); unit.setDead(true);
unit.setGroup(unitGroups[unit.getTeam().ordinal()]); unit.add();
CallBlocks.onCoreUnitSet(tile, unit);
unit.setGroup(null); entity.droneID = unit.id;
} }
} }
@@ -210,7 +218,7 @@ public class CoreBlock extends StorageBlock {
rect.setSize(supplyRadius*2).setCenter(tile.drawx(), tile.drawy()); rect.setSize(supplyRadius*2).setCenter(tile.drawx(), tile.drawy());
Units.getNearby(tile.getTeam(), rect, unit -> { Units.getNearby(tile.getTeam(), rect, unit -> {
if(unit.isDead() || unit.distanceTo(tile.drawx(), tile.drawy()) > supplyRadius) return; if(unit.isDead() || unit.distanceTo(tile.drawx(), tile.drawy()) > supplyRadius || unit.getGroup() == null) return;
for(int i = 0; i < tile.entity.items.items.length; i ++){ for(int i = 0; i < tile.entity.items.items.length; i ++){
Item item = Item.getByID(i); Item item = Item.getByID(i);
@@ -243,18 +251,17 @@ public class CoreBlock extends StorageBlock {
entity.currentUnit.rotation = 90f; entity.currentUnit.rotation = 90f;
entity.currentUnit.setNet(tile.drawx(), tile.drawy()); entity.currentUnit.setNet(tile.drawx(), tile.drawy());
entity.currentUnit.add(); entity.currentUnit.add();
if(entity.currentUnit instanceof Player){
((Player) entity.currentUnit).baseRotation = 90f;
}
entity.currentUnit = null; entity.currentUnit = null;
} }
@Remote(called = Loc.server, in = In.blocks)
public static void setCoreSolid(Tile tile, boolean solid){
CoreEntity entity = tile.entity();
entity.solid = solid;
}
/*
@Remote(called = Loc.server, in = In.blocks) @Remote(called = Loc.server, in = In.blocks)
public static void onCoreUnitSet(Tile tile, Unit player){ public static void onCoreUnitSet(Tile tile, Unit player){
if(player == null) return;
CoreEntity entity = tile.entity(); CoreEntity entity = tile.entity();
entity.currentUnit = player; entity.currentUnit = player;
entity.progress = 0f; entity.progress = 0f;
@@ -264,15 +271,9 @@ public class CoreBlock extends StorageBlock {
((Player) player).setRespawning(true); ((Player) player).setRespawning(true);
} }
} }
*/
@Remote(called = Loc.server, in = In.blocks) public class CoreEntity extends TileEntity implements SpawnerTrait{
public static void setCoreSolid(Tile tile, boolean solid){ public Unit currentUnit;
CoreEntity entity = tile.entity();
entity.solid = solid;
}
public class CoreEntity extends TileEntity{
Unit currentUnit;
int droneID = -1; int droneID = -1;
boolean solid = true; boolean solid = true;
float warmup; float warmup;
@@ -280,12 +281,20 @@ public class CoreBlock extends StorageBlock {
float time; float time;
float heat; float heat;
public void trySetPlayer(Player player){ @Override
public void updateSpawning(Unit unit) {
if(currentUnit == null){ if(currentUnit == null){
CallBlocks.onCoreUnitSet(tile, player); currentUnit = unit;
progress = 0f;
unit.set(tile.drawx(), tile.drawy());
} }
} }
@Override
public float getSpawnProgress() {
return progress;
}
@Override @Override
public void write(DataOutputStream stream) throws IOException { public void write(DataOutputStream stream) throws IOException {
stream.writeBoolean(solid); stream.writeBoolean(solid);

View File

@@ -8,7 +8,9 @@ import io.anuke.mindustry.content.Mechs;
import io.anuke.mindustry.content.fx.Fx; import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.traits.SpawnerTrait;
import io.anuke.mindustry.gen.CallBlocks; import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders; import io.anuke.mindustry.graphics.Shaders;
@@ -52,7 +54,7 @@ public class MechFactory extends Block{
public void tapped(Tile tile, Player player) { public void tapped(Tile tile, Player player) {
if(checkValidTap(tile, player)){ if(checkValidTap(tile, player)){
CallBlocks.onMechFactoryBegin(player, tile); CallBlocks.onMechFactoryTap(player, tile);
}else if(player.isLocal && mobile){ }else if(player.isLocal && mobile){
player.moveTarget = tile.entity; player.moveTarget = tile.entity;
} }
@@ -115,7 +117,7 @@ public class MechFactory extends Block{
entity.heat = Mathf.lerpDelta(entity.heat, 1f, 0.1f); entity.heat = Mathf.lerpDelta(entity.heat, 1f, 0.1f);
entity.progress += 1f / buildTime; entity.progress += 1f / buildTime;
entity.time += entity.heat; entity.time += 0.5f;
if(entity.progress >= 1f){ if(entity.progress >= 1f){
CallBlocks.onMechFactoryDone(tile); CallBlocks.onMechFactoryDone(tile);
@@ -134,19 +136,12 @@ public class MechFactory extends Block{
return new MechFactoryEntity(); return new MechFactoryEntity();
} }
@Remote(targets = Loc.both, called = Loc.server, in = In.blocks, forward = true) @Remote(targets = Loc.both, called = Loc.server, in = In.blocks)
public static void onMechFactoryBegin(Player player, Tile tile){ public static void onMechFactoryTap(Player player, Tile tile){
if(!checkValidTap(tile, player)) return; if(!checkValidTap(tile, player)) return;
MechFactoryEntity entity = tile.entity(); MechFactoryEntity entity = tile.entity();
entity.progress = 0f; player.beginRespawning(entity);
entity.player = player;
player.rotation = 90f;
player.baseRotation = 90f;
player.set(entity.x, entity.y);
player.setDead(true);
player.setRespawning(true);
} }
@Remote(called = Loc.server, in = In.blocks) @Remote(called = Loc.server, in = In.blocks)
@@ -154,6 +149,9 @@ public class MechFactory extends Block{
MechFactoryEntity entity = tile.entity(); MechFactoryEntity entity = tile.entity();
Effects.effect(Fx.spawn, entity); Effects.effect(Fx.spawn, entity);
if(entity.player == null) return;
Mech result = ((MechFactory)tile.block()).mech; Mech result = ((MechFactory)tile.block()).mech;
if(entity.player.mech == result){ if(entity.player.mech == result){
@@ -176,12 +174,32 @@ public class MechFactory extends Block{
Math.abs(player.y - tile.drawy()) <= tile.block().size * tilesize / 2f && entity.player == null; Math.abs(player.y - tile.drawy()) <= tile.block().size * tilesize / 2f && entity.player == null;
} }
public class MechFactoryEntity extends TileEntity{ public class MechFactoryEntity extends TileEntity implements SpawnerTrait{
public Player player; Player player;
public float progress; float progress;
public float time; float time;
public float heat; float heat;
public boolean open; boolean open;
@Override
public void updateSpawning(Unit unit) {
if(!(unit instanceof Player)) throw new IllegalArgumentException("Mech factories only accept player respawners.");
if(player == null){
progress = 0f;
player = (Player)unit;
player.rotation = 90f;
player.baseRotation = 90f;
player.set(x, y);
player.beginRespawning(this);
}
}
@Override
public float getSpawnProgress() {
return progress;
}
@Override @Override
public void write(DataOutputStream stream) throws IOException { public void write(DataOutputStream stream) throws IOException {

View File

@@ -7,7 +7,9 @@ import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.fx.Fx; import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.traits.SpawnerTrait;
import io.anuke.mindustry.gen.CallBlocks; import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders; import io.anuke.mindustry.graphics.Shaders;
@@ -28,6 +30,7 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
//TODO re-implement properly
public class Reconstructor extends Block{ public class Reconstructor extends Block{
protected float departTime = 30f; protected float departTime = 30f;
protected float arriveTime = 40f; protected float arriveTime = 40f;
@@ -117,9 +120,9 @@ public class Reconstructor extends Block{
if(entity.current != null){ if(entity.current != null){
float progress = entity.departing ? entity.updateTime : (1f - entity.updateTime); float progress = entity.departing ? entity.updateTime : (1f - entity.updateTime);
Player player = entity.current; //Player player = entity.current;
TextureRegion region = player.mech.iconRegion; TextureRegion region = entity.current.getIconRegion();
Shaders.build.region = region; Shaders.build.region = region;
Shaders.build.progress = progress; Shaders.build.progress = progress;
@@ -157,7 +160,7 @@ public class Reconstructor extends Block{
if(entity.departing){ if(entity.departing){
//force respawn if there's suddenly nothing to link to //force respawn if there's suddenly nothing to link to
if(!validLink(tile, entity.link)){ if(!validLink(tile, entity.link)){
entity.current.setRespawning(false); //entity.current.setRespawning(false);
return; return;
} }
@@ -168,7 +171,7 @@ public class Reconstructor extends Block{
//no power? death. //no power? death.
if(other.power.amount < powerPerTeleport){ if(other.power.amount < powerPerTeleport){
entity.current.setDead(true); entity.current.setDead(true);
entity.current.setRespawning(false); //entity.current.setRespawning(false);
entity.current = null; entity.current = null;
return; return;
} }
@@ -263,8 +266,8 @@ public class Reconstructor extends Block{
player.rotation = 90f; player.rotation = 90f;
player.baseRotation = 90f; player.baseRotation = 90f;
player.setDead(true); player.setDead(true);
player.setRespawning(true); // player.setRespawning(true);
player.setRespawning(); //player.setRespawning();
} }
@Remote(targets = Loc.both, called = Loc.server, in = In.blocks, forward = true) @Remote(targets = Loc.both, called = Loc.server, in = In.blocks, forward = true)
@@ -300,12 +303,22 @@ public class Reconstructor extends Block{
}); });
} }
public class ReconstructorEntity extends TileEntity{ public class ReconstructorEntity extends TileEntity implements SpawnerTrait{
public Player current; Unit current;
public float updateTime; float updateTime;
public float time; float time;
public int link; int link;
public boolean solid = true, departing; boolean solid = true, departing;
@Override
public void updateSpawning(Unit unit) {
}
@Override
public float getSpawnProgress() {
return 0;
}
@Override @Override
public void write(DataOutputStream stream) throws IOException { public void write(DataOutputStream stream) throws IOException {

Some files were not shown because too many files have changed in this diff Show More