Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,647 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user