mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-07-21 13:17:39 +07:00
Block rotation / Mod loading improvements
This commit is contained in:
@ -627,6 +627,7 @@ keybind.chat.name = Chat
|
||||
keybind.player_list.name = Player list
|
||||
keybind.console.name = Console
|
||||
keybind.rotate.name = Rotate
|
||||
keybind.rotateplaced.name = Rotate Placed (Hold)
|
||||
keybind.toggle_menus.name = Toggle menus
|
||||
keybind.chat_history_prev.name = Chat history prev
|
||||
keybind.chat_history_next.name = Chat history next
|
||||
|
@ -2,8 +2,7 @@ package io.anuke.mindustry.graphics;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.Draw;
|
||||
import io.anuke.arc.graphics.g2d.Lines;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Rectangle;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
@ -14,7 +13,7 @@ import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.entities.Units;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.input.InputHandler;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
@ -112,6 +111,13 @@ public class OverlayRenderer{
|
||||
|
||||
if(tile != null && tile.block() != Blocks.air && tile.getTeam() == player.getTeam()){
|
||||
tile.block().drawSelect(tile);
|
||||
|
||||
if(Core.input.keyDown(Binding.rotateplaced) && tile.block().rotate){
|
||||
control.input.drawArrow(tile.block(), tile.x, tile.y, tile.rotation(), true);
|
||||
Draw.color(Pal.accent, 0.3f + Mathf.absin(4f, 0.2f));
|
||||
Fill.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize/2f);
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ public enum Binding implements KeyBind{
|
||||
deselect(KeyCode.MOUSE_RIGHT),
|
||||
break_block(KeyCode.MOUSE_RIGHT),
|
||||
rotate(new Axis(KeyCode.SCROLL)),
|
||||
rotateplaced(KeyCode.R),
|
||||
diagonal_placement(KeyCode.CONTROL_LEFT),
|
||||
pick(KeyCode.MOUSE_MIDDLE),
|
||||
dash(KeyCode.SHIFT_LEFT),
|
||||
|
@ -11,9 +11,9 @@ import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.core.GameState.*;
|
||||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.PlaceUtils.*;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import static io.anuke.arc.Core.scene;
|
||||
@ -189,6 +189,10 @@ public class DesktopInput extends InputHandler{
|
||||
if(canTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y)){
|
||||
cursorType = ui.unloadCursor;
|
||||
}
|
||||
|
||||
if(!isPlacing() && Math.abs(Core.input.axisTap(Binding.rotate)) > 0 && Core.input.keyDown(Binding.rotateplaced) && cursor.block().rotate){
|
||||
Call.rotateBlock(player, cursor, Core.input.axisTap(Binding.rotate) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Core.scene.hasMouse()){
|
||||
|
@ -57,6 +57,20 @@ public abstract class InputHandler implements InputProcessor{
|
||||
player.clearItem();
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.server, forward = true, unreliable = true)
|
||||
public static void rotateBlock(Player player, Tile tile, boolean direction){
|
||||
if(net.server() && !Units.canInteract(player, tile)){
|
||||
throw new ValidateException(player, "Player cannot drop an item.");
|
||||
}
|
||||
|
||||
tile.rotation(Mathf.mod(tile.rotation() + Mathf.sign(direction), 4));
|
||||
|
||||
if(tile.entity != null){
|
||||
tile.entity.updateProximity();
|
||||
tile.entity.noSleep();
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, forward = true, called = Loc.server)
|
||||
public static void transferInventory(Player player, Tile tile){
|
||||
if(player == null || player.timer == null || !player.timer.get(Player.timerTransfer, 40)) return;
|
||||
@ -352,15 +366,19 @@ public abstract class InputHandler implements InputProcessor{
|
||||
player.addBuildRequest(new BuildRequest(tile.x, tile.y));
|
||||
}
|
||||
|
||||
void drawArrow(Block block, int x, int y, int rotation){
|
||||
Draw.color(!validPlace(x, y, block, rotation) ? Pal.removeBack : Pal.accentBack);
|
||||
public void drawArrow(Block block, int x, int y, int rotation){
|
||||
drawArrow(block, x, y, rotation, validPlace(x, y, block, rotation));
|
||||
}
|
||||
|
||||
public void drawArrow(Block block, int x, int y, int rotation, boolean valid){
|
||||
Draw.color(!valid ? Pal.removeBack : Pal.accentBack);
|
||||
Draw.rect(Core.atlas.find("place-arrow"),
|
||||
x * tilesize + block.offset(),
|
||||
y * tilesize + block.offset() - 1,
|
||||
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
|
||||
Core.atlas.find("place-arrow").getHeight() * Draw.scl, rotation * 90 - 90);
|
||||
|
||||
Draw.color(!validPlace(x, y, block, rotation) ? Pal.remove : Pal.accent);
|
||||
Draw.color(!valid ? Pal.remove : Pal.accent);
|
||||
Draw.rect(Core.atlas.find("place-arrow"),
|
||||
x * tilesize + block.offset(),
|
||||
y * tilesize + block.offset(),
|
||||
|
@ -1,14 +1,15 @@
|
||||
package io.anuke.mindustry.mod;
|
||||
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.util.ArcAnnotate.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.reflect.*;
|
||||
import io.anuke.arc.util.serialization.*;
|
||||
import io.anuke.arc.util.serialization.Json.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.content.*;
|
||||
import io.anuke.mindustry.entities.Effects.*;
|
||||
import io.anuke.mindustry.entities.bullet.*;
|
||||
import io.anuke.mindustry.entities.type.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
@ -19,24 +20,24 @@ import io.anuke.mindustry.world.*;
|
||||
public class ContentParser{
|
||||
private static final boolean ignoreUnknownFields = true;
|
||||
private ObjectMap<Class<?>, ContentType> contentTypes = new ObjectMap<>();
|
||||
private ObjectMap<Class<?>, FieldParser> classParsers = new ObjectMap<Class<?>, FieldParser>(){{
|
||||
put(BulletType.class, (type, data) -> field(Bullets.class, data));
|
||||
put(Effect.class, (type, data) -> field(Fx.class, data));
|
||||
}};
|
||||
|
||||
private Json parser = new Json(){
|
||||
public <T> T readValue(Class<T> type, Class elementType, JsonValue jsonData){
|
||||
try{
|
||||
if(type == BulletType.class){
|
||||
BulletType b = (BulletType)Bullets.class.getField(jsonData.asString()).get(null);
|
||||
if(b == null) throw new IllegalArgumentException("Bullet type not found: " + jsonData.asString());
|
||||
return (T)b;
|
||||
if(type != null){
|
||||
if(classParsers.containsKey(type)){
|
||||
return (T)classParsers.get(type).parse(type, jsonData);
|
||||
}
|
||||
|
||||
if(type != null && Content.class.isAssignableFrom(type)){
|
||||
if(Content.class.isAssignableFrom(type)){
|
||||
return (T)Vars.content.getByName(contentTypes.getThrow(type, () -> new IllegalArgumentException("No content type for class: " + type.getSimpleName())), jsonData.asString());
|
||||
}
|
||||
|
||||
return super.readValue(type, elementType, jsonData);
|
||||
}catch(Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return super.readValue(type, elementType, jsonData);
|
||||
}
|
||||
};
|
||||
|
||||
@ -55,11 +56,6 @@ public class ContentParser{
|
||||
|
||||
return block;
|
||||
},
|
||||
ContentType.item, (TypeParser<Item>)(mod, name, value) -> {
|
||||
Item item = new Item(mod + "-" + name, new Color(Color.black));
|
||||
readFields(item, value);
|
||||
return item;
|
||||
},
|
||||
ContentType.unit, (TypeParser<UnitType>)(mod, name, value) -> {
|
||||
String clas = value.getString("type");
|
||||
Class<BaseUnit> type = resolve("io.anuke.mindustry.entities.type.base." + clas);
|
||||
@ -75,9 +71,20 @@ public class ContentParser{
|
||||
readFields(unit, value);
|
||||
|
||||
return unit;
|
||||
}
|
||||
},
|
||||
ContentType.item, parser(Item::new),
|
||||
ContentType.liquid, parser(Liquid::new),
|
||||
ContentType.mech, parser(Mech::new)
|
||||
);
|
||||
|
||||
private <T extends Content> TypeParser<T> parser(Function<String, T> constructor){
|
||||
return (mod, name, value) -> {
|
||||
T item = constructor.get(mod + "-" + name);
|
||||
readFields(item, value);
|
||||
return item;
|
||||
};
|
||||
}
|
||||
|
||||
private void init(){
|
||||
for(ContentType type : ContentType.all){
|
||||
Array<Content> arr = Vars.content.getBy(type);
|
||||
@ -115,6 +122,21 @@ public class ContentParser{
|
||||
return c;
|
||||
}
|
||||
|
||||
private Object field(Class<?> type, JsonValue value){
|
||||
return field(type, value.asString());
|
||||
}
|
||||
|
||||
/** Gets a field from a static class by name, throwing a descriptive exception if not found. */
|
||||
private Object field(Class<?> type, String name){
|
||||
try{
|
||||
Object b = type.getField(name).get(null);
|
||||
if(b == null) throw new IllegalArgumentException(type.getSimpleName() + ": not found: '" + name + "'");
|
||||
return b;
|
||||
}catch(Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks all @NonNull fields in this object, recursively.
|
||||
* Throws an exception if any are null.*/
|
||||
private void checkNulls(Object object){
|
||||
@ -186,7 +208,11 @@ public class ContentParser{
|
||||
throw new IllegalArgumentException("Type not found: " + potentials[0]);
|
||||
}
|
||||
|
||||
public interface TypeParser<T extends Content>{
|
||||
private interface FieldParser{
|
||||
Object parse(Class<?> type, JsonValue value);
|
||||
}
|
||||
|
||||
private interface TypeParser<T extends Content>{
|
||||
T parse(String mod, String name, JsonValue value) throws Exception;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,9 @@ public class Mod{
|
||||
}
|
||||
|
||||
/** Create any content needed here. */
|
||||
public void loadContent(){}
|
||||
public void loadContent(){
|
||||
|
||||
}
|
||||
|
||||
/** Register any commands to be used on the server side, e.g. from the console. */
|
||||
public void registerServerCommands(CommandHandler handler){
|
||||
|
@ -102,6 +102,7 @@ public class Mods implements Loadable{
|
||||
public void loadSync(){
|
||||
if(packer == null) return;
|
||||
|
||||
//get textures packed
|
||||
if(totalSprites > 0){
|
||||
TextureFilter filter = Core.settings.getBool("linear") ? TextureFilter.Linear : TextureFilter.Nearest;
|
||||
packer.getPages().each(page -> page.updateTexture(filter, filter, false));
|
||||
@ -140,6 +141,9 @@ public class Mods implements Loadable{
|
||||
}
|
||||
}
|
||||
|
||||
//sort mods to make sure servers handle them properly.
|
||||
loaded.sort(Structs.comparing(m -> m.name));
|
||||
|
||||
buildFiles();
|
||||
}
|
||||
|
||||
@ -280,5 +284,7 @@ public class Mods implements Loadable{
|
||||
public static class ModMeta{
|
||||
public String name, author, description, version, main;
|
||||
public String[] dependencies = {}; //TODO implement
|
||||
/** Hidden mods are only server-side or client-side, and do not support adding new content. */
|
||||
public boolean hidden;
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,10 @@ public class Item extends UnlockableContent implements Comparable<Item>{
|
||||
this.description = Core.bundle.getOrNull("item." + this.name + ".description");
|
||||
}
|
||||
|
||||
public Item(String name){
|
||||
this(name, new Color(Color.black));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
regions = new TextureRegion[Icon.values().length];
|
||||
|
@ -34,6 +34,11 @@ public class Liquid extends UnlockableContent{
|
||||
this.description = Core.bundle.getOrNull("liquid." + name + ".description");
|
||||
}
|
||||
|
||||
/** For modding only.*/
|
||||
public Liquid(String name){
|
||||
this(name, new Color(Color.black));
|
||||
}
|
||||
|
||||
public boolean canExtinguish(){
|
||||
return flammability < 0.1f && temperature <= 0.5f;
|
||||
}
|
||||
|
@ -41,6 +41,10 @@ public class Mech extends UnlockableContent{
|
||||
this.description = Core.bundle.get("mech." + name + ".description");
|
||||
}
|
||||
|
||||
public Mech(String name){
|
||||
this(name, false);
|
||||
}
|
||||
|
||||
public String localizedName(){
|
||||
return Core.bundle.get("mech." + name + ".name");
|
||||
}
|
||||
|
@ -216,7 +216,9 @@ public class FileChooser extends FloatingDialog{
|
||||
|
||||
String filename = file.name();
|
||||
|
||||
TextButton button = new TextButton(shorten(filename), Styles.clearTogglet);
|
||||
TextButton button = new TextButton(filename, Styles.clearTogglet);
|
||||
button.getLabel().setWrap(false);
|
||||
button.getLabel().setEllipsis(true);
|
||||
group.add(button);
|
||||
|
||||
button.clicked(() -> {
|
||||
|
@ -161,8 +161,7 @@ public class MenuFragment extends Fragment{
|
||||
new Buttoni("$loadgame", Icon.loadSmall, ui.load::show),
|
||||
new Buttoni("$tutorial", Icon.infoSmall, control::playTutorial)
|
||||
),
|
||||
new Buttoni("$editor", Icon.editorSmall, ui.maps::show),
|
||||
steam ? new Buttoni("$workshop", Icon.saveSmall, platform::openWorkshop) : null,
|
||||
new Buttoni("$editor", Icon.editorSmall, ui.maps::show), steam ? new Buttoni("$workshop", Icon.saveSmall, platform::openWorkshop) : null,
|
||||
new Buttoni(Core.bundle.get("mods") + "\n" + Core.bundle.get("mods.alpha"), Icon.wikiSmall, ui.mods::show),
|
||||
new Buttoni("$settings", Icon.toolsSmall, ui.settings::show),
|
||||
new Buttoni("$about.button", Icon.infoSmall, ui.about::show),
|
||||
|
@ -3,7 +3,7 @@ package io.anuke.mindustry.world;
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.entities.Effects;
|
||||
|
Reference in New Issue
Block a user