package com.galvarez.ttw.screens;
import static java.util.stream.Collectors.toList;
import java.util.ListIterator;
import java.util.function.Consumer;
import com.artemis.World;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox;
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox.SelectBoxStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import com.galvarez.ttw.ThingsThatWereGame;
import com.galvarez.ttw.model.data.Culture;
import com.galvarez.ttw.model.data.Empire;
import com.galvarez.ttw.model.data.SessionSettings;
import com.galvarez.ttw.model.map.MapGenerator.Generator;
import com.galvarez.ttw.rendering.ui.FramedMenu;
/**
* This screen presents the user with a menu to launch the game.
*
* @author Guillaume Alvarez
*/
public final class CustomizeGameMenuScreen extends AbstractScreen {
private final Stage stage;
private final SessionSettings settings;
private final FramedMenu map;
private final FramedMenu empires;
private final FramedMenu actions;
private final Texture square;
public CustomizeGameMenuScreen(ThingsThatWereGame game, World world, SpriteBatch batch, SessionSettings settings) {
super(game, world, batch);
this.settings = settings;
stage = new Stage(new ScreenViewport(), batch);
Skin skin = new Skin(Gdx.files.internal("uiskin/uiskin.json"));
map = new FramedMenu(skin, 800, 160).nbColumns(2);
empires = new FramedMenu(skin, 800, 480).nbColumns(3);
actions = new FramedMenu(skin, 800, 160);
square = createSquare();
updateMenu();
}
private Texture createSquare() {
// A Pixmap is basically a raw image in memory as represented by pixels
// We create one 16 wide, 16 height using 8 bytes for Red, Green, Blue and
// Alpha channels
Pixmap pixmap = new Pixmap(16, 16, Pixmap.Format.RGBA8888);
// Fill it red
pixmap.setColor(Color.WHITE);
pixmap.fill();
return new Texture(pixmap);
}
private void updateMenu() {
map.clear();
map.addSelectBox("Map type:", settings.mapType.get(), Generator.values(), new Consumer<Generator>() {
@Override
public void accept(Generator t) {
settings.mapType.set(t);
updateMenu();
}
});
for (String prop : settings.map.stringPropertyNames())
map.addTextField(prop, settings.map.get(prop),
(textField, c) -> settings.map.setProperty(prop, textField.getText()));
map.addToStage(stage, 30, stage.getHeight() - 30, false);
empires.clear();
LabelStyle style = new LabelStyle(empires.getSkin().get(LabelStyle.class));
empires.getTable().add(new Label("EMPIRE", style));
empires.getTable().add(new Label("CULTURE", style));
empires.getTable().add(new Label("CONTROL", style));
empires.getTable().row();
for (Empire e : settings.empires)
displayEmpire(e, empires.getSkin(), empires.getTable());
empires.addToStage(stage, 30, map.getY() - 30, false);
stage.setScrollFocus(empires.getTable());
actions.clear();
actions.addCheckBox("Start with diplomacy?", settings.startWithDiplomacy.get(),
s -> settings.startWithDiplomacy.set(s));
actions.addButton("Add new empire", "", this::createNewEmpire,
settings.empires.size() < SessionSettings.COLORS.size());
actions.addButton("Start new game", () -> game.startGame(false));
actions.addToStage(stage, 30, empires.getY() - 30, false);
}
private void displayEmpire(Empire empire, Skin skin, Table table) {
Image i = new Image(square);
i.setColor(empire.color);
table.add(i).minHeight(i.getMinHeight()).prefHeight(i.getPrefHeight());
SelectBox<Culture> sb = new SelectBox<Culture>(skin.get(SelectBoxStyle.class));
sb.setItems(settings
.getCultures()
.stream()
.filter(
c -> c == empire.culture
|| settings.empires.stream().filter(e -> e.getCulture() == c).count() < c.cities.size)
.collect(toList()).toArray(new Culture[0]));
sb.setSelected(empire.culture);
sb.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
changeCulture(empire, sb.getSelected());
}
});
table.add(sb).minHeight(sb.getMinHeight()).prefHeight(sb.getMinHeight());
Button b = new Button(skin.get(ButtonStyle.class));
LabelStyle style = new LabelStyle(skin.get(LabelStyle.class));
style.fontColor = empire.color;
b.add(new Label(empire.isComputerControlled() ? "CPU" : "Player", style));
b.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
changeController(empire);
}
});
table.add(b).minHeight(b.getMinHeight()).prefHeight(b.getPrefHeight());
table.row();
}
private void changeCulture(Empire e, Culture selected) {
settings.empires.set(settings.empires.indexOf(e), new Empire(e.color, selected, e.isComputerControlled()));
updateMenu();
}
private void changeController(Empire e) {
if (e.isComputerControlled()) {
// can only have 1 player-controlled empire
for (ListIterator<Empire> it = settings.empires.listIterator(); it.hasNext();) {
Empire empire = it.next();
it.set(new Empire(e.color, e.culture, empire != e));
}
} else
settings.empires.set(settings.empires.indexOf(e), new Empire(e.color, e.culture, true));
updateMenu();
}
/**
* Create a new empire from random data. It tries to use unused
* culture/colors.
*/
private void createNewEmpire() {
settings.empires.add(new Empire(settings.guessColor(), settings.guessCulture(), true));
updateMenu();
}
@Override
public void show() {
super.show();
Gdx.input.setInputProcessor(stage);
}
@Override
public void render(float delta) {
super.render(delta);
stage.act(delta);
stage.draw();
}
@Override
public void resize(int width, int height) {
super.resize(width, height);
stage.getViewport().update(width, height, true);
/*
* The GUI use a ScreenViewport, meaning it won't scale when screen size
* change. This is fine because we don't want the GUI size to change,
* becoming zoomed in and ugly or zoomed and unreadable. However it has a
* small side effect: the existing menu were placed according to the
* vertical stage size. The stage size changed with the screen (game window)
* one. So we must recompute the GUI elements coordinates. The simlest way
* to do it is to recreate the menu.
*/
updateMenu();
}
@Override
public void dispose() {
super.dispose();
stage.dispose();
}
}