Campaign mechanics

This commit is contained in:
Anuken
2020-07-06 21:53:34 -04:00
parent a6523630bf
commit 56ffe5aea8
22 changed files with 196 additions and 54 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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! */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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