DinkSmallwoodHD/source/GUI/BrowseMenu.cpp
seth 20f8a6a856 * Mobile GUI tweaks
* Added option to disable screen scroll transitions, one of my Android test devices has the slowest glread in the world, it's like 1 full second to walk to the next screen because of it.  This might make dmods that 
use timing (Myster island camera sequence for example) easier though, not sure.
* Fixed issue where behavior of two load_sequence commands in a row on the same sequence differed from 1.08 (in 1.08, max frame is set to the second one as long as the first one wasn't actually loaded yet.  key now shows up in TGKA)
* Fixed regression where offsets in Mayhem were incorrect on the hand mouse pointer
* Upped max frames per seq to 100 instead of 50, fixes Echoes of the Ancient.  I should probably make it dynamic but changing one const number is much easier!
* Now gives a clear warning in the log.txt if a seq tries to go beyond what it can handle and will truncate it (better than breaking the whole sequence as it did before)
* Added support for a weird bmp header type, fixes incorrect palette issue in the dmod The Orb of Darkness
* Fixed life/exp/health to correctly interpolate between values like the original dink, huge thanks to Dan's detailed bug report on this, he even made a mod which made testing the fix a breeze!

git-svn-id: svn://rtsoft.com/rtsvn/projects/RTDink@1520 353e56fe-9613-0410-8469-b96ad8e6f29c
2017-10-08 04:12:51 +00:00

568 lines
No EOL
16 KiB
C++

#include "PlatformPrecomp.h"
#include "BrowseMenu.h"
#include "MainMenu.h"
#include "DMODInstallMenu.h"
#include "GameMenu.h"
#include "Entity/EntityUtils.h"
#include "dink/dink.h"
#include "PopUpMenu.h"
#include "util/TextScanner.h"
#include "EnterURLMenu.h"
#include "Renderer/SoftSurface.h"
#include "QuickTipMenu.h"
#include "DMODMenu.h"
#include "Network/NetHTTP.h"
#include "Entity/HTTPComponent.h"
enum DMODSortEnum
{
DMOD_SORT_RATING,
DMOD_SORT_DATE,
DMOD_SORT_ALPHABETICAL,
//add above here
DMOD_SORT_COUNT
};
int g_dmodSorting = DMOD_SORT_RATING;
int g_dmods_per_screen = 5;
int g_dmod_cur_page = 0;
struct DMODEntry
{
bool operator < (const DMODEntry& str) const
{
return (m_rating < str.m_rating);
}
string m_name;
string m_url;
string m_author;
float m_size;
float m_rating;
string m_description;
string m_date;
string m_version;
string m_thumb;
};
vector<DMODEntry> g_dmodData;
void BrowseMenuAddScrollContent(Entity *pParent, TextScanner *t);
Entity * ShowScoreMessage(Entity *pMenu, string msg);
int SmartModulo(int a, int b)
{
return (((a % b) + b) % b);
}
void BrowseMenuOnSelect(VariantList *pVList) //0=vec2 point of click, 1=entity sent from
{
Entity *pEntClicked = pVList->m_variant[1].GetEntity();
LogMsg("Clicked %s entity at %s", pEntClicked->GetName().c_str(),pVList->m_variant[1].Print().c_str());
Entity *pMenu = GetEntityRoot()->GetEntityByName("BrowseMenu");
//LogMsg("Clicked %s entity at %s", pEntClicked->GetName().c_str(),pVList->m_variant[0].Print().c_str());
if (pEntClicked->GetName() == "Back")
{
SlideScreen(pEntClicked->GetParent(), false);
GetMessageManager()->CallEntityFunction(pEntClicked->GetParent(), 500, "OnDelete", NULL);
DMODMenuCreate(pEntClicked->GetParent()->GetParent(), true);
return;
}
int totalDMODPages = g_dmodData.size() / g_dmods_per_screen;
if (pEntClicked->GetName() == "Next")
{
g_dmod_cur_page++;
g_dmod_cur_page = SmartModulo(g_dmod_cur_page, totalDMODPages+1);
BrowseMenuAddScrollContent(pMenu, NULL);
return;
}
if (pEntClicked->GetName() == "Prev")
{
g_dmod_cur_page--;
g_dmod_cur_page = SmartModulo(g_dmod_cur_page, totalDMODPages+1);
BrowseMenuAddScrollContent(pMenu, NULL);
return;
}
if (pEntClicked->GetName() == "Label")
{
g_dmodSorting = ( (g_dmodSorting + 1) % DMOD_SORT_COUNT);
g_dmod_cur_page = 0;
BrowseMenuAddScrollContent(pMenu, NULL);
return;
}
//they must have clicked on a DMOD if they got this far
if (pEntClicked->GetName() == "install")
{
//save position of the scroll bar first
EntityComponent *pScrollerComp = pMenu->GetEntityByName("scroll")->GetComponentByName("Scroll");
//GetApp()->GetVar("DMODProgress2d")->Set(pScrollerComp->GetVar("progress2d")->GetVector2());
string dmodurl = pEntClicked->GetParent()->GetVar("dmodurl")->GetString();
string dmodName = pEntClicked->GetParent()->GetVar("dmodtitle")->GetString();
SendFakeInputMessageToEntity(GetEntityRoot(), MESSAGE_TYPE_GUI_CLICK_END, pVList->m_variant[0].GetVector2()); //otherwise the menu may never get the touch release message
DisableAllButtonsEntity(pMenu);
SlideScreen(pMenu, false);
GetMessageManager()->CallEntityFunction(pMenu, 500, "OnDelete", NULL);
DMODInstallMenuCreate(pEntClicked->GetParent()->GetParent()->GetParent()->GetParent()->GetParent(), dmodurl, GetDMODRootPath() , "", true, dmodName);
}
//GetEntityRoot()->PrintTreeAsText(); //useful for debugging
}
string VersionToString(float v)
{
char tmp[32];
sprintf(tmp, "%.2f", v);
if (tmp[3] == '0')
{
//cut off this, not needed
tmp[3] = 0;
}
return string(tmp);
}
void AddEntryBar(Entity *pParent, float &x, float &y, DMODEntry &s, int index)
{
Entity *pBG = CreateOverlayEntity(pParent, s.m_name, ReplaceWithLargeInFileName("interface/iphone/browse_dmod_bar.rttex"), x, y);
y += pBG->GetVar("size2d")->GetVector2().y;
pBG->GetVar("dmodurl")->Set(s.m_url); //save for later
pBG->GetVar("dmodtitle")->Set(s.m_name); //save for later
//title
string displayName = s.m_name;
int maxNameChars = 30;
int maxDescripChars = 57;
switch (g_dmodSorting)
{
case DMOD_SORT_DATE:
break;
default:
maxNameChars = 37;
break;
}
if (displayName.length() > maxNameChars)
{
TruncateString(displayName, maxNameChars);
displayName += "...";
}
string displayDescription = s.m_description;
if (displayDescription.length() > maxDescripChars)
{
TruncateString(displayDescription, maxDescripChars);
displayDescription += "...";
}
char stTemp[512];
switch (g_dmodSorting)
{
case DMOD_SORT_DATE:
sprintf(stTemp, "#%d %s `6%s`` R:`6%.1f``", index+1, s.m_date.c_str(), displayName.c_str(), s.m_rating);
break;
default:
sprintf(stTemp, "#%d `6%s`` R:`6%.1f``", index+1, displayName.c_str(), s.m_rating);
break;
}
Entity *pTitle = CreateTextLabelEntity(pBG, "title", iPhoneMapX2X( 16) ,iPhoneMapY2X( 13), stTemp);
Entity *pDescription = CreateTextBoxEntity(pBG, "descrip", iPhoneMap2X(16, 34), iPhoneMap2X(425, 54), "`6"+ displayDescription);
//Entity *pIcon = CreateButtonHotspot(pBG, "icon_hotspot", GetDMODBarIconOffset(), GetDMODBarIconSize(), Button2DComponent::BUTTON_STYLE_CLICK_ON_TOUCH_IGNORE_DRAGGING);
//SetTouchPaddingEntity(pIcon, CL_Rectf(0,iPhoneMapY2X(5),0,iPhoneMapY2X(5)));
//pIcon->GetFunction("OnButtonSelected")->sig_function.connect(&BrowseMenuOnSelect);
//processing the icon image might be slow, lets do it a bit later, sequencing the timing by using the y, which should be going up
//the delete icon part
{
CL_Vec2f iconPos = iPhoneMap2X(379,10);
//CL_Vec2f iconSize = iPhoneMap2X(27, 27);
Entity *pIcon = CreateOverlayButtonEntity(pBG, "install", ReplaceWithLargeInFileName("interface/iphone/browse_install.rttex"), iconPos.x, iconPos.y);
SetButtonStyleEntity(pIcon, Button2DComponent::BUTTON_STYLE_CLICK_ON_TOUCH_IGNORE_DRAGGING);
SetTouchPaddingEntity(pIcon, CL_Rectf(0,0,0,0));
pIcon->GetFunction("OnButtonSelected")->sig_function.connect(&BrowseMenuOnSelect);
}
//add animation effect
//ZoomToPositionFromThisOffsetEntity(pBG, CL_Vec2f(GetScreenSizeXf(), 0), 500, INTERPOLATE_EASE_TO, 10);
}
void UpdateBrowseControlButtons(Entity *pParent)
{
Entity *pEnt = NULL;
pParent->RemoveEntityByName("Prev");
pParent->RemoveEntityByName("Next");
pParent->RemoveEntityByName("Label");
int totalDMODPages = g_dmodData.size() / g_dmods_per_screen;
//if (g_dmod_cur_page > 0)
{
pEnt = CreateTextButtonEntity(pParent, "Prev", iPhoneMapX(25 + 50 * 1), iPhoneMapY(BACK_BUTTON_Y), "`6<<``Prev", false);
pEnt->GetFunction("OnButtonSelected")->sig_function.connect(&BrowseMenuOnSelect);
SetButtonRepeatDelayMS(pEnt, 50);
}
//if (g_dmod_cur_page < totalDMODPages)
{
pEnt = CreateTextButtonEntity(pParent, "Next", iPhoneMapX(25 + 50 * 2), iPhoneMapY(BACK_BUTTON_Y), "Next`6>>``", false);
pEnt->GetFunction("OnButtonSelected")->sig_function.connect(&BrowseMenuOnSelect);
SetButtonRepeatDelayMS(pEnt, 50);
}
string sorting;
switch (g_dmodSorting)
{
case DMOD_SORT_DATE:
sorting = "newest";
break;
case DMOD_SORT_ALPHABETICAL:
sorting = "alphabetical";
break;
case DMOD_SORT_RATING:
sorting = "ratings";
break;
default:
sorting = "error";
break;
}
string label = "Page `6" + toString(g_dmod_cur_page+1) + "``/`6" + toString(totalDMODPages+1) + "`` - Sorting by `6<``"+ sorting+"`6>``";
pEnt = CreateTextButtonEntity(pParent, "Label", iPhoneMapX(25 + 50 * 4), iPhoneMapY(BACK_BUTTON_Y), label, false);
pEnt->GetFunction("OnButtonSelected")->sig_function.connect(&BrowseMenuOnSelect);
}
bool DmodEntrySortByName(const DMODEntry& lhs, const DMODEntry& rhs)
{
return lhs.m_name < rhs.m_name;
}
bool DmodEntrySortByDate(const DMODEntry& lhs, const DMODEntry& rhs)
{
return lhs.m_date < rhs.m_date;
}
void BrowseMenuAddScrollContent(Entity *pParent, TextScanner *t)
{
pParent = pParent->GetEntityByName("scroll_child");
pParent->RemoveAllEntities();
float x = iPhoneMapX(5);
float y = 0;
//Entity *pEnt;
int dmodsAdded = 0;
if (g_dmodData.empty())
{
string msg = t->GetMultipleLineStrings("msg", "|");
vector<string> p = StringTokenize(msg, "|");
if (p.size() == 2 && p[1].length() > 1)
{
StringReplace("<cr>", "\n", p[1]);
//add a message we just downloaded
CL_Vec2f vTextBoxPos(x + iPhoneMapX(5), y);
CL_Vec2f vTextBounds(iPhoneMapX(434), iPhoneMapY(200));
Entity *pEnt = CreateTextBoxEntity(pParent, "", vTextBoxPos, vTextBounds, p[1]);
y += pEnt->GetVar("size2d")->GetVector2().y;
y += iPhoneMapY(5);
}
string line;
//populate our internal DB
g_dmodData.clear();
for (int i = 0; i < t->GetLineCount(); i++)
{
//LogMsg(line.c_str());
vector<string> p = StringTokenize(t->GetLine(i), "|");
if (p.size() < 8) continue; //don't care
DMODEntry s;
s.m_name = p[0];
if (s.m_name == "Title") continue;
s.m_url = p[1];
s.m_author = p[2];
s.m_size = atof(p[3].c_str());
s.m_rating = atof(p[4].c_str());
s.m_description = p[5];
s.m_version = p[6];
s.m_date = p[7].c_str();
StringReplace("V", "", s.m_version);
StringReplace("v", "", s.m_version);
s.m_thumb = atof(p[8].c_str());
g_dmodData.push_back(s);
dmodsAdded++;
}
}
switch (g_dmodSorting)
{
case DMOD_SORT_ALPHABETICAL:
sort(g_dmodData.begin(), g_dmodData.end(), DmodEntrySortByName);
break;
case DMOD_SORT_DATE:
sort(g_dmodData.rbegin(), g_dmodData.rend(), DmodEntrySortByDate);
break;
default:
sort(g_dmodData.rbegin(), g_dmodData.rend());
break;
}
//actually add them to the list
for (int i = g_dmods_per_screen*(g_dmod_cur_page); i < (g_dmods_per_screen*g_dmod_cur_page)+g_dmods_per_screen && i < g_dmodData.size(); i++)
{
AddEntryBar(pParent, x, y, g_dmodData[i], i);
//y += iPhoneMapY(1);
}
VariantList vList(pParent->GetParent());
ResizeScrollBounds(&vList);
DisableHorizontalScrolling(pParent->GetParent());
UpdateBrowseControlButtons(pParent->GetParent()->GetParent());
}
void OnDownloadError(VariantList *pVList)
{
NetHTTP::eError e = (NetHTTP::eError)pVList->m_variant[1].GetUINT32();
string msg = "`4Unable to connect. Try later. ("+toString(e)+")";
if (e == NetHTTP::ERROR_COMMUNICATION_TIMEOUT)
{
msg = "`4Connection timed out. Try Later.";
}
ShowScoreMessage(pVList->m_variant[0].GetComponent()->GetParent(), msg);
}
Entity * ShowScoreMessage(Entity *pMenu, string msg)
{
Entity *pInfo = pMenu->GetEntityByName("Info");
if (pInfo)
{
pInfo->GetComponentByName("TextRender")->GetVar("text")->Set(msg);
pInfo->RemoveComponentByName("Typer"); // a thing that types stuff
} else
{
pInfo = CreateTextLabelEntity(pMenu, "Info", iPhoneMapX(130), iPhoneMapY(220), msg);
}
return pInfo;
}
void OnDownloadHTTPFinish(VariantList *pVList)
{
Entity *pMenu = pVList->m_variant[0].GetComponent()->GetParent();
TextScanner t((char*)pVList->m_variant[1].GetString().c_str());
string line;
// ShowScoreMessage(pMenu, t.GetParmString("msg",1));
ShowScoreMessage(pMenu, "");
//GetApp()->GetVar("score_msg")->Set(t.GetParmString("msg",1));
//GetHighScoreManager()->SetupOnlineScores(t);
//GetApp()->GetVar("cur_score")->Set(uint32(0)); //reset score drawing
//ScoresAddStuffToScroll(NULL);
BrowseMenuAddScrollContent(pMenu, &t);
// AddHighScores(pMenu, -1);
}
void DownloadDMODList(Entity *pMenu)
{
pMenu->RemoveComponentByName("HTTP"); //just in case it already exists
//get the internet stuff going
EntityComponent *pComp = pMenu->AddComponent(new HTTPComponent);
VariantList vPostData;
vPostData.m_variant[0].Set("version");
vPostData.m_variant[1].Set(toString(GetApp()->GetVersion()));
pComp->GetFunction("AddPostData")->sig_function(&vPostData);
vPostData.m_variant[0].Set("build");
vPostData.m_variant[1].Set(toString(GetApp()->GetBuild()));
pComp->GetFunction("AddPostData")->sig_function(&vPostData);
vPostData.m_variant[0].Set("platform");
vPostData.m_variant[1].Set(toString(GetEmulatedPlatformID()));
pComp->GetFunction("AddPostData")->sig_function(&vPostData);
VariantList v;
string url = "www.dinknetwork.com";
uint32 port = 80;
//GetApp()->GetServerInfo(url, port);
v.m_variant[0].Set(url);
v.m_variant[1].Set(port);
v.m_variant[2].Set("api");
v.m_variant[3].Set(uint32(NetHTTP::END_OF_DATA_SIGNAL_HTTP)); //need this for it to detect a disconnect instead of the weird RTsoft symbol
pComp->GetFunction("Init")->sig_function(&v);
pComp->GetFunction("OnError")->sig_function.connect(&OnDownloadError);
pComp->GetFunction("OnFinish")->sig_function.connect(&OnDownloadHTTPFinish);
Entity *pEnt = ShowScoreMessage(pMenu, "`6Downloading dmod data");
EntityComponent *pTyper = pEnt->AddComponent(new TyperComponent);
pTyper->GetVar("text")->Set("..........");
pTyper->GetVar("speedMS")->Set(uint32(50));
//KillScores();
}
void BrowseOnPostIntroTransition(VariantList *pVList)
{
Entity *pBG = pVList->Get(0).GetEntity();
//fresh download or use cached data?
if (g_dmodData.empty())
{
DownloadDMODList(pBG);
}
else
{
//use what we got
BrowseMenuAddScrollContent(pBG, NULL);
}
//CreateQuickTipFirstTimeOnly(pBG, "interface/iphone/quicktip_dmod.rttex", false);
}
Entity * BrowseMenuCreate( Entity *pParentEnt )
{
//GetMessageManager()->SendGame(MESSAGE_TYPE_PLAY_MUSIC, "audio/title.mp3", 200);
GetBaseApp()->ClearError();
Entity *pBG = NULL;
if (IsLargeScreen())
{
pBG = CreateOverlayEntity(pParentEnt, "BrowseMenu", ReplaceWithDeviceNameInFileName("interface/iphone/bkgd_stone.rttex"), 0,0);
} else
{
//pBG = CreateOverlayEntity(pParentEnt, "BrowseMenu", "interface/iphone/dmod_bg.rttex", 0,0);
pBG = CreateOverlayEntity(pParentEnt, "BrowseMenu", ReplaceWithDeviceNameInFileName("interface/iphone/bkgd_stone.rttex"), 0,0);
}
pBG->SetName("BrowseMenu");
AddFocusIfNeeded(pBG, true, 500);
pBG->AddComponent(new FocusRenderComponent);
CL_Vec2f vTextAreaPos = iPhoneMap(2,10);
float offsetFromBottom = iPhoneMapY(42);
float offsetFromRight = iPhoneMapY(0);
CL_Vec2f vTextAreaBounds = (GetScreenSize()- CL_Vec2f(offsetFromRight,offsetFromBottom))-vTextAreaPos;
Entity *pScroll = pBG->AddEntity(new Entity("scroll"));
pScroll->GetVar("pos2d")->Set(vTextAreaPos);
pScroll->GetVar("size2d")->Set(vTextAreaBounds);
pScroll->AddComponent(new TouchHandlerComponent);
/*
Entity *pLabel = CreateTextLabelEntity(pBG, "scanning", GetScreenSizeXf()/2, GetScreenSizeYf()/2, "Updating add-on browse list...");
SetAlignmentEntity(pLabel, ALIGNMENT_CENTER);
FadeOutAndKillEntity(pLabel, true, 300, 501);
*/
EntityComponent *pFilter = pScroll->AddComponent(new FilterInputComponent);
EntityComponent *pScrollComp = pScroll->AddComponent(new ScrollComponent);
EntityComponent *pScrollBarComp = pScroll->AddComponent(new ScrollBarRenderComponent); //also let's add a visual way to see the scroller position
//pScroll->GetVar("color")->Set(MAKE_RGBA(61,155, 193, 255));
Entity *pScrollChild = pScroll->AddEntity(new Entity("scroll_child"));
pScrollComp->GetVar("fingerTracking")->Set(uint32(1));
//EntityComponent *pClip = pScroll->AddComponent(new RenderClipComponent);
//pClip->GetVar("clipMode")->Set(uint32(RenderClipComponent::CLIP_MODE_BOTTOM));
Entity *pOverlay = CreateOverlayEntity(pBG, "", ReplaceWithDeviceNameInFileName("interface/iphone/bg_stone_overlay.rttex"), 0, GetScreenSizeYf());
SetAlignmentEntity(pOverlay, ALIGNMENT_DOWN_LEFT);
// ZoomFromPositionEntity(pBG, CL_Vec2f(0, -GetScreenSizeYf()), 500);
//the continue button
Entity *pEnt;
//pEnt = CreateOverlayRectEntity(pBG, CL_Rectf(0, GetScreenSizeYf()-offsetFromBottom, GetScreenSizeXf(), 320), MAKE_RGBA(0,0,0,100));
eFont fontID = FONT_SMALL;
pEnt = CreateTextButtonEntity(pBG, "Back", iPhoneMapX(25), iPhoneMapY(BACK_BUTTON_Y), "Back", false);
pEnt->GetFunction("OnButtonSelected")->sig_function.connect(&BrowseMenuOnSelect);
SetupTextEntity(pEnt, fontID);
AddHotKeyToButton(pEnt, VIRTUAL_KEY_BACK);
SlideScreen(pBG, true, 500);
pBG->GetFunction("OnPostIntroTransition")->sig_function.connect(&BrowseOnPostIntroTransition);
VariantList vList(pBG, string(""));
GetMessageManager()->CallEntityFunction(pBG, 500, "OnPostIntroTransition", &vList);
return pBG;
}