648 lines
14 KiB
ActionScript
648 lines
14 KiB
ActionScript
#priority init 100
|
|
#priority draw 100
|
|
import elements.BaseGuiElement;
|
|
import elements.GuiButton;
|
|
import elements.GuiListbox;
|
|
import elements.GuiText;
|
|
import elements.GuiSprite;
|
|
import elements.GuiPanel;
|
|
import dialogs.MessageDialog;
|
|
import version;
|
|
from gui import animate_speed, animate_time, animate_remove;
|
|
|
|
enum MenuAnimation {
|
|
MAni_LeftOut,
|
|
MAni_RightHide,
|
|
|
|
MAni_RightOut,
|
|
MAni_LeftHide,
|
|
|
|
MAni_LeftIn,
|
|
MAni_RightShow,
|
|
|
|
MAni_RightIn,
|
|
MAni_LeftShow,
|
|
};
|
|
|
|
GuiText@ version;
|
|
MenuContainer@ menu_container;
|
|
GuiSprite@ menu_logo;
|
|
MenuBox@ active_menu;
|
|
DescBox@ active_desc;
|
|
|
|
MenuBox@ main_menu;
|
|
MenuBox@ campaign_menu;
|
|
MenuBox@ load_menu;
|
|
MenuBox@ save_menu;
|
|
MenuBox@ options_menu;
|
|
MenuBox@ mods_menu;
|
|
|
|
const double MSLIDE_TIME = 0.6;
|
|
const double MANI_SPEED_1 = 200.0;
|
|
const double MANI_SPEED_2 = 120.0;
|
|
const int MANI_SPACE = -240;
|
|
|
|
const double MENU_OFFSET_TIME = 0.1;
|
|
const int MENU_OFFSET = 10;
|
|
|
|
const double BG_FADE_TIME = 0.7;
|
|
const double BG_FADE_DELAY = 0.2;
|
|
|
|
DynamicTexture logo;
|
|
DynamicTexture defaultBackground;
|
|
bool hasDefaultBackground = false;
|
|
|
|
DynamicTexture currentBackground;
|
|
double bgTimer = 0;
|
|
string displayedBackground;
|
|
string backgroundFile;
|
|
|
|
string latestSave;
|
|
|
|
void init() {
|
|
//Show the version
|
|
@version = GuiText(null, Alignment(Right-200, Bottom-20, Right-4, Bottom));
|
|
version.horizAlign = 1.0;
|
|
version.text = format("Version: $1 ($2)", GAME_VERSION, SCRIPT_VERSION);
|
|
version.color = Color(0xaaaaaaaa);
|
|
|
|
//Create container
|
|
@menu_container = MenuContainer();
|
|
|
|
//Ready backgrounds
|
|
@defaultBackground.material.shader = shader::MenuBlur;
|
|
defaultBackground.material.wrapHorizontal = TW_ClampEdge;
|
|
defaultBackground.material.wrapVertical = TW_ClampEdge;
|
|
|
|
@currentBackground.material.shader = shader::MenuSaveBackground;
|
|
currentBackground.material.wrapHorizontal = TW_ClampEdge;
|
|
currentBackground.material.wrapVertical = TW_ClampEdge;
|
|
|
|
//Find latest savegame
|
|
string dir = baseProfile["saves"];
|
|
FileList files(dir, "*.sr2");
|
|
|
|
uint cnt = files.length;
|
|
int64 lastTime = 0;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
string basename = getBasename(files.basename[i], false);
|
|
string fname = path_join(dir, basename)+".sr2";
|
|
|
|
int64 mtime = getModifiedTime(fname);
|
|
if(mtime > lastTime) {
|
|
lastTime = mtime;
|
|
latestSave = basename;
|
|
}
|
|
}
|
|
|
|
//Find latest screenshot
|
|
string latestShot;
|
|
files.navigate(dir, "*.png");
|
|
|
|
lastTime = 0;
|
|
cnt = files.length;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
string fname = files.path[i];
|
|
|
|
int64 mtime = getModifiedTime(fname);
|
|
if(mtime > lastTime) {
|
|
lastTime = mtime;
|
|
latestShot = fname;
|
|
}
|
|
}
|
|
|
|
//Set background from latest screenshot
|
|
if(latestShot.length > 0 && fileExists(latestShot) && settings::bMenuBGScreenshot) {
|
|
hasDefaultBackground = true;
|
|
defaultBackground.load(latestShot);
|
|
}
|
|
else {
|
|
hasDefaultBackground = true;
|
|
defaultBackground.load("data/images/title_shot_BG.png");
|
|
}
|
|
|
|
if(hasDLC("Heralds"))
|
|
logo.load("data/images/heralds_logo.png");
|
|
else
|
|
logo.load("data/images/sr_logo.png");
|
|
}
|
|
|
|
void tick(double time) {
|
|
mouseLock = (game_state == GS_Game);
|
|
|
|
if(backgroundFile.length != 0) {
|
|
if(backgroundFile != displayedBackground) {
|
|
if(currentBackground.isLoaded()) {
|
|
currentBackground.load(backgroundFile);
|
|
bgTimer = BG_FADE_TIME;
|
|
displayedBackground = backgroundFile;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
displayedBackground = "";
|
|
}
|
|
|
|
if(bgTimer >= 0)
|
|
bgTimer = max(0.0, bgTimer - time);
|
|
|
|
if(active_menu !is null)
|
|
active_menu.tick(time);
|
|
|
|
if(isUpdating)
|
|
updateTick(time);
|
|
}
|
|
|
|
void switchToMenu(MenuBox@ menu, bool left = true, bool snap = false) {
|
|
if(snap || active_menu is null) {
|
|
if(active_menu !is null)
|
|
active_menu.hide();
|
|
if(menu !is null)
|
|
menu.show();
|
|
}
|
|
else {
|
|
active_menu.animate(left ? MAni_LeftOut : MAni_RightOut);
|
|
if(menu !is null)
|
|
menu.animate(left ? MAni_RightIn : MAni_LeftIn);
|
|
}
|
|
|
|
@active_menu = menu;
|
|
if(menu_logo !is null)
|
|
menu_logo.visible = menu !is null;
|
|
}
|
|
|
|
void showDescBox(DescBox@ box) {
|
|
if(active_desc !is null)
|
|
active_desc.hide();
|
|
if(box !is null)
|
|
box.show();
|
|
@active_desc = box;
|
|
}
|
|
|
|
class MenuContainer : BaseGuiElement {
|
|
bool animating = false;
|
|
bool hide = false;
|
|
|
|
MenuContainer() {
|
|
super(null, recti());
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void updateAbsolutePosition() {
|
|
if(!animating) {
|
|
size = parent.size;
|
|
position = vec2i(0, 0);
|
|
}
|
|
BaseGuiElement::updateAbsolutePosition();
|
|
}
|
|
|
|
void animateIn() {
|
|
animating = true;
|
|
hide = false;
|
|
|
|
rect = recti_area(vec2i(-parent.size.x, 0), parent.size);
|
|
animate_time(this, recti_area(vec2i(), parent.size), MSLIDE_TIME);
|
|
}
|
|
|
|
void animateOut() {
|
|
animating = true;
|
|
hide = true;
|
|
|
|
rect = recti_area(vec2i(), parent.size);
|
|
animate_time(this, recti_area(vec2i(-parent.size.x, 0), parent.size), MSLIDE_TIME);
|
|
}
|
|
|
|
void show() {
|
|
hide = false;
|
|
Position = recti_area(vec2i(), parent.size);
|
|
animate_remove(this);
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
bool onGuiEvent(const GuiEvent& event) {
|
|
switch(event.type) {
|
|
case GUI_Animation_Complete:
|
|
if(event.caller is this) {
|
|
animating = false;
|
|
if(hide)
|
|
visible = false;
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return BaseGuiElement::onGuiEvent(event);
|
|
}
|
|
};
|
|
|
|
class MenuAction : GuiListElement {
|
|
int value = -1;
|
|
Sprite icon;
|
|
string text;
|
|
bool disabled = false;
|
|
double offset = 0.0;
|
|
Color color;
|
|
|
|
MenuAction(const string& txt, int val, bool dis = false) {
|
|
value = val;
|
|
text = txt;
|
|
disabled = dis;
|
|
}
|
|
|
|
MenuAction(const Sprite& sprt, const string& txt, int val, bool dis = false) {
|
|
value = val;
|
|
text = txt;
|
|
disabled = dis;
|
|
icon = sprt;
|
|
}
|
|
|
|
void set(const string& txt) {
|
|
text = txt;
|
|
}
|
|
|
|
string get() {
|
|
return text;
|
|
}
|
|
|
|
void draw(GuiListbox@ ele, uint flags, const recti& absPos) override {
|
|
const Font@ font = ele.skin.getFont(ele.TextFont);
|
|
int baseLine = font.getBaseline();
|
|
vec2i textOffset(ele.horizPadding, (ele.lineHeight - baseLine) / 2);
|
|
textOffset.x += offset * MENU_OFFSET;
|
|
if(ele.itemStyle == SS_NULL)
|
|
ele.skin.draw(SS_ListboxItem, flags, absPos);
|
|
if(icon.valid) {
|
|
int iSize = absPos.height - 6;
|
|
recti iPos = recti_area(absPos.topLeft + vec2i(textOffset.x, 4), vec2i(iSize, iSize));
|
|
iPos = iPos.aspectAligned(icon.aspect);
|
|
icon.draw(iPos);
|
|
textOffset.x += iSize + 8;
|
|
}
|
|
if(disabled)
|
|
font.draw(absPos.topLeft + textOffset, text, Color(0x888888ff));
|
|
else
|
|
font.draw(absPos.topLeft + textOffset, text, color);
|
|
if(flags & SF_Hovered != 0)
|
|
offset = min(1.0, offset + (frameLength / MENU_OFFSET_TIME));
|
|
else
|
|
offset = max(0.0, offset - (frameLength / MENU_OFFSET_TIME));
|
|
}
|
|
|
|
bool onMouseEvent(const MouseEvent& event) {
|
|
return disabled;
|
|
}
|
|
};
|
|
|
|
class MenuBox : BaseGuiElement {
|
|
GuiListbox@ items;
|
|
GuiText@ title;
|
|
|
|
MenuAnimation anim;
|
|
bool animating = false;
|
|
bool selectable = false;
|
|
|
|
MenuBox() {
|
|
super(menu_container, recti());
|
|
visible = false;
|
|
|
|
@title = GuiText(this, Alignment(Left, Top+4, Right, Top+44), "Load Game");
|
|
title.horizAlign = 0.5;
|
|
title.font = FT_Big;
|
|
|
|
@items = GuiListbox(this, Alignment(Left+4, Top+44, Right-4, Bottom-4));
|
|
items.itemStyle = SS_MainMenuItem;
|
|
items.font = FT_Medium;
|
|
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void tick(double time) {
|
|
}
|
|
|
|
void animate(MenuAnimation type) {
|
|
switch(type) {
|
|
case MAni_LeftIn:
|
|
sendToBack();
|
|
show();
|
|
case MAni_LeftOut:
|
|
animate_speed(this, basePosition-vec2i(size.width/2+MANI_SPACE, 0), MANI_SPEED_1);
|
|
break;
|
|
case MAni_RightHide:
|
|
case MAni_RightShow:
|
|
animate_speed(this, basePosition, MANI_SPEED_2);
|
|
break;
|
|
case MAni_RightIn:
|
|
sendToBack();
|
|
show();
|
|
case MAni_RightOut:
|
|
animate_speed(this, basePosition+vec2i(size.width/2+MANI_SPACE, 0), MANI_SPEED_1);
|
|
break;
|
|
case MAni_LeftHide:
|
|
case MAni_LeftShow:
|
|
animate_speed(this, basePosition, MANI_SPEED_2);
|
|
break;
|
|
}
|
|
|
|
anim = type;
|
|
animating = true;
|
|
}
|
|
|
|
void completeAnimation(MenuAnimation type) {
|
|
animating = false;
|
|
switch(type) {
|
|
case MAni_LeftOut:
|
|
sendToBack();
|
|
animate(MAni_RightHide);
|
|
break;
|
|
case MAni_RightHide:
|
|
hide();
|
|
break;
|
|
case MAni_RightOut:
|
|
sendToBack();
|
|
animate(MAni_LeftHide);
|
|
break;
|
|
case MAni_LeftHide:
|
|
hide();
|
|
break;
|
|
case MAni_LeftIn:
|
|
bringToFront();
|
|
animate(MAni_RightShow);
|
|
break;
|
|
case MAni_RightShow:
|
|
break;
|
|
case MAni_RightIn:
|
|
bringToFront();
|
|
animate(MAni_LeftShow);
|
|
break;
|
|
case MAni_LeftShow:
|
|
break;
|
|
}
|
|
|
|
if(!animating)
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
recti get_basePosition() {
|
|
int width = 576;
|
|
int height = min(648, int(Parent.size.height * 0.8f - 36));
|
|
|
|
vec2i position = vec2i(Parent.size.width / 2 - 12 - width,
|
|
Parent.size.height * 0.2f + 24);
|
|
return recti_area(position, vec2i(width, height));
|
|
}
|
|
|
|
void updateAbsolutePosition() {
|
|
if(!animating)
|
|
rect = basePosition;
|
|
BaseGuiElement::updateAbsolutePosition();
|
|
}
|
|
|
|
void refresh() {
|
|
clearMenu();
|
|
buildMenu();
|
|
}
|
|
|
|
void clearMenu() {
|
|
items.clearItems();
|
|
}
|
|
|
|
void buildMenu() {
|
|
}
|
|
|
|
void show() {
|
|
clearMenu();
|
|
buildMenu();
|
|
visible = true;
|
|
}
|
|
|
|
void hide() {
|
|
visible = false;
|
|
}
|
|
|
|
bool onGuiEvent(const GuiEvent& event) {
|
|
switch(event.type) {
|
|
case GUI_Changed:
|
|
if(event.caller is items) {
|
|
MenuAction@ act = cast<MenuAction>(items.selectedItem);
|
|
if(act !is null)
|
|
onSelected(act.text, act.value);
|
|
if(!selectable)
|
|
items.clearSelection();
|
|
return true;
|
|
}
|
|
break;
|
|
case GUI_Animation_Complete:
|
|
if(event.caller is this) {
|
|
completeAnimation(anim);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return BaseGuiElement::onGuiEvent(event);
|
|
}
|
|
|
|
void onSelected(const string& name, int value) {
|
|
}
|
|
|
|
void draw() {
|
|
skin.draw(SS_MainMenuPanel, SF_Normal, AbsolutePosition);
|
|
BaseGuiElement::draw();
|
|
}
|
|
};
|
|
|
|
class DescBox : BaseGuiElement {
|
|
GuiText@ title;
|
|
GuiPanel@ panel;
|
|
|
|
DescBox() {
|
|
super(menu_container, recti());
|
|
visible = false;
|
|
|
|
@title = GuiText(this, Alignment(Left, Top+4, Right, Top+44), "");
|
|
title.horizAlign = 0.5;
|
|
title.font = FT_Big;
|
|
|
|
@panel = GuiPanel(this, Alignment(Left+3, Top+44, Right-4, Bottom-4));
|
|
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
recti get_basePosition() {
|
|
int width = 576;
|
|
int height = min(648, int(Parent.size.height * 0.8f - 36));
|
|
|
|
vec2i position = vec2i(Parent.size.width / 2 + 12,
|
|
Parent.size.height * 0.2f + 24);
|
|
return recti_area(position, vec2i(width, height));
|
|
}
|
|
|
|
void updateAbsolutePosition() {
|
|
rect = basePosition;
|
|
BaseGuiElement::updateAbsolutePosition();
|
|
}
|
|
|
|
void refresh() {
|
|
}
|
|
|
|
void show() {
|
|
visible = true;
|
|
}
|
|
|
|
void hide() {
|
|
visible = false;
|
|
}
|
|
|
|
void draw() {
|
|
skin.draw(SS_MainMenuDescPanel, SF_Normal, AbsolutePosition);
|
|
BaseGuiElement::draw();
|
|
}
|
|
};
|
|
|
|
//Pause the game when entering the menu if we're set to
|
|
double prevGameSpeed = 0;
|
|
void onGameStateChange() {
|
|
if(game_state == GS_Menu) {
|
|
if(!mpClient && !mpServer) {
|
|
prevGameSpeed = gameSpeed;
|
|
if(settings::bMenuPause && gameSpeed != 0)
|
|
gameSpeed = 0;
|
|
}
|
|
active_menu.refresh();
|
|
}
|
|
else {
|
|
if(!mpClient && !mpServer && settings::bMenuPause && gameSpeed == 0)
|
|
gameSpeed = prevGameSpeed;
|
|
}
|
|
}
|
|
|
|
//Render client view if a game is active
|
|
void preRender(double time) {
|
|
if(game_running)
|
|
preRenderClient();
|
|
}
|
|
|
|
void render(double time) {
|
|
if(game_running)
|
|
renderClient();
|
|
}
|
|
|
|
void drawBackground(DynamicTexture@ bg, double alpha = 1.0) {
|
|
if(!bg.isLoaded(0))
|
|
return;
|
|
|
|
vec2i screen(screenSize);
|
|
vec2i matSize(bg.size[0]);
|
|
|
|
if(matSize.width == 0 || matSize.height == 0)
|
|
return;
|
|
|
|
float aspect = float(matSize.width) / float(matSize.height);
|
|
if(aspect > 1.f) {
|
|
matSize.height = float(screen.width) / aspect;
|
|
matSize.width = screen.width;
|
|
}
|
|
else {
|
|
matSize.height = screen.height;
|
|
matSize.width = float(screen.height) * aspect;
|
|
}
|
|
|
|
recti pos = recti_area(
|
|
vec2i((screen.width - matSize.width) / 2,
|
|
(screen.height - matSize.height) / 2),
|
|
matSize);
|
|
|
|
Color col(0xffffffff);
|
|
col.a = alpha * 255;
|
|
bg.draw(pos, col);
|
|
}
|
|
|
|
bool isUpdating = false;
|
|
bool startedUpdate = false;
|
|
WebData updateCheck;
|
|
void checkForUpdates() {
|
|
if(isUpdating)
|
|
return;
|
|
isUpdating = true;
|
|
webAPICall("updates/version", updateCheck);
|
|
}
|
|
|
|
void updateTick(double time) {
|
|
if(startedUpdate) {
|
|
if(!::updating) {
|
|
startedUpdate = false;
|
|
isUpdating = false;
|
|
if(updateStatus < 0)
|
|
message(format(locale::UPDATE_FAIL, toString(updateStatus)));
|
|
else
|
|
quitGame();
|
|
}
|
|
}
|
|
else if(updateCheck.completed) {
|
|
if(updateCheck.error) {
|
|
message(locale::CHECK_UPDATE_FAIL+":\n"+updateCheck.errorStr);
|
|
isUpdating = false;
|
|
return;
|
|
}
|
|
else {
|
|
string ver = updateCheck.result.trimmed();
|
|
if(ver == GAME_VERSION) {
|
|
message(locale::CHECK_UPDATE_UPTODATE);
|
|
isUpdating = false;
|
|
return;
|
|
}
|
|
|
|
//Do update
|
|
startedUpdate = true;
|
|
updateGame();
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw() {
|
|
//Draw background screenshot
|
|
if(!game_running && defaultBackground.isLoaded() && hasDefaultBackground)
|
|
drawBackground(defaultBackground);
|
|
if(currentBackground.isLoaded() && backgroundFile.length != 0) {
|
|
if(BG_FADE_TIME - bgTimer > BG_FADE_DELAY)
|
|
drawBackground(currentBackground, (BG_FADE_TIME - bgTimer) / BG_FADE_TIME);
|
|
}
|
|
|
|
//Draw top and bottom bars
|
|
vec2i screen = screenSize;
|
|
double size = double(screen.x) / 1920;
|
|
|
|
if(isUpdating) {
|
|
gui_root.visible = false;
|
|
|
|
string txt;
|
|
if(updating)
|
|
txt = format(locale::UPDATE_PROGRESS, toString(updateProgress, 0));
|
|
else
|
|
txt = locale::CHECKING_UPDATES;
|
|
|
|
auto@ ft = gui_root.skin.getFont(FT_Big);
|
|
ft.draw(pos=recti_area(vec2i(),screenSize), horizAlign=0.5, vertAlign=0.5,
|
|
stroke=colors::Black, color=colors::Green, text=txt);
|
|
}
|
|
else if(inGalaxyCreation && !mpClient) {
|
|
gui_root.visible = false;
|
|
auto@ ft = gui_root.skin.getFont(FT_Big);
|
|
ft.draw(pos=recti_area(vec2i(),screenSize), horizAlign=0.5, vertAlign=0.5,
|
|
stroke=colors::Black, color=colors::Green, text=locale::MENU_LOADING);
|
|
}
|
|
else {
|
|
gui_root.visible = true;
|
|
if(menu_container.visible && logo.isLoaded()) {
|
|
vec2i size = logo.material.size;
|
|
int logoWidth = min(840, menu_container.size.width);
|
|
int logoHeight = min(350, int(floor(0.2 * menu_container.size.height) + 23));
|
|
recti area = recti_area(vec2i((menu_container.size.width-logoWidth)/2, 0), vec2i(logoWidth, logoHeight));
|
|
area += menu_container.absolutePosition.topLeft;
|
|
|
|
area = area.aspectAligned(float(size.x) / float(size.y), 0.5, 0.5);
|
|
logo.draw(area, colors::White);
|
|
}
|
|
}
|
|
}
|