mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-02-13 12:16:53 +07:00
More workshop implementation
This commit is contained in:
parent
ad3463cbc4
commit
e4cdf515c9
BIN
core/assets-raw/sprites/ui/icons/icon-workshop.png
Normal file
BIN
core/assets-raw/sprites/ui/icons/icon-workshop.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
@ -14,7 +14,7 @@ linkfail = Failed to open link!\nThe URL has been copied to your clipboard.
|
||||
screenshot = Screenshot saved to {0}
|
||||
screenshot.invalid = Map too large, potentially not enough memory for screenshot.
|
||||
gameover = Game Over
|
||||
gameover.pvp = The[accent] {0}[] team is victorious!f
|
||||
gameover.pvp = The[accent] {0}[] team is victorious!
|
||||
highscore = [accent]New highscore!
|
||||
|
||||
load.sound = Sounds
|
||||
@ -52,8 +52,17 @@ close = Close
|
||||
website = Website
|
||||
quit = Quit
|
||||
maps = Maps
|
||||
maps.browse = Browse Maps
|
||||
continue = Continue
|
||||
maps.none = [lightgray]No maps found!
|
||||
invalid = Invalid
|
||||
preparingconfig = Prepating Config
|
||||
preparingcontent = Prepating Content
|
||||
uploadingcontent = Uploading Content
|
||||
uploadingpreviewfile = Uploading Preview File
|
||||
committingchanges = Comitting Changes
|
||||
done = Done
|
||||
|
||||
about.button = About
|
||||
name = Name:
|
||||
noname = Pick a[accent] player name[] first.
|
||||
@ -200,6 +209,8 @@ map.nospawn.pvp = This map does not have any enemy cores for player to spawn int
|
||||
map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor.
|
||||
map.invalid = Error loading map: corrupted or invalid map file.
|
||||
map.publish.error = Error publishing map: {0}
|
||||
map.publish = Map published.
|
||||
map.publishing = [accent]Publishing map...
|
||||
editor.brush = Brush
|
||||
editor.openin = Open In Editor
|
||||
editor.oregen = Ore Generation
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 575 KiB After Width: | Height: | Size: 577 KiB |
@ -383,7 +383,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
if(world.isZone()){
|
||||
for(Tile tile : state.teams.get(player.getTeam()).cores){
|
||||
for(Item item : content.items()){
|
||||
if(tile.entity.items.has(item)){
|
||||
if(tile.entity != null && tile.entity.items.has(item)){
|
||||
data.unlockContent(item);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package io.anuke.mindustry.core;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.Input.*;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.math.*;
|
||||
@ -25,6 +26,9 @@ public interface Platform{
|
||||
/** Steam: Share a map on the workshop.*/
|
||||
default void publishMap(Map map){}
|
||||
|
||||
/** Steam: Find workshop maps.*/
|
||||
default void findMaps(String query, Consumer<Array<PostedMap>> result){}
|
||||
|
||||
/** Get the networking implementation.*/
|
||||
default NetProvider getNet(){
|
||||
return new ArcNetImpl();
|
||||
@ -107,4 +111,16 @@ public interface Platform{
|
||||
/** Stops forcing the app into landscape orientation.*/
|
||||
default void endForceLandscape(){
|
||||
}
|
||||
|
||||
/** Specifies a map posted in the steam workshop.*/
|
||||
interface PostedMap{
|
||||
String name();
|
||||
String author();
|
||||
String description();
|
||||
String gamemode();
|
||||
void openPage();
|
||||
void subscribe();
|
||||
boolean subscribed();
|
||||
void preview(Consumer<FileHandle> file);
|
||||
}
|
||||
}
|
@ -67,6 +67,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public DeployDialog deploy;
|
||||
public TechTreeDialog tech;
|
||||
public MinimapDialog minimap;
|
||||
public BrowseMapsDialog browse;
|
||||
|
||||
public Cursor drillCursor, unloadCursor;
|
||||
|
||||
@ -217,6 +218,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
deploy = new DeployDialog();
|
||||
tech = new TechTreeDialog();
|
||||
minimap = new MinimapDialog();
|
||||
browse = new BrowseMapsDialog();
|
||||
|
||||
Group group = Core.scene.root;
|
||||
|
||||
|
@ -152,7 +152,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
menu.cont.row();
|
||||
|
||||
if(steam){
|
||||
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.arrowSmall, () -> {
|
||||
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> {
|
||||
Map map = save();
|
||||
if(map != null){
|
||||
platform.publishMap(map);
|
||||
@ -162,7 +162,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
menu.cont.row();
|
||||
}
|
||||
|
||||
menu.cont.addImageTextButton("$editor.ingame", Icon.arrowSmall, this::playtest).padTop(-3).size(swidth * 2f + 10, 60f);
|
||||
menu.cont.addImageTextButton("$editor.ingame", Icon.arrowSmall, this::playtest).padTop(!steam ? -3 : 1).size(swidth * 2f + 10, 60f);
|
||||
|
||||
menu.cont.row();
|
||||
|
||||
@ -293,9 +293,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
if(map != null && !map.custom){
|
||||
handleSaveBuiltin(map);
|
||||
}else{
|
||||
maps.saveMap(editor.getTags());
|
||||
returned = maps.saveMap(editor.getTags());
|
||||
ui.showInfoFade("$editor.saved");
|
||||
returned = map;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,7 @@ public class WaveInfoDialog extends FloatingDialog{
|
||||
groups = maps.readWaves(Core.app.getClipboardText());
|
||||
buildGroups();
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
ui.showErrorMessage("$waves.invalid");
|
||||
}
|
||||
dialog.hide();
|
||||
|
@ -25,7 +25,7 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool
|
||||
private float lifeScl;
|
||||
private Team team;
|
||||
private Object data;
|
||||
private boolean supressCollision, supressOnce, initialized;
|
||||
private boolean supressCollision, supressOnce, initialized, deflected;
|
||||
|
||||
protected BulletType type;
|
||||
protected Entity owner;
|
||||
@ -100,9 +100,14 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool
|
||||
return type.collidesTiles;
|
||||
}
|
||||
|
||||
public void supress(){
|
||||
public void deflect(){
|
||||
supressCollision = true;
|
||||
supressOnce = true;
|
||||
deflected = true;
|
||||
}
|
||||
|
||||
public boolean isDeflected(){
|
||||
return deflected;
|
||||
}
|
||||
|
||||
public BulletType getBulletType(){
|
||||
@ -239,6 +244,7 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool
|
||||
data = null;
|
||||
supressCollision = false;
|
||||
supressOnce = false;
|
||||
deflected = false;
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
|
@ -297,7 +297,7 @@ public abstract class InputHandler implements InputProcessor{
|
||||
|
||||
ItemStack stack = player.item();
|
||||
|
||||
if(tile.block().acceptStack(stack.item, stack.amount, tile, player) > 0 && tile.interactable(player.getTeam()) && tile.block().hasItems){
|
||||
if(tile.block().acceptStack(stack.item, stack.amount, tile, player) > 0 && tile.interactable(player.getTeam()) && tile.block().hasItems && player.item().amount > 0 && !player.isTransferring && tile.interactable(player.getTeam())){
|
||||
Call.transferInventory(player, tile);
|
||||
}else{
|
||||
Call.dropItem(player.angleTo(x, y));
|
||||
|
@ -108,7 +108,7 @@ public class Maps{
|
||||
* Save a custom map to the directory. This updates all values and stored data necessary.
|
||||
* The tags are copied to prevent mutation later.
|
||||
*/
|
||||
public void saveMap(ObjectMap<String, String> baseTags){
|
||||
public Map saveMap(ObjectMap<String, String> baseTags){
|
||||
|
||||
try{
|
||||
StringMap tags = new StringMap(baseTags);
|
||||
@ -166,6 +166,9 @@ public class Maps{
|
||||
}
|
||||
maps.add(map);
|
||||
maps.sort();
|
||||
|
||||
return map;
|
||||
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -37,6 +37,18 @@ public class Bar extends Element{
|
||||
});
|
||||
}
|
||||
|
||||
public Bar(){
|
||||
|
||||
}
|
||||
|
||||
public void set(Supplier<String> name, FloatProvider fraction, Color color){
|
||||
this.fraction = fraction;
|
||||
this.lastValue = fraction.get();
|
||||
this.blinkColor.set(color);
|
||||
setColor(color);
|
||||
update(() -> this.name = name.get());
|
||||
}
|
||||
|
||||
public Bar blink(Color color){
|
||||
blinkColor.set(color);
|
||||
return this;
|
||||
@ -44,6 +56,8 @@ public class Bar extends Element{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(fraction == null) return;
|
||||
|
||||
float computed = Mathf.clamp(fraction.get());
|
||||
if(!Mathf.isEqual(lastValue, computed)){
|
||||
blink = 1f;
|
||||
@ -73,7 +87,7 @@ public class Bar extends Element{
|
||||
|
||||
Draw.color();
|
||||
|
||||
BitmapFont font = Fonts.def;
|
||||
BitmapFont font = Fonts.outline;
|
||||
GlyphLayout lay = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
|
||||
lay.setText(font, name);
|
||||
|
||||
|
75
core/src/io/anuke/mindustry/ui/dialogs/BrowseMapsDialog.java
Normal file
75
core/src/io/anuke/mindustry/ui/dialogs/BrowseMapsDialog.java
Normal file
@ -0,0 +1,75 @@
|
||||
package io.anuke.mindustry.ui.dialogs;
|
||||
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.assets.*;
|
||||
import io.anuke.arc.graphics.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.scene.ui.*;
|
||||
import io.anuke.arc.scene.ui.layout.*;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.*;
|
||||
import io.anuke.mindustry.core.Platform.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.ui.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.platform;
|
||||
|
||||
public class BrowseMapsDialog extends FloatingDialog{
|
||||
|
||||
public BrowseMapsDialog(){
|
||||
super("$maps.browse");
|
||||
shown(this::setup);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
cont.clear();
|
||||
|
||||
cont.addImage(Icon.refresh);
|
||||
platform.findMaps("", list -> {
|
||||
Table maps = new Table();
|
||||
maps.marginRight(24);
|
||||
|
||||
ScrollPane pane = new ScrollPane(maps);
|
||||
pane.setFadeScrollBars(false);
|
||||
|
||||
int maxwidth = Mathf.clamp((int)(Core.graphics.getWidth() / Scl.scl(230)), 1, 8);
|
||||
float mapsize = 200f;
|
||||
|
||||
int i = 0;
|
||||
for(PostedMap map : list){
|
||||
|
||||
if(i % maxwidth == 0){
|
||||
maps.row();
|
||||
}
|
||||
|
||||
TextButton button = maps.addButton("", Styles.cleart, map::openPage).width(mapsize).pad(8).get();
|
||||
button.clearChildren();
|
||||
button.margin(9);
|
||||
button.add(map.name()).width(mapsize - 18f).center().get().setEllipsis(true);
|
||||
button.row();
|
||||
button.addImage().growX().pad(4).color(Pal.gray);
|
||||
button.row();
|
||||
Stack stack = button.stack(new Image(Icon.refresh)).size(mapsize - 20f).get();
|
||||
map.preview(file -> {
|
||||
Core.assets.load(new AssetDescriptor<>(file, Texture.class)).loaded = ct -> {
|
||||
Texture tex = (Texture)ct;
|
||||
stack.clearChildren();
|
||||
stack.add(new Image(tex).setScaling(Scaling.fit));
|
||||
stack.add(new BorderImage(tex).setScaling(Scaling.fit));
|
||||
};
|
||||
});
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(Vars.maps.all().size == 0){
|
||||
maps.add("$maps.none");
|
||||
}
|
||||
|
||||
cont.add(buttons).growX();
|
||||
cont.row();
|
||||
cont.add(pane).uniformX();
|
||||
});
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.anuke.mindustry.ui.fragments;
|
||||
|
||||
import io.anuke.arc.function.*;
|
||||
import io.anuke.arc.scene.Group;
|
||||
import io.anuke.arc.scene.actions.*;
|
||||
import io.anuke.arc.scene.event.Touchable;
|
||||
@ -12,6 +13,7 @@ import io.anuke.mindustry.ui.*;
|
||||
public class LoadingFragment extends Fragment{
|
||||
private Table table;
|
||||
private TextButton button;
|
||||
private Bar bar;
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
@ -27,23 +29,35 @@ public class LoadingFragment extends Fragment{
|
||||
t.addImage().growX().height(3f).pad(4f).growX().get().setColor(Pal.accent);
|
||||
t.row();
|
||||
|
||||
button = t.addButton("$cancel", () -> {
|
||||
}).pad(20).size(250f, 70f).visible(false).get();
|
||||
bar = t.add(new Bar()).pad(3).size(500f, 40f).visible(false).get();
|
||||
t.row();
|
||||
button = t.addButton("$cancel", () -> {}).pad(20).size(250f, 70f).visible(false).get();
|
||||
table = t;
|
||||
});
|
||||
}
|
||||
|
||||
public void setProgress(FloatProvider progress){
|
||||
bar.visible(true);
|
||||
bar.set(() -> ((int)(progress.get() * 100) + "%"), progress, Pal.accent);
|
||||
}
|
||||
|
||||
public void setButton(Runnable listener){
|
||||
button.visible(true);
|
||||
button.getListeners().remove(button.getListeners().size - 1);
|
||||
button.clicked(listener);
|
||||
}
|
||||
|
||||
public void setText(String text){
|
||||
table.<Label>find("namelabel").setText(text);
|
||||
table.<Label>find("namelabel").setColor(Pal.accent);
|
||||
}
|
||||
|
||||
public void show(){
|
||||
show("$loading");
|
||||
}
|
||||
|
||||
public void show(String text){
|
||||
bar.visible(false);
|
||||
table.clearActions();
|
||||
table.touchable(Touchable.enabled);
|
||||
table.<Label>find("namelabel").setText(text);
|
||||
|
@ -162,6 +162,7 @@ public class MenuFragment extends Fragment{
|
||||
new Buttoni("$loadgame", Icon.loadSmall, ui.load::show),
|
||||
new Buttoni("$tutorial", Icon.infoSmall, control::playTutorial)
|
||||
),
|
||||
steam ? new Buttoni("$maps.browse", Icon.saveSmall, ui.browse::show) : null,
|
||||
new Buttoni("$editor", Icon.editorSmall, ui.maps::show),
|
||||
new Buttoni("$settings", Icon.toolsSmall, ui.settings::show),
|
||||
new Buttoni("$about.button", Icon.infoSmall, ui.about::show),
|
||||
@ -197,6 +198,7 @@ public class MenuFragment extends Fragment{
|
||||
|
||||
private void buttons(Table t, Buttoni... buttons){
|
||||
for(Buttoni b : buttons){
|
||||
if(b == null) continue;
|
||||
Button[] out = {null};
|
||||
out[0] = t.addImageTextButton(b.text, b.icon, Styles.clearToggleMenut, () -> {
|
||||
if(currentMenu == out[0]){
|
||||
|
@ -5,7 +5,7 @@ import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.*;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.mindustry.entities.bullet.Bullet;
|
||||
import io.anuke.mindustry.entities.type.TileEntity;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
@ -47,14 +47,14 @@ public class DeflectorWall extends Wall{
|
||||
super.handleBulletHit(entity, bullet);
|
||||
|
||||
//doesn't reflect powerful bullets
|
||||
if(bullet.damage() > maxDamageDeflect) return;
|
||||
if(bullet.damage() > maxDamageDeflect || bullet.isDeflected()) return;
|
||||
|
||||
float penX = Math.abs(entity.x - bullet.x), penY = Math.abs(entity.y - bullet.y);
|
||||
|
||||
bullet.hitbox(rect2);
|
||||
|
||||
Vector2 position = Geometry.raycastRect(bullet.x, bullet.y, bullet.x + bullet.velocity().x, bullet.y + bullet.velocity().y,
|
||||
rect.setCenter(entity.x, entity.y).setSize(size * tilesize + rect2.width + rect2.height));
|
||||
Vector2 position = Geometry.raycastRect(bullet.x - bullet.velocity().x*Time.delta(), bullet.y - bullet.velocity().y*Time.delta(), bullet.x + bullet.velocity().x*Time.delta(), bullet.y + bullet.velocity().y*Time.delta(),
|
||||
rect.setSize(size * tilesize + rect2.width*2 + rect2.height*2).setCenter(entity.x, entity.y));
|
||||
|
||||
if(position != null){
|
||||
bullet.set(position.x, position.y);
|
||||
@ -66,10 +66,10 @@ public class DeflectorWall extends Wall{
|
||||
bullet.velocity().y *= -1;
|
||||
}
|
||||
|
||||
bullet.updateVelocity();
|
||||
//bullet.updateVelocity();
|
||||
bullet.resetOwner(entity, Team.derelict);
|
||||
bullet.scaleTime(1f);
|
||||
bullet.supress();
|
||||
bullet.deflect();
|
||||
|
||||
((DeflectorEntity)entity).hit = 1f;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package io.anuke.mindustry.desktop.steam;
|
||||
|
||||
import com.codedisaster.steamworks.*;
|
||||
import com.codedisaster.steamworks.SteamRemoteStorage.*;
|
||||
import com.codedisaster.steamworks.SteamUGC.*;
|
||||
import io.anuke.arc.*;
|
||||
import io.anuke.arc.files.*;
|
||||
import io.anuke.arc.util.*;
|
||||
@ -18,6 +19,8 @@ public class SWorkshop implements SteamUGCCallback{
|
||||
public void publishMap(Map map){
|
||||
this.lastMap = map;
|
||||
ugc.createItem(SVars.steamID, WorkshopFileType.GameManagedItem);
|
||||
ui.loadfrag.show("$map.publishing");
|
||||
Log.info("Publish map " + map.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -47,15 +50,21 @@ public class SWorkshop implements SteamUGCCallback{
|
||||
return;
|
||||
}
|
||||
|
||||
//SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + publishedFileID.toString());
|
||||
|
||||
Map map = lastMap;
|
||||
Log.info("Create item {0} result {1} {2}", SteamNativeHandle.getNativeHandle(publishedFileID), result, needsToAcceptWLA);
|
||||
|
||||
if(result == SteamResult.OK){
|
||||
SteamUGCUpdateHandle h = ugc.startItemUpdate(SVars.steamID, publishedFileID);
|
||||
|
||||
Gamemode mode = Gamemode.attack.valid(map) ? Gamemode.attack : Gamemode.survival;
|
||||
FileHandle mapFile = tmpDirectory.child("map_" + publishedFileID.toString()).child("preview.png");
|
||||
FileHandle mapFile = tmpDirectory.child("map_" + publishedFileID.toString()).child("map.msav");
|
||||
lastMap.file.copyTo(mapFile);
|
||||
|
||||
Log.info(mapFile.parent().absolutePath());
|
||||
Log.info(map.previewFile().absolutePath());
|
||||
|
||||
ugc.setItemTitle(h, map.name());
|
||||
ugc.setItemDescription(h, map.description());
|
||||
ugc.setItemTags(h, new String[]{"map", mode.name()});
|
||||
@ -64,6 +73,18 @@ public class SWorkshop implements SteamUGCCallback{
|
||||
ugc.setItemContent(h, mapFile.parent().absolutePath());
|
||||
ugc.addItemKeyValueTag(h, "mode", mode.name());
|
||||
ugc.submitItemUpdate(h, "Map created");
|
||||
|
||||
ItemUpdateInfo info = new ItemUpdateInfo();
|
||||
|
||||
ui.loadfrag.setProgress(() -> {
|
||||
ItemUpdateStatus status = ugc.getItemUpdateProgress(h, info);
|
||||
ui.loadfrag.setText("$" + status.name().toLowerCase());
|
||||
if(status == ItemUpdateStatus.Invalid){
|
||||
ui.loadfrag.setText("$done");
|
||||
return 1f;
|
||||
}
|
||||
return (float)status.ordinal() / (float)ItemUpdateStatus.values().length;
|
||||
});
|
||||
}else{
|
||||
ui.showErrorMessage(Core.bundle.format("map.publish.error ", result.name()));
|
||||
}
|
||||
@ -73,9 +94,11 @@ public class SWorkshop implements SteamUGCCallback{
|
||||
|
||||
@Override
|
||||
public void onSubmitItemUpdate(SteamPublishedFileID publishedFileID, boolean needsToAcceptWLA, SteamResult result){
|
||||
ui.loadfrag.hide();
|
||||
Log.info("onsubmititemupdate {0} {1} {2}", publishedFileID, needsToAcceptWLA, result);
|
||||
if(result == SteamResult.OK){
|
||||
//redirect user to page for further updates
|
||||
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + publishedFileID.toString());
|
||||
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + SteamNativeHandle.getNativeHandle(publishedFileID));
|
||||
}else{
|
||||
ui.showErrorMessage(Core.bundle.format("map.publish.error ", result.name()));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user