More workshop implementation

This commit is contained in:
Anuken 2019-09-10 20:53:31 -04:00
parent ad3463cbc4
commit e4cdf515c9
18 changed files with 1056 additions and 862 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);
}
}

View 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;

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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));

View File

@ -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);
}

View File

@ -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);

View 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();
});
}
}

View File

@ -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);

View File

@ -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]){

View File

@ -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;
}

View File

@ -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()));
}