Campaign mechanics
This commit is contained in:
@@ -78,8 +78,8 @@ public class Vars implements Loadable{
|
||||
public static final float miningRange = 70f;
|
||||
/** range for building */
|
||||
public static final float buildingRange = 220f;
|
||||
/** duration of time between events in ticks */
|
||||
public static final float eventRate = 5 * Time.toMinutes;
|
||||
/** duration of time between turns in ticks */
|
||||
public static final float turnDuration = 20 * Time.toMinutes;
|
||||
/** min armor fraction damage; e.g. 0.05 = at least 5% damage */
|
||||
public static final float minArmorDamage = 0.05f;
|
||||
/** launch animation duration */
|
||||
@@ -287,7 +287,7 @@ public class Vars implements Loadable{
|
||||
logBuffer.add(result);
|
||||
}else if(!headless){
|
||||
if(!OS.isWindows){
|
||||
for(String code : ColorCodes.codes.values()){
|
||||
for(String code : ColorCodes.values){
|
||||
result = result.replace(code, "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +534,7 @@ public class UnitTypes implements ContentList{
|
||||
isCounted = false;
|
||||
|
||||
flying = true;
|
||||
mineSpeed = 2f;
|
||||
mineSpeed = 12f;
|
||||
mineTier = 1;
|
||||
buildSpeed = 0.5f;
|
||||
drag = 0.05f;
|
||||
|
||||
@@ -507,7 +507,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
platform.updateRPC();
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.pause) && !scene.hasDialog() && !scene.hasKeyboard() && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){
|
||||
if(Core.input.keyTap(Binding.pause) && !state.isOutOfTime() && !scene.hasDialog() && !scene.hasKeyboard() && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){
|
||||
state.set(state.is(State.playing) ? State.paused : State.playing);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,11 @@ public class GameState{
|
||||
return rules.sector != null;
|
||||
}
|
||||
|
||||
/** @return whether the player is in a campaign and they are out of sector time */
|
||||
public boolean isOutOfTime(){
|
||||
return isCampaign() && isGame() && getSector().getTimeSpent() >= turnDuration;
|
||||
}
|
||||
|
||||
public boolean hasSector(){
|
||||
return rules.sector != null;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
state.rules.sector.setLastSecond(universe.seconds());
|
||||
state.rules.sector.setSecondsPassed(0);
|
||||
}
|
||||
|
||||
//enable infinite ammo for wave team by default
|
||||
@@ -281,7 +281,7 @@ public class Logic implements ApplicationListener{
|
||||
sector.save.save();
|
||||
|
||||
//run a turn, since launching takes up a turn
|
||||
universe.runEvents();
|
||||
universe.runTurn();
|
||||
|
||||
//TODO apply extra damage to sector
|
||||
//sector.setTurnsPassed(sector.getTurnsPassed() + 3);
|
||||
@@ -316,6 +316,11 @@ public class Logic implements ApplicationListener{
|
||||
state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type().isCounted);
|
||||
}
|
||||
|
||||
//force pausing when the player is out of sector time
|
||||
if(state.isOutOfTime()){
|
||||
state.set(State.paused);
|
||||
}
|
||||
|
||||
if(!state.isPaused()){
|
||||
state.secinfo.update();
|
||||
|
||||
|
||||
@@ -467,6 +467,15 @@ public class UI implements ApplicationListener, Loadable{
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void announce(String text){
|
||||
Table t = new Table();
|
||||
t.background(Styles.black3).margin(8f)
|
||||
.add(text).style(Styles.outlineLabel);
|
||||
t.update(() -> t.setPosition(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f, Align.center));
|
||||
t.actions(Actions.fadeOut(3, Interp.pow4In));
|
||||
Core.scene.add(t);
|
||||
}
|
||||
|
||||
public void showOkText(String title, String text, Runnable confirmed){
|
||||
BaseDialog dialog = new BaseDialog(title);
|
||||
dialog.cont.add(text).width(500f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
|
||||
|
||||
@@ -45,6 +45,7 @@ public class EventType{
|
||||
public static class PlayEvent{}
|
||||
public static class ResetEvent{}
|
||||
public static class WaveEvent{}
|
||||
public static class TurnEvent{}
|
||||
/** Called when the player places a line, mobile or desktop.*/
|
||||
public static class LineConfirmEvent{}
|
||||
/** Called when a turret recieves ammo, but only when the tutorial is active! */
|
||||
|
||||
@@ -17,6 +17,7 @@ public class SectorInfo{
|
||||
private static final int valueWindow = 60;
|
||||
/** refresh period of export in ticks */
|
||||
private static final float refreshPeriod = 60;
|
||||
|
||||
/** Core input statistics. */
|
||||
public ObjectMap<Item, ExportStat> production = new ObjectMap<>();
|
||||
/** Export statistics. */
|
||||
@@ -31,6 +32,8 @@ public class SectorInfo{
|
||||
public boolean hasCore = true;
|
||||
/** Sector that was launched from. */
|
||||
public @Nullable Sector origin;
|
||||
/** Time spent at this sector. Do not use unless you know what you're doing. */
|
||||
public transient float internalTimeSpent;
|
||||
|
||||
/** Counter refresh state. */
|
||||
private transient Interval time = new Interval();
|
||||
@@ -73,11 +76,21 @@ public class SectorInfo{
|
||||
hasCore = entity != null;
|
||||
bestCoreType = !hasCore ? Blocks.air : state.rules.defaultTeam.cores().max(e -> e.block.size).block;
|
||||
storageCapacity = entity != null ? entity.storageCapacity : 0;
|
||||
|
||||
//update sector's internal time spent counter1
|
||||
state.rules.sector.setTimeSpent(internalTimeSpent);
|
||||
}
|
||||
|
||||
/** Update averages of various stats.
|
||||
* Called every frame. */
|
||||
public void update(){
|
||||
internalTimeSpent += Time.delta();
|
||||
|
||||
//time spent exceeds turn duration!
|
||||
if(internalTimeSpent >= turnDuration && internalTimeSpent - Time.delta() < turnDuration){
|
||||
universe.displayTimeEnd();
|
||||
}
|
||||
|
||||
//create last stored core items
|
||||
if(lastCoreItems == null){
|
||||
lastCoreItems = new int[content.items().size];
|
||||
|
||||
@@ -5,6 +5,8 @@ import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
|
||||
@@ -14,8 +16,7 @@ import static mindustry.Vars.*;
|
||||
public class Universe{
|
||||
private long seconds;
|
||||
private float secondCounter;
|
||||
private int event;
|
||||
private float eventCounter;
|
||||
private int turn;
|
||||
|
||||
private Schematic lastLoadout;
|
||||
private Seq<ItemStack> lastLaunchResources = new Seq<>();
|
||||
@@ -30,6 +31,10 @@ public class Universe{
|
||||
updatePlanet(Planets.sun);
|
||||
}
|
||||
|
||||
public int turn(){
|
||||
return turn;
|
||||
}
|
||||
|
||||
private void updatePlanet(Planet planet){
|
||||
planet.position.setZero();
|
||||
planet.addParentOffset(planet.position);
|
||||
@@ -41,6 +46,14 @@ public class Universe{
|
||||
}
|
||||
}
|
||||
|
||||
public void displayTimeEnd(){
|
||||
if(!headless){
|
||||
state.set(State.paused);
|
||||
|
||||
ui.announce("Next turn incoming.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Update planet rotations, global time and relevant state. */
|
||||
public void update(){
|
||||
secondCounter += Time.delta() / 60f;
|
||||
@@ -55,13 +68,6 @@ public class Universe{
|
||||
}
|
||||
}
|
||||
|
||||
//update event counter - happens only in-game
|
||||
eventCounter += Time.delta();
|
||||
|
||||
if(eventCounter >= eventRate){
|
||||
runEvents();
|
||||
}
|
||||
|
||||
if(state.hasSector()){
|
||||
//update sector light
|
||||
float light = state.getSector().getLight();
|
||||
@@ -110,11 +116,31 @@ public class Universe{
|
||||
}
|
||||
|
||||
/** Runs possible events. Resets event counter. */
|
||||
public void runEvents(){
|
||||
event++;
|
||||
eventCounter = 0;
|
||||
public void runTurn(){
|
||||
turn++;
|
||||
|
||||
int newSecondsPassed = (int)(turnDuration / 60);
|
||||
|
||||
//update relevant sectors
|
||||
for(Planet planet : content.planets()){
|
||||
for(Sector sector : planet.sectors){
|
||||
if(sector.hasSave()){
|
||||
int spent = (int)(sector.getTimeSpent() / 60);
|
||||
int actuallyPassed = Math.max(newSecondsPassed - spent, 0);
|
||||
|
||||
//increment seconds passed for this sector by the time that just passed with this turn
|
||||
if(!sector.isBeingPlayed()){
|
||||
sector.setSecondsPassed(sector.getSecondsPassed() + actuallyPassed);
|
||||
}
|
||||
|
||||
//reset time spent to 0
|
||||
sector.setTimeSpent(0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO events
|
||||
|
||||
Events.fire(new TurnEvent());
|
||||
}
|
||||
|
||||
public float secondsMod(float mod, float scale){
|
||||
@@ -131,14 +157,12 @@ public class Universe{
|
||||
|
||||
private void save(){
|
||||
Core.settings.put("utime", seconds);
|
||||
Core.settings.put("event", event);
|
||||
Core.settings.put("eventtime", eventCounter);
|
||||
Core.settings.put("turn", turn);
|
||||
}
|
||||
|
||||
private void load(){
|
||||
seconds = Core.settings.getLong("utime");
|
||||
event = Core.settings.getInt("event");
|
||||
eventCounter = Core.settings.getFloat("eventtime");
|
||||
turn = Core.settings.getInt("turn");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -113,6 +113,11 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get();
|
||||
lastReadBuild = map.getInt("build", -1);
|
||||
|
||||
//load time spent on sector into state
|
||||
if(state.rules.sector != null){
|
||||
state.secinfo.internalTimeSpent = state.rules.sector.getStoredTimeSpent();
|
||||
}
|
||||
|
||||
if(!headless){
|
||||
Tmp.v1.tryFromString(map.get("viewpos"));
|
||||
Core.camera.position.set(Tmp.v1);
|
||||
|
||||
@@ -131,12 +131,42 @@ public class Sector{
|
||||
return Core.settings.getInt(key("spawn-position"), Point2.pack(world.width() / 2, world.height() / 2));
|
||||
}
|
||||
|
||||
public void setLastSecond(long number){
|
||||
put("last-second", number);
|
||||
/** @return time spent in this sector this turn in ticks. */
|
||||
public float getTimeSpent(){
|
||||
//return currently counting time spent if being played on
|
||||
if(isBeingPlayed()) return state.secinfo.internalTimeSpent;
|
||||
|
||||
//else return the stored value
|
||||
return getStoredTimeSpent();
|
||||
}
|
||||
|
||||
public void setTimeSpent(float time){
|
||||
put("time-spent", time);
|
||||
|
||||
//update counting time
|
||||
if(isBeingPlayed()){
|
||||
state.secinfo.internalTimeSpent = time;
|
||||
}
|
||||
}
|
||||
|
||||
public String displayTimeRemaining(){
|
||||
float amount = Vars.turnDuration - getTimeSpent();
|
||||
int seconds = (int)(amount / 60);
|
||||
int sf = seconds % 60;
|
||||
return (seconds / 60) + ":" + (sf < 10 ? "0" : "") + sf;
|
||||
}
|
||||
|
||||
/** @return the stored amount of time spent in this sector this turn in ticks. */
|
||||
public float getStoredTimeSpent(){
|
||||
return Core.settings.getFloat(key("time-spent"));
|
||||
}
|
||||
|
||||
public void setSecondsPassed(long number){
|
||||
put("seconds-passed", number);
|
||||
}
|
||||
|
||||
public long getSecondsPassed(){
|
||||
return universe.seconds() - Core.settings.getLong(key("last-second"));
|
||||
return Core.settings.getLong(key("seconds-passed"));
|
||||
}
|
||||
|
||||
private String key(String key){
|
||||
|
||||
@@ -23,7 +23,7 @@ import static mindustry.gen.Tex.*;
|
||||
|
||||
@StyleDefaults
|
||||
public class Styles{
|
||||
public static Drawable black, black9, black8, black6, black3, none, flatDown, flatOver;
|
||||
public static Drawable black, black9, black8, black6, black3, black5, none, flatDown, flatOver;
|
||||
public static ButtonStyle defaultb, waveb;
|
||||
public static TextButtonStyle defaultt, squaret, nodet, cleart, discordt, infot, clearPartialt, clearTogglet, clearToggleMenut, togglet, transt;
|
||||
public static ImageButtonStyle defaulti, nodei, righti, emptyi, emptytogglei, selecti, cleari, clearFulli, clearPartiali, clearPartial2i, clearTogglei, clearTransi, clearToggleTransi, clearTogglePartiali;
|
||||
@@ -40,6 +40,7 @@ public class Styles{
|
||||
black9 = whiteui.tint(0f, 0f, 0f, 0.9f);
|
||||
black8 = whiteui.tint(0f, 0f, 0f, 0.8f);
|
||||
black6 = whiteui.tint(0f, 0f, 0f, 0.6f);
|
||||
black5 = whiteui.tint(0f, 0f, 0f, 0.5f);
|
||||
black3 = whiteui.tint(0f, 0f, 0f, 0.3f);
|
||||
none = whiteui.tint(0f, 0f, 0f, 0f);
|
||||
flatDown = createFlatDown();
|
||||
|
||||
@@ -34,6 +34,12 @@ public class PausedDialog extends BaseDialog{
|
||||
});
|
||||
|
||||
if(!mobile){
|
||||
//TODO localize
|
||||
cont.label(() -> state.getSector() == null ? "" :
|
||||
"[lightgray]Next turn in [accent]" + state.getSector().displayTimeRemaining())
|
||||
.visible(() -> state.getSector() != null).colspan(2);
|
||||
cont.row();
|
||||
|
||||
float dw = 220f;
|
||||
cont.defaults().width(dw).height(55).pad(5f);
|
||||
|
||||
@@ -45,7 +51,10 @@ public class PausedDialog extends BaseDialog{
|
||||
//}else{
|
||||
// cont.button("$database", Icon.book, ui.database::show);
|
||||
//}
|
||||
cont.button("placeholder", Icon.warning, () -> ui.showInfo("go away"));
|
||||
//TODO remove
|
||||
cont.button("launch core", Icon.warning, () -> ui.planet.show(state.getSector(), player.team().core()))
|
||||
.disabled(b -> player.team().core() == null || !player.team().core().items.has(player.team().core().block.requirements))
|
||||
.visible(() -> state.isCampaign());
|
||||
cont.button("$settings", Icon.settings, ui.settings::show);
|
||||
|
||||
if(!state.rules.tutorial){
|
||||
|
||||
@@ -7,6 +7,7 @@ import arc.graphics.gl.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
@@ -197,19 +198,36 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
cont.clear();
|
||||
titleTable.remove();
|
||||
|
||||
//add listener to the background rect, so it doesn't get unnecessary touch input
|
||||
cont.rect((x, y, w, h) -> planets.render(this)).grow().get().addListener(new ElementGestureListener(){
|
||||
@Override
|
||||
public void tap(InputEvent event, float x, float y, int count, KeyCode button){
|
||||
if(hovered != null && (mode == launch ? canLaunch(hovered) && hovered != launchSector : hovered.unlocked())){
|
||||
selected = hovered;
|
||||
}
|
||||
|
||||
if(selected != null){
|
||||
updateSelected();
|
||||
}
|
||||
cont.stack(
|
||||
new Element(){
|
||||
{
|
||||
//add listener to the background rect, so it doesn't get unnecessary touch input
|
||||
addListener(new ElementGestureListener(){
|
||||
@Override
|
||||
public void tap(InputEvent event, float x, float y, int count, KeyCode button){
|
||||
if(hovered != null && (mode == launch ? canLaunch(hovered) && hovered != launchSector : hovered.unlocked())){
|
||||
selected = hovered;
|
||||
}
|
||||
|
||||
if(selected != null){
|
||||
updateSelected();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
planets.render(PlanetDialog.this);
|
||||
}
|
||||
},
|
||||
new Table(t -> {
|
||||
//TODO localize
|
||||
t.top();
|
||||
t.label(() -> "Turn " + universe.turn()).style(Styles.outlineLabel).color(Pal.accent);
|
||||
})).grow();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -47,6 +47,7 @@ public class HudFragment extends Fragment{
|
||||
|
||||
//TODO details and stuff
|
||||
Events.on(SectorCaptureEvent.class, e ->{
|
||||
//TODO localize
|
||||
showToast("Sector[accent] captured[]!");
|
||||
});
|
||||
|
||||
@@ -55,6 +56,16 @@ public class HudFragment extends Fragment{
|
||||
coreItems.resetUsed();
|
||||
});
|
||||
|
||||
Events.on(TurnEvent.class, e -> {
|
||||
ui.announce("[accent][[ Turn " + universe.turn() + " ]");
|
||||
});
|
||||
|
||||
//paused table
|
||||
parent.fill(t -> {
|
||||
t.top().visible(() -> state.isPaused() && !state.isOutOfTime()).touchable(Touchable.disabled);
|
||||
t.table(Styles.black5, top -> top.add("$paused").style(Styles.outlineLabel).pad(8f)).growX();
|
||||
});
|
||||
|
||||
//TODO tear this all down
|
||||
//menu at top left
|
||||
parent.fill(cont -> {
|
||||
@@ -254,6 +265,27 @@ public class HudFragment extends Fragment{
|
||||
.update(label -> label.getColor().set(Color.orange).lerp(Color.scarlet, Mathf.absin(Time.time(), 2f, 1f)))).touchable(Touchable.disabled);
|
||||
});
|
||||
|
||||
//paused table for when the player is out of time
|
||||
parent.fill(t -> {
|
||||
t.top().visible(() -> state.isOutOfTime());
|
||||
t.table(Styles.black5, top -> {
|
||||
//TODO localize when done
|
||||
top.add("Out of sector time.").style(Styles.outlineLabel).color(Pal.accent).update(l -> l.color.a = Mathf.absin(Time.globalTime(), 7f, 1f)).colspan(2);
|
||||
top.row();
|
||||
|
||||
top.defaults().pad(2).size(150f, 54f);
|
||||
top.button("Next Turn", () -> {
|
||||
universe.runTurn();
|
||||
state.set(State.playing);
|
||||
});
|
||||
|
||||
top.button("Back to Planet", () -> {
|
||||
ui.paused.runExitSave();
|
||||
ui.planet.show();
|
||||
});
|
||||
}).margin(8).growX();
|
||||
});
|
||||
|
||||
//tutorial text
|
||||
parent.fill(t -> {
|
||||
Runnable resize = () -> {
|
||||
@@ -276,12 +308,6 @@ public class HudFragment extends Fragment{
|
||||
Events.on(ResizeEvent.class, e -> resize.run());
|
||||
});
|
||||
|
||||
//paused table
|
||||
parent.fill(t -> {
|
||||
t.top().visible(() -> state.isPaused()).touchable(Touchable.disabled);
|
||||
t.table(Tex.buttonTrans, top -> top.add("$paused").pad(5f));
|
||||
});
|
||||
|
||||
//'saving' indicator
|
||||
parent.fill(t -> {
|
||||
t.bottom().visible(() -> control.saves.isSaving());
|
||||
@@ -335,14 +361,6 @@ public class HudFragment extends Fragment{
|
||||
}).visible(() -> state.isCampaign() && content.items().contains(i -> state.secinfo.getExport(i) > 0));
|
||||
});
|
||||
|
||||
//TODO move, select loadout, consume resources
|
||||
parent.fill(t -> {
|
||||
t.bottom().visible(() -> state.isCampaign() && player.team().core() != null);
|
||||
|
||||
t.button("test launch", Icon.warning, () -> ui.planet.show(state.getSector(), player.team().core()))
|
||||
.width(150f).disabled(b -> player.team().core() == null || !player.team().core().items.has(player.team().core().block.requirements)); //disable core when missing resources for launch
|
||||
});
|
||||
|
||||
blockfrag.build(parent);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user