parent
ec92e7683c
commit
65e1700014
@ -0,0 +1,7 @@ |
|||||||
|
#pragma once |
||||||
|
enum ActionID{ |
||||||
|
RELOAD_FILE_DIALOG, |
||||||
|
CHANGE_LOAD_PATH, |
||||||
|
GO_BACK, |
||||||
|
SELECTED_FOLDER, |
||||||
|
}; |
@ -0,0 +1,141 @@ |
|||||||
|
#define OLC_PGE_APPLICATION |
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
#define OLC_PGEX_POPUPMENU |
||||||
|
#include "olcPGEX_PopupMenu.h" |
||||||
|
#define OLC_PGEX_SHNFile |
||||||
|
#include "SHNFileDecryptor.h" |
||||||
|
#include "OpenFileDialog.h" |
||||||
|
#include "ActionIDs.h" |
||||||
|
#include "State.h" |
||||||
|
#include "ItemEditor.h" |
||||||
|
#include "olcUTIL_DataFile.h" |
||||||
|
|
||||||
|
class FiestaOnlineEditor : public olc::PixelGameEngine |
||||||
|
{ |
||||||
|
popup::Menu menu; |
||||||
|
popup::Manager manager; |
||||||
|
Sprite*sprMenu; |
||||||
|
OpenFileDialog dialog; |
||||||
|
std::list<std::string>selectedPath; |
||||||
|
float lastPressedTime=0; |
||||||
|
State appState=LOADFOLDER; |
||||||
|
ItemEditor itemEditor; |
||||||
|
utils::datafile config; |
||||||
|
|
||||||
|
public: |
||||||
|
FiestaOnlineEditor() |
||||||
|
{ |
||||||
|
// Name your application
|
||||||
|
sAppName = "Fiesta Online Editor"; |
||||||
|
} |
||||||
|
|
||||||
|
public: |
||||||
|
bool OnUserCreate() override |
||||||
|
{ |
||||||
|
menu.SetTable(1,6); |
||||||
|
menu["Save"].Enable(false); |
||||||
|
menu["Items"].SetTable(1,4).Enable(false); |
||||||
|
menu["Items"]["Item Editor"]; |
||||||
|
menu["Items"]["Drop Groups"]; |
||||||
|
menu["Items"]["Icons"]; |
||||||
|
menu["Mobs"].Enable(false); |
||||||
|
menu["Abilities"].Enable(false); |
||||||
|
menu["Buffs"].Enable(false); |
||||||
|
menu["Reload"].SetID(RELOAD_FILE_DIALOG); |
||||||
|
menu.Build(); |
||||||
|
sprMenu = new Sprite("assets/RetroMenu.png"); |
||||||
|
|
||||||
|
selectedPath.push_back(""); |
||||||
|
|
||||||
|
utils::datafile::Read(config,"assets/program.txt"); |
||||||
|
if(config.HasProperty("DefaultPath")){ |
||||||
|
itemEditor.Load(config["DefaultPath"].GetString()); |
||||||
|
} else { |
||||||
|
manager.Open(&dialog.GetMenu()); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool OnUserUpdate(float fElapsedTime) override |
||||||
|
{ |
||||||
|
Clear(olc::VERY_DARK_GREEN); |
||||||
|
lastPressedTime=std::max(0.f,lastPressedTime-fElapsedTime); |
||||||
|
if (GetKey(UP).bHeld&&lastPressedTime==0){ |
||||||
|
if(GetKey(UP).bPressed){ |
||||||
|
lastPressedTime=0.2; |
||||||
|
} else { |
||||||
|
lastPressedTime=0.03; |
||||||
|
} |
||||||
|
manager.OnUp(); |
||||||
|
} |
||||||
|
if (GetKey(DOWN).bHeld&&lastPressedTime==0){ |
||||||
|
if(GetKey(DOWN).bPressed){ |
||||||
|
lastPressedTime=0.2; |
||||||
|
} else { |
||||||
|
lastPressedTime=0.03; |
||||||
|
} |
||||||
|
manager.OnDown(); |
||||||
|
} |
||||||
|
if (GetKey(LEFT).bHeld&&lastPressedTime==0){ |
||||||
|
if(GetKey(LEFT).bPressed){ |
||||||
|
lastPressedTime=0.2; |
||||||
|
} else { |
||||||
|
lastPressedTime=0.03; |
||||||
|
} |
||||||
|
manager.OnLeft(); |
||||||
|
} |
||||||
|
if (GetKey(RIGHT).bHeld&&lastPressedTime==0){ |
||||||
|
if(GetKey(RIGHT).bPressed){ |
||||||
|
lastPressedTime=0.2; |
||||||
|
} else { |
||||||
|
lastPressedTime=0.03; |
||||||
|
} |
||||||
|
manager.OnRight(); |
||||||
|
} |
||||||
|
if (GetKey(X).bPressed){ |
||||||
|
if(appState!=LOADFOLDER){ |
||||||
|
manager.OnBack(); |
||||||
|
} |
||||||
|
} |
||||||
|
if (GetKey(Z).bPressed){ |
||||||
|
popup::Menu*selected; |
||||||
|
if((selected=manager.OnConfirm())!=nullptr){ |
||||||
|
std::string sLastAction =
|
||||||
|
"Selected: " + selected->GetName() +
|
||||||
|
" ID: " + std::to_string(selected->GetID()); |
||||||
|
std::cout<<sLastAction<<std::endl; |
||||||
|
switch(selected->GetID()){ |
||||||
|
case RELOAD_FILE_DIALOG:{ |
||||||
|
}break; |
||||||
|
case CHANGE_LOAD_PATH:{ |
||||||
|
selectedPath.push_back(selectedPath.back()+"/"+selected->GetName()); |
||||||
|
dialog.ChangePath(selectedPath.back()); |
||||||
|
manager.Open(&dialog.GetMenu()); |
||||||
|
}break; |
||||||
|
case GO_BACK:{ |
||||||
|
selectedPath.pop_back(); |
||||||
|
dialog.ChangePath(selectedPath.back()); |
||||||
|
manager.Open(&dialog.GetMenu()); |
||||||
|
}break; |
||||||
|
case SELECTED_FOLDER:{ |
||||||
|
std::cout<<selectedPath.back()<<std::endl; |
||||||
|
config["DefaultPath"].SetString(selectedPath.back()); |
||||||
|
utils::datafile::Write(config,"assets/program.txt"); |
||||||
|
itemEditor.Load(selectedPath.back()); |
||||||
|
}break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
manager.Draw(sprMenu,{64,64}); |
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
FiestaOnlineEditor demo; |
||||||
|
if (demo.Construct(960, 360, 2, 2)) |
||||||
|
demo.Start(); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,151 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||||
|
<ItemGroup Label="ProjectConfigurations"> |
||||||
|
<ProjectConfiguration Include="Debug|Win32"> |
||||||
|
<Configuration>Debug</Configuration> |
||||||
|
<Platform>Win32</Platform> |
||||||
|
</ProjectConfiguration> |
||||||
|
<ProjectConfiguration Include="Release|Win32"> |
||||||
|
<Configuration>Release</Configuration> |
||||||
|
<Platform>Win32</Platform> |
||||||
|
</ProjectConfiguration> |
||||||
|
<ProjectConfiguration Include="Debug|x64"> |
||||||
|
<Configuration>Debug</Configuration> |
||||||
|
<Platform>x64</Platform> |
||||||
|
</ProjectConfiguration> |
||||||
|
<ProjectConfiguration Include="Release|x64"> |
||||||
|
<Configuration>Release</Configuration> |
||||||
|
<Platform>x64</Platform> |
||||||
|
</ProjectConfiguration> |
||||||
|
</ItemGroup> |
||||||
|
<PropertyGroup Label="Globals"> |
||||||
|
<VCProjectVersion>16.0</VCProjectVersion> |
||||||
|
<Keyword>Win32Proj</Keyword> |
||||||
|
<ProjectGuid>{75409de7-0ab8-4ca1-b323-c6f433d8e13f}</ProjectGuid> |
||||||
|
<RootNamespace>FiestaOnlineEditor</RootNamespace> |
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> |
||||||
|
</PropertyGroup> |
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> |
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> |
||||||
|
<ConfigurationType>Application</ConfigurationType> |
||||||
|
<UseDebugLibraries>true</UseDebugLibraries> |
||||||
|
<PlatformToolset>v143</PlatformToolset> |
||||||
|
<CharacterSet>Unicode</CharacterSet> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> |
||||||
|
<ConfigurationType>Application</ConfigurationType> |
||||||
|
<UseDebugLibraries>false</UseDebugLibraries> |
||||||
|
<PlatformToolset>v143</PlatformToolset> |
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization> |
||||||
|
<CharacterSet>Unicode</CharacterSet> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> |
||||||
|
<ConfigurationType>Application</ConfigurationType> |
||||||
|
<UseDebugLibraries>true</UseDebugLibraries> |
||||||
|
<PlatformToolset>v143</PlatformToolset> |
||||||
|
<CharacterSet>Unicode</CharacterSet> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> |
||||||
|
<ConfigurationType>Application</ConfigurationType> |
||||||
|
<UseDebugLibraries>false</UseDebugLibraries> |
||||||
|
<PlatformToolset>v143</PlatformToolset> |
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization> |
||||||
|
<CharacterSet>Unicode</CharacterSet> |
||||||
|
</PropertyGroup> |
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> |
||||||
|
<ImportGroup Label="ExtensionSettings"> |
||||||
|
</ImportGroup> |
||||||
|
<ImportGroup Label="Shared"> |
||||||
|
</ImportGroup> |
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
||||||
|
</ImportGroup> |
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> |
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
||||||
|
</ImportGroup> |
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> |
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
||||||
|
</ImportGroup> |
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> |
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
||||||
|
</ImportGroup> |
||||||
|
<PropertyGroup Label="UserMacros" /> |
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |
||||||
|
<ClCompile> |
||||||
|
<WarningLevel>Level3</WarningLevel> |
||||||
|
<SDLCheck>true</SDLCheck> |
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||||
|
<ConformanceMode>true</ConformanceMode> |
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard> |
||||||
|
</ClCompile> |
||||||
|
<Link> |
||||||
|
<SubSystem>Console</SubSystem> |
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation> |
||||||
|
</Link> |
||||||
|
</ItemDefinitionGroup> |
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> |
||||||
|
<ClCompile> |
||||||
|
<WarningLevel>Level3</WarningLevel> |
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking> |
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions> |
||||||
|
<SDLCheck>true</SDLCheck> |
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||||
|
<ConformanceMode>true</ConformanceMode> |
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard> |
||||||
|
</ClCompile> |
||||||
|
<Link> |
||||||
|
<SubSystem>Console</SubSystem> |
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding> |
||||||
|
<OptimizeReferences>true</OptimizeReferences> |
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation> |
||||||
|
</Link> |
||||||
|
</ItemDefinitionGroup> |
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> |
||||||
|
<ClCompile> |
||||||
|
<WarningLevel>Level3</WarningLevel> |
||||||
|
<SDLCheck>true</SDLCheck> |
||||||
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||||
|
<ConformanceMode>true</ConformanceMode> |
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard> |
||||||
|
</ClCompile> |
||||||
|
<Link> |
||||||
|
<SubSystem>Console</SubSystem> |
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation> |
||||||
|
</Link> |
||||||
|
</ItemDefinitionGroup> |
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> |
||||||
|
<ClCompile> |
||||||
|
<WarningLevel>Level3</WarningLevel> |
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking> |
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions> |
||||||
|
<SDLCheck>true</SDLCheck> |
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||||
|
<ConformanceMode>true</ConformanceMode> |
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard> |
||||||
|
</ClCompile> |
||||||
|
<Link> |
||||||
|
<SubSystem>Console</SubSystem> |
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding> |
||||||
|
<OptimizeReferences>true</OptimizeReferences> |
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation> |
||||||
|
</Link> |
||||||
|
</ItemDefinitionGroup> |
||||||
|
<ItemGroup> |
||||||
|
<ClInclude Include="ActionIDs.h" /> |
||||||
|
<ClInclude Include="ItemEditor.h" /> |
||||||
|
<ClInclude Include="olcPGEX_PopupMenu.h" /> |
||||||
|
<ClInclude Include="olcPixelGameEngine.h" /> |
||||||
|
<ClInclude Include="olcUTIL_DataFile.h" /> |
||||||
|
<ClInclude Include="OpenFileDialog.h" /> |
||||||
|
<ClInclude Include="SHNFileDecryptor.h" /> |
||||||
|
<ClInclude Include="State.h" /> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<ClCompile Include="FiestaOnlineEditor.cpp" /> |
||||||
|
<ClCompile Include="ItemEditor.cpp" /> |
||||||
|
<ClCompile Include="OpenFileDialog.cpp" /> |
||||||
|
</ItemGroup> |
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> |
||||||
|
<ImportGroup Label="ExtensionTargets"> |
||||||
|
</ImportGroup> |
||||||
|
</Project> |
@ -0,0 +1,7 @@ |
|||||||
|
#include "ItemEditor.h" |
||||||
|
|
||||||
|
ItemEditor::ItemEditor(){} |
||||||
|
|
||||||
|
void ItemEditor::Load(std::string basePath){ |
||||||
|
ItemInfo.Load(basePath+"/ItemInfo.shn"); |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
#pragma once |
||||||
|
#include "SHNFileDecryptor.h" |
||||||
|
class ItemEditor{ |
||||||
|
SHNFile ItemInfo,ItemInfoServer,ItemViewInfo; |
||||||
|
public: |
||||||
|
ItemEditor(); |
||||||
|
void Load(std::string basePath); |
||||||
|
}; |
@ -0,0 +1,34 @@ |
|||||||
|
#include "OpenFileDialog.h" |
||||||
|
#include "ActionIDs.h" |
||||||
|
|
||||||
|
OpenFileDialog::OpenFileDialog(){ |
||||||
|
BuildMenu(); |
||||||
|
} |
||||||
|
popup::Menu&OpenFileDialog::GetMenu(){ |
||||||
|
return menu; |
||||||
|
} |
||||||
|
void OpenFileDialog::ChangePath(std::string newPath){ |
||||||
|
if(newPath==""){ |
||||||
|
path="/"; //This is an edge case. We can't use the empty string to indicate a valid path.
|
||||||
|
} else { |
||||||
|
path=newPath; |
||||||
|
} |
||||||
|
BuildMenu(); |
||||||
|
} |
||||||
|
|
||||||
|
void OpenFileDialog::BuildMenu(){ |
||||||
|
menu.Clear(); |
||||||
|
menu["Select "+path].SetID(SELECTED_FOLDER); |
||||||
|
menu[""].Enable(false); |
||||||
|
if(path!="/"){ |
||||||
|
menu[".."].SetID(GO_BACK); |
||||||
|
} |
||||||
|
for(auto&dir:std::filesystem::directory_iterator(path)){ |
||||||
|
if(dir.is_directory()){ |
||||||
|
std::filesystem::path filename = dir.path().filename(); |
||||||
|
menu[filename.string()].SetID(CHANGE_LOAD_PATH); |
||||||
|
} |
||||||
|
} |
||||||
|
menu.SetTable(1,std::min(20,menu.GetChildrenCount())); |
||||||
|
menu.Build(); |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
#pragma once |
||||||
|
#include "olcPGEX_PopupMenu.h" |
||||||
|
#include <filesystem> |
||||||
|
|
||||||
|
class OpenFileDialog{ |
||||||
|
popup::Menu menu; |
||||||
|
std::string path="/"; |
||||||
|
public: |
||||||
|
OpenFileDialog(); |
||||||
|
popup::Menu&GetMenu(); |
||||||
|
void ChangePath(std::string newPath); |
||||||
|
void BuildMenu(); |
||||||
|
}; |
@ -0,0 +1,535 @@ |
|||||||
|
#pragma once |
||||||
|
#include <vector> |
||||||
|
#include <cstddef> |
||||||
|
#include <fstream> |
||||||
|
#include <iostream> |
||||||
|
#include <string> |
||||||
|
#include <chrono> |
||||||
|
#include <sstream> |
||||||
|
|
||||||
|
//typedef std::std::byte std::byte;
|
||||||
|
typedef char sbyte; |
||||||
|
|
||||||
|
class SHNFile{ |
||||||
|
std::vector<std::byte>ReadBytes(std::ifstream&file); |
||||||
|
std::vector<std::byte>ReadBytes(std::ifstream&file,int bytes); |
||||||
|
void WriteBytes(std::ofstream&file,std::vector<std::byte>&data); |
||||||
|
int ReadInt32(std::ifstream&file); |
||||||
|
void Encrypt(); |
||||||
|
void Decrypt(); |
||||||
|
std::vector<std::byte>&ReadBytes(int bytes); |
||||||
|
std::byte ReadByte(); |
||||||
|
void WriteByte(std::ofstream&f,std::byte b); |
||||||
|
void WriteSByte(std::ofstream&f,sbyte b); |
||||||
|
uint16_t ReadUInt16(); |
||||||
|
void WriteUInt16(std::ofstream&f,uint16_t val); |
||||||
|
int16_t ReadInt16(); |
||||||
|
void WriteInt16(std::ofstream&f,int16_t val); |
||||||
|
uint32_t ReadUInt32(); |
||||||
|
void WriteUInt32(std::ofstream&f,uint32_t val); |
||||||
|
int Read(); |
||||||
|
int32_t ReadInt32(); |
||||||
|
void WriteInt32(std::ofstream&f,int32_t val); |
||||||
|
std::string ReadString(int bytes); |
||||||
|
void WriteString(std::ofstream&f,std::string str,int bytes); |
||||||
|
float ReadSingle(); |
||||||
|
void WriteSingle(std::ofstream&f,float n); |
||||||
|
sbyte ReadSByte(); |
||||||
|
std::string ReadString(); |
||||||
|
uint32_t GetRecordLength(); |
||||||
|
public: |
||||||
|
enum class DataType:int{ |
||||||
|
BYTE=1, |
||||||
|
SBYTE=7, |
||||||
|
UINT16=2, |
||||||
|
INT16=6, |
||||||
|
UINT32=3, |
||||||
|
INT32=8, |
||||||
|
FLOAT=4, |
||||||
|
STRING=5 |
||||||
|
}; |
||||||
|
struct Data{ |
||||||
|
std::shared_ptr<void>data; |
||||||
|
DataType type=DataType::BYTE; |
||||||
|
Data(std::byte b); |
||||||
|
Data(sbyte b); |
||||||
|
Data(uint16_t n); |
||||||
|
Data(int16_t n); |
||||||
|
Data(uint32_t n); |
||||||
|
Data(int32_t n); |
||||||
|
Data(float n); |
||||||
|
Data(std::string str); |
||||||
|
template<typename T> |
||||||
|
T Get(); |
||||||
|
template<typename T> |
||||||
|
void Set(T b); |
||||||
|
std::string GetDisplayText(); |
||||||
|
}; |
||||||
|
private: |
||||||
|
friend std::ostream&operator<<(std::ostream&out,SHNFile::Data&d){ |
||||||
|
out<<d.GetDisplayText(); |
||||||
|
return out; |
||||||
|
} |
||||||
|
struct Column{ |
||||||
|
std::string name; |
||||||
|
uint32_t type=0; |
||||||
|
int length=0; |
||||||
|
}; |
||||||
|
int marker=0; |
||||||
|
std::vector<std::byte>cryptHeader; |
||||||
|
std::vector<std::byte>data,rawData; |
||||||
|
uint32_t header=0,recordCount=0,defaultRecordLength=0,columnCount=0; |
||||||
|
std::vector<Column>columns; |
||||||
|
std::vector<std::vector<Data>>contents; |
||||||
|
std::string filename; |
||||||
|
std::vector<std::byte>readArr; |
||||||
|
std::byte*fileMarker=0; |
||||||
|
public: |
||||||
|
void Load(std::string file); |
||||||
|
void Save(); |
||||||
|
void Write(int row,int col,std::byte val); |
||||||
|
void Write(int row,int col,sbyte val); |
||||||
|
void Write(int row,int col,int16_t val); |
||||||
|
void Write(int row,int col,uint16_t val); |
||||||
|
void Write(int row,int col,int32_t val); |
||||||
|
void Write(int row,int col,uint32_t val); |
||||||
|
void Write(int row,int col,float val); |
||||||
|
void Write(int row,int col,std::string val); |
||||||
|
const SHNFile::Data Get(int row,int col)const; |
||||||
|
SHNFile(); |
||||||
|
}; |
||||||
|
#ifdef OLC_PGEX_SHNFile |
||||||
|
std::vector<std::byte>SHNFile::ReadBytes(std::ifstream&file){ |
||||||
|
std::vector<std::byte>byteArr; |
||||||
|
while(!file.eof()){ |
||||||
|
byteArr.push_back(std::byte(file.get())); |
||||||
|
} |
||||||
|
return byteArr; |
||||||
|
} |
||||||
|
std::vector<std::byte>SHNFile::ReadBytes(std::ifstream&file,int bytes){ |
||||||
|
std::vector<std::byte>byteArr; |
||||||
|
for(int i=0;i<bytes;i++){ |
||||||
|
if(!file.eof()){ |
||||||
|
byteArr.push_back(std::byte(file.get())); |
||||||
|
} else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return byteArr; |
||||||
|
} |
||||||
|
void SHNFile::WriteBytes(std::ofstream&file,std::vector<std::byte>&data){ |
||||||
|
for(int i=0;i<data.size();i++){ |
||||||
|
file<<unsigned char(data[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
int SHNFile::ReadInt32(std::ifstream&file){ |
||||||
|
std::vector<std::byte>intBytes=ReadBytes(file,4); |
||||||
|
int numb = int(intBytes[3])<<24|int(intBytes[2])<<16|int(intBytes[1])<<8|int(intBytes[0]); |
||||||
|
return numb; |
||||||
|
} |
||||||
|
void SHNFile::Encrypt(){ |
||||||
|
Decrypt(); |
||||||
|
} |
||||||
|
void SHNFile::Decrypt(){ |
||||||
|
std::byte num = std::byte(data.size()); |
||||||
|
for(int i=data.size()-1;i>=0;i--){ |
||||||
|
data[i] = std::byte(data[i]^num); |
||||||
|
std::byte num3 = std::byte(i); |
||||||
|
num3 = std::byte(num3&std::byte(15)); |
||||||
|
num3 = std::byte(int(num3)+0x55); |
||||||
|
num3 = std::byte(num3 ^ (std::byte((int(std::byte(i))*11)))); |
||||||
|
num3 = std::byte(num3^num); |
||||||
|
num3 = std::byte(int(num3)^170); |
||||||
|
num = num3; |
||||||
|
} |
||||||
|
} |
||||||
|
std::vector<std::byte>&SHNFile::ReadBytes(int bytes){ |
||||||
|
readArr.clear(); |
||||||
|
std::copy(data.begin()+marker,data.begin()+marker+bytes,std::back_inserter(readArr)); |
||||||
|
marker+=bytes; |
||||||
|
return readArr; |
||||||
|
} |
||||||
|
std::byte SHNFile::ReadByte(){ |
||||||
|
std::vector<std::byte>&b=ReadBytes(1); |
||||||
|
if(b.size()>0){ |
||||||
|
return b[0]; |
||||||
|
} else { |
||||||
|
return std::byte(0); |
||||||
|
} |
||||||
|
} |
||||||
|
void SHNFile::WriteByte(std::ofstream&f,std::byte b){ |
||||||
|
f<<unsigned char(b); |
||||||
|
} |
||||||
|
void SHNFile::WriteSByte(std::ofstream&f,sbyte b){ |
||||||
|
f<<char(b); |
||||||
|
} |
||||||
|
uint16_t SHNFile::ReadUInt16(){ |
||||||
|
std::vector<std::byte>&intBytes=ReadBytes(2); |
||||||
|
uint16_t numb = uint16_t(intBytes[1])<<8|uint16_t(intBytes[0]); |
||||||
|
return numb; |
||||||
|
} |
||||||
|
void SHNFile::WriteUInt16(std::ofstream&f,uint16_t val){ |
||||||
|
f<<unsigned char(val&0xFF)<<unsigned char((val>>8)&0xFF); |
||||||
|
} |
||||||
|
int16_t SHNFile::ReadInt16(){ |
||||||
|
std::vector<std::byte>&intBytes=ReadBytes(2); |
||||||
|
int16_t numb = int16_t(intBytes[1])<<8|int16_t(intBytes[0]); |
||||||
|
return numb; |
||||||
|
} |
||||||
|
void SHNFile::WriteInt16(std::ofstream&f,int16_t val){ |
||||||
|
f<<unsigned char(val&0xFF)<<unsigned char((val>>8)&0xFF); |
||||||
|
} |
||||||
|
uint32_t SHNFile::ReadUInt32(){ |
||||||
|
std::vector<std::byte>&intBytes=ReadBytes(4); |
||||||
|
uint32_t numb = uint32_t(intBytes[3])<<24|uint32_t(intBytes[2])<<16|uint32_t(intBytes[1])<<8|uint32_t(intBytes[0]); |
||||||
|
return numb; |
||||||
|
} |
||||||
|
void SHNFile::WriteUInt32(std::ofstream&f,uint32_t val){ |
||||||
|
f<<unsigned char(val&0xFF)<<unsigned char((val>>8)&0xFF)<<unsigned char((val>>16)&0xFF)<<unsigned char((val>>24)&0xFF); |
||||||
|
} |
||||||
|
int SHNFile::Read(){ |
||||||
|
std::vector<std::byte>&intBytes=ReadBytes(4); |
||||||
|
int numb = int(intBytes[3])<<24|int(intBytes[2])<<16|int(intBytes[1])<<8|int(intBytes[0]); |
||||||
|
return numb; |
||||||
|
} |
||||||
|
int32_t SHNFile::ReadInt32(){ |
||||||
|
std::vector<std::byte>&intBytes=ReadBytes(4); |
||||||
|
int32_t numb = int32_t(intBytes[3])<<24|int32_t(intBytes[2])<<16|int32_t(intBytes[1])<<8|uint32_t(intBytes[0]); |
||||||
|
return numb; |
||||||
|
} |
||||||
|
void SHNFile::WriteInt32(std::ofstream&f,int32_t val){ |
||||||
|
f<<unsigned char(val&0xFF)<<unsigned char((val>>8)&0xFF)<<unsigned char((val>>16)&0xFF)<<unsigned char((val>>24)&0xFF); |
||||||
|
} |
||||||
|
std::string SHNFile::ReadString(int bytes){ |
||||||
|
std::vector<std::byte>&strBytes=ReadBytes(bytes); |
||||||
|
std::string str=""; |
||||||
|
for(int i=0;i<strBytes.size();i++){ |
||||||
|
if(strBytes[i]!=std::byte(0)){ |
||||||
|
str+=unsigned char(strBytes[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
return str; |
||||||
|
} |
||||||
|
void SHNFile::WriteString(std::ofstream&f,std::string str,int bytes){ |
||||||
|
for(int i=0;i<bytes;i++){ |
||||||
|
if(i<str.length()){ |
||||||
|
f<<unsigned char(str[i]); |
||||||
|
} else { |
||||||
|
f<<unsigned char(0x00); |
||||||
|
} |
||||||
|
} |
||||||
|
if(bytes==-1){ |
||||||
|
//We use this to append a 0 for unknown length strings (of the shn file)
|
||||||
|
f<<unsigned char(0x00); |
||||||
|
} |
||||||
|
} |
||||||
|
float SHNFile::ReadSingle(){ |
||||||
|
std::vector<std::byte>strBytes=ReadBytes(4); |
||||||
|
std::byte bytes[]={strBytes[0],strBytes[1],strBytes[2],strBytes[3]}; |
||||||
|
float f; |
||||||
|
memcpy(&f,&bytes,4); |
||||||
|
return f; |
||||||
|
} |
||||||
|
void SHNFile::WriteSingle(std::ofstream&f,float n){ |
||||||
|
std::byte bytes[4]={}; |
||||||
|
memcpy(&n,&bytes,4); |
||||||
|
for(int i=0;i<4;i++){ |
||||||
|
f<<unsigned char(bytes[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
sbyte SHNFile::ReadSByte(){ |
||||||
|
return sbyte(ReadBytes(1)[0]); |
||||||
|
} |
||||||
|
std::string SHNFile::ReadString(){ |
||||||
|
std::string str=""; |
||||||
|
while(true){ |
||||||
|
std::vector<std::byte>byteArr=ReadBytes(1); |
||||||
|
if(byteArr.size()>0&&byteArr[0]!=std::byte(0)){ |
||||||
|
str+=unsigned char(byteArr[0]); |
||||||
|
}else{ |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return str; |
||||||
|
} |
||||||
|
uint32_t SHNFile::GetRecordLength(){ |
||||||
|
uint32_t start=2; |
||||||
|
for(Column&col:columns){ |
||||||
|
start+=uint32_t(col.length); |
||||||
|
} |
||||||
|
return start; |
||||||
|
} |
||||||
|
SHNFile::Data::Data(std::byte b){ |
||||||
|
std::shared_ptr<std::byte>ptr=std::make_shared<std::byte>(std::byte(b)); |
||||||
|
data=ptr; |
||||||
|
type=DataType::BYTE; |
||||||
|
} |
||||||
|
SHNFile::Data::Data(sbyte b){ |
||||||
|
std::shared_ptr<sbyte>ptr=std::make_shared<sbyte>(sbyte(b)); |
||||||
|
data=ptr; |
||||||
|
type=DataType::SBYTE; |
||||||
|
} |
||||||
|
SHNFile::Data::Data(uint16_t n){ |
||||||
|
std::shared_ptr<uint16_t>ptr=std::make_shared<uint16_t>(uint16_t(n)); |
||||||
|
data=ptr; |
||||||
|
type=DataType::UINT16; |
||||||
|
} |
||||||
|
SHNFile::Data::Data(int16_t n){ |
||||||
|
std::shared_ptr<int16_t>ptr=std::make_shared<int16_t>(int16_t(n)); |
||||||
|
data=ptr; |
||||||
|
type=DataType::INT16; |
||||||
|
} |
||||||
|
SHNFile::Data::Data(uint32_t n){ |
||||||
|
std::shared_ptr<uint32_t>ptr=std::make_shared<uint32_t>(uint32_t(n)); |
||||||
|
data=ptr; |
||||||
|
type=DataType::UINT32; |
||||||
|
} |
||||||
|
SHNFile::Data::Data(int32_t n){ |
||||||
|
std::shared_ptr<int32_t>ptr=std::make_shared<int32_t>(int32_t(n)); |
||||||
|
data=ptr; |
||||||
|
type=DataType::INT32; |
||||||
|
} |
||||||
|
SHNFile::Data::Data(float n){ |
||||||
|
std::shared_ptr<float>ptr=std::make_shared<float>(float(n)); |
||||||
|
data=ptr; |
||||||
|
type=DataType::FLOAT; |
||||||
|
} |
||||||
|
SHNFile::Data::Data(std::string str){ |
||||||
|
std::shared_ptr<std::string>ptr=std::make_shared<std::string>(str); |
||||||
|
data=ptr; |
||||||
|
type=DataType::STRING; |
||||||
|
} |
||||||
|
template <typename T> |
||||||
|
T SHNFile::Data::Get(){ |
||||||
|
return *std::static_pointer_cast<T>(data); |
||||||
|
} |
||||||
|
template<typename T> |
||||||
|
void SHNFile::Data::Set(T b){ |
||||||
|
data=std::make_shared<T>(b); |
||||||
|
} |
||||||
|
std::string SHNFile::Data::GetDisplayText(){ |
||||||
|
switch(type){ |
||||||
|
case DataType::BYTE:{ |
||||||
|
return std::to_string(int(Get<std::byte>())); |
||||||
|
}break; |
||||||
|
case DataType::SBYTE:{ |
||||||
|
return std::to_string(int(Get<sbyte>())); |
||||||
|
}break; |
||||||
|
case DataType::UINT16:{ |
||||||
|
return std::to_string(Get<uint16_t>()); |
||||||
|
}break; |
||||||
|
case DataType::INT16:{ |
||||||
|
return std::to_string(Get<int16_t>()); |
||||||
|
}break; |
||||||
|
case DataType::UINT32:{ |
||||||
|
return std::to_string(Get<uint32_t>()); |
||||||
|
}break; |
||||||
|
case DataType::INT32:{ |
||||||
|
return std::to_string(Get<int32_t>()); |
||||||
|
}break; |
||||||
|
case DataType::FLOAT:{ |
||||||
|
return std::to_string(Get<float>()); |
||||||
|
}break; |
||||||
|
case DataType::STRING:{ |
||||||
|
return Get<std::string>(); |
||||||
|
}break; |
||||||
|
} |
||||||
|
} |
||||||
|
void SHNFile::Load(std::string file){ |
||||||
|
header=recordCount=defaultRecordLength=columnCount=0; |
||||||
|
columns.clear(); |
||||||
|
contents.clear(); |
||||||
|
rawData.clear(); |
||||||
|
marker=0; |
||||||
|
std::chrono::time_point<std::chrono::high_resolution_clock>timer=std::chrono::high_resolution_clock::now(); |
||||||
|
filename=file; |
||||||
|
|
||||||
|
//FILE OPERATIONS!
|
||||||
|
std::ifstream f(filename,std::ios::binary); |
||||||
|
//Since we don't just read in the entire file raw, we have to do some additional work here as the header provides us with some basic info on how to decrypt this file.
|
||||||
|
//The backup itself needs all the data from the original file, so we're appending it to rawData as we continue reading it.
|
||||||
|
cryptHeader=ReadBytes(f,0x20); |
||||||
|
int readAmt=ReadInt32(f); |
||||||
|
rawData.push_back(std::byte(readAmt&0xFF)); |
||||||
|
rawData.push_back(std::byte((readAmt>>8)&0xFF)); |
||||||
|
rawData.push_back(std::byte((readAmt>>16)&0xFF)); |
||||||
|
rawData.push_back(std::byte((readAmt>>24)&0xFF)); |
||||||
|
data=ReadBytes(f,readAmt-0x24); |
||||||
|
std::copy(data.begin(),data.end(),std::back_inserter(rawData)); |
||||||
|
|
||||||
|
Decrypt(); |
||||||
|
header=ReadUInt32(); |
||||||
|
recordCount=ReadUInt32(); |
||||||
|
defaultRecordLength=ReadUInt32(); |
||||||
|
columnCount=ReadUInt32(); |
||||||
|
|
||||||
|
int num2=2; |
||||||
|
for(int i=0;i<columnCount;i++){ |
||||||
|
Column columnData; |
||||||
|
columnData.name=ReadString(0x30); |
||||||
|
columnData.type=ReadUInt32(); |
||||||
|
columnData.length=ReadInt32(); |
||||||
|
num2+=columnData.length; |
||||||
|
columns.push_back(columnData); |
||||||
|
} |
||||||
|
|
||||||
|
for(int i=0;i<recordCount;i++){ |
||||||
|
ReadUInt16(); |
||||||
|
std::vector<Data>row; |
||||||
|
for(int j=0;j<columns.size();j++){ |
||||||
|
switch(columns[j].type){ |
||||||
|
case 1: |
||||||
|
case 12: |
||||||
|
case 0x10:{ |
||||||
|
row.push_back(ReadByte()); |
||||||
|
}break; |
||||||
|
case 2:{ |
||||||
|
row.push_back(ReadUInt16()); |
||||||
|
}break; |
||||||
|
case 3: |
||||||
|
case 11: |
||||||
|
case 0x12: |
||||||
|
case 0x1b:{ |
||||||
|
row.push_back(ReadUInt32()); |
||||||
|
}break; |
||||||
|
case 5:{ |
||||||
|
row.push_back(ReadSingle()); |
||||||
|
}break; |
||||||
|
case 9: |
||||||
|
case 0x18:{ |
||||||
|
row.push_back(ReadString(columns[j].length)); |
||||||
|
}break; |
||||||
|
case 13: |
||||||
|
case 0x15:{ |
||||||
|
row.push_back(ReadInt16()); |
||||||
|
}break; |
||||||
|
case 20:{ |
||||||
|
row.push_back(ReadSByte()); |
||||||
|
}break; |
||||||
|
case 0x16:{ |
||||||
|
row.push_back(ReadInt32()); |
||||||
|
}break; |
||||||
|
case 0x1a:{ |
||||||
|
row.push_back(ReadString()); |
||||||
|
}break; |
||||||
|
} |
||||||
|
} |
||||||
|
contents.push_back(row); |
||||||
|
} |
||||||
|
std::chrono::duration<float>dur=std::chrono::high_resolution_clock::now()-timer; |
||||||
|
std::cout<<"Loaded "<<contents.size()<<" rows, "<<contents.size()*columnCount<<" columns, "<<dur.count()<<"s"<<std::endl; |
||||||
|
} |
||||||
|
void SHNFile::Save(){ |
||||||
|
std::ofstream fBackup(filename+".bak",std::ios::binary); |
||||||
|
std::cout<<"Saving a backup to "<<filename+".bak"<<std::endl; |
||||||
|
for(int i=0;i<rawData.size();i++){ |
||||||
|
fBackup<<unsigned char(rawData[i]); |
||||||
|
} |
||||||
|
/*//Decoded version only required for debugging.
|
||||||
|
std::ofstream fDecodedBackup(filename+"_decoded.bak",std::ios::binary); |
||||||
|
std::cout<<"Saving a backup to "<<filename+"_decoded.bak"<<std::endl; |
||||||
|
for(int i=0;i<data.size();i++){ |
||||||
|
fDecodedBackup<<unsigned char(data[i]); |
||||||
|
}*/ |
||||||
|
std::cout<<"Saving new file..."<<std::endl; |
||||||
|
std::ofstream f(filename,std::ios::binary); |
||||||
|
WriteUInt32(f,header); |
||||||
|
WriteUInt32(f,contents.size()); |
||||||
|
WriteUInt32(f,GetRecordLength()); |
||||||
|
WriteUInt32(f,columnCount); |
||||||
|
for(int i=0;i<columnCount;i++){ |
||||||
|
WriteString(f,columns[i].name,0x30); |
||||||
|
WriteUInt32(f,columns[i].type); |
||||||
|
WriteInt32(f,columns[i].length); |
||||||
|
} |
||||||
|
for(std::vector<Data>&row:contents){ |
||||||
|
std::streampos marker = f.tellp(); |
||||||
|
WriteUInt16(f,uint16_t(0)); |
||||||
|
int colNum=0; |
||||||
|
for(Data&col:row){ |
||||||
|
switch(columns[colNum].type){ |
||||||
|
case 1: |
||||||
|
case 12: |
||||||
|
case 0x10:{ |
||||||
|
WriteByte(f,col.Get<std::byte>()); |
||||||
|
}break; |
||||||
|
case 2:{ |
||||||
|
WriteUInt16(f,col.Get<uint16_t>()); |
||||||
|
}break; |
||||||
|
case 3: |
||||||
|
case 11: |
||||||
|
case 0x12: |
||||||
|
case 0x1b:{ |
||||||
|
WriteUInt32(f,col.Get<uint32_t>()); |
||||||
|
}break; |
||||||
|
case 5:{ |
||||||
|
WriteSingle(f,col.Get<float>()); |
||||||
|
}break; |
||||||
|
case 9: |
||||||
|
case 0x18:{ |
||||||
|
WriteString(f,std::string(col.Get<std::string>()),columns[colNum].length); |
||||||
|
}break; |
||||||
|
case 13: |
||||||
|
case 0x15:{ |
||||||
|
WriteInt16(f,col.Get<uint16_t>()); |
||||||
|
}break; |
||||||
|
case 20:{ |
||||||
|
WriteSByte(f,col.Get<sbyte>()); |
||||||
|
}break; |
||||||
|
case 0x16:{ |
||||||
|
WriteInt32(f,col.Get<int32_t>()); |
||||||
|
}break; |
||||||
|
case 0x1a:{ |
||||||
|
WriteString(f,col.Get<std::string>(),-1); |
||||||
|
}break; |
||||||
|
} |
||||||
|
colNum++; |
||||||
|
} |
||||||
|
std::streampos offset = f.tellp() - marker; |
||||||
|
std::streampos newMarker = f.tellp(); |
||||||
|
f.seekp(marker); |
||||||
|
WriteUInt16(f,uint16_t(offset)); |
||||||
|
f.seekp(newMarker); |
||||||
|
} |
||||||
|
f.close(); |
||||||
|
std::ifstream finishedFile(filename,std::ios::binary); |
||||||
|
data=ReadBytes(finishedFile); |
||||||
|
std::ofstream encryptedFile(filename,std::ios::binary); |
||||||
|
std::cout<<"Encrypting..."<<std::endl; |
||||||
|
Encrypt(); |
||||||
|
WriteBytes(encryptedFile,cryptHeader); |
||||||
|
WriteInt32(encryptedFile,int32_t(data.size()+0x24)); |
||||||
|
WriteBytes(encryptedFile,data); |
||||||
|
std::cout<<"File "<<filename<<" Saved!"<<std::endl; |
||||||
|
} |
||||||
|
const SHNFile::Data SHNFile::Get(int row,int col)const{ |
||||||
|
return contents[row][col];
|
||||||
|
}; |
||||||
|
void SHNFile::Write(int row,int col,std::byte val){ |
||||||
|
contents[row][col].Set<std::byte>(val); |
||||||
|
} |
||||||
|
void SHNFile::Write(int row,int col,sbyte val){ |
||||||
|
contents[row][col].Set<sbyte>(val); |
||||||
|
} |
||||||
|
void SHNFile::Write(int row,int col,int16_t val){ |
||||||
|
contents[row][col].Set<int16_t>(val); |
||||||
|
} |
||||||
|
void SHNFile::Write(int row,int col,uint16_t val){ |
||||||
|
contents[row][col].Set<uint16_t>(val); |
||||||
|
} |
||||||
|
void SHNFile::Write(int row,int col,int32_t val){ |
||||||
|
contents[row][col].Set<int32_t>(val); |
||||||
|
} |
||||||
|
void SHNFile::Write(int row,int col,uint32_t val){ |
||||||
|
contents[row][col].Set<uint32_t>(val); |
||||||
|
} |
||||||
|
void SHNFile::Write(int row,int col,float val){ |
||||||
|
contents[row][col].Set<float>(val); |
||||||
|
} |
||||||
|
void SHNFile::Write(int row,int col,std::string val){ |
||||||
|
contents[row][col].Set<std::string>(val); |
||||||
|
} |
||||||
|
SHNFile::SHNFile(){ |
||||||
|
readArr.reserve(64000); |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,8 @@ |
|||||||
|
#pragma once |
||||||
|
enum State{ |
||||||
|
LOADFOLDER, |
||||||
|
ITEMEDIT, |
||||||
|
MOBEDIT, |
||||||
|
QUESTEDIT, |
||||||
|
|
||||||
|
}; |
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1 @@ |
|||||||
|
DefaultPath = /Users/sigon/Documents/NA2016-main/Server/9Data/Shine |
@ -0,0 +1,602 @@ |
|||||||
|
/*
|
||||||
|
olcPGEX_PopUp.h |
||||||
|
|
||||||
|
+-------------------------------------------------------------+ |
||||||
|
| OneLoneCoder Pixel Game Engine Extension | |
||||||
|
| Retro PopUp Menu 1.0 | |
||||||
|
+-------------------------------------------------------------+ |
||||||
|
|
||||||
|
What is this? |
||||||
|
~~~~~~~~~~~~~ |
||||||
|
This is an extension to the olcPixelGameEngine, which provides |
||||||
|
a quick and easy to use, flexible, skinnable context pop-up
|
||||||
|
menu system. |
||||||
|
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2018 - 2020 OneLoneCoder.com |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above |
||||||
|
copyright notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce |
||||||
|
the above copyright notice. This list of conditions and the following |
||||||
|
disclaimer must be reproduced in the documentation and/or other |
||||||
|
materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
Links |
||||||
|
~~~~~ |
||||||
|
YouTube: https://www.youtube.com/javidx9
|
||||||
|
Discord: https://discord.gg/WhwHUMV
|
||||||
|
Twitter: https://www.twitter.com/javidx9
|
||||||
|
Twitch: https://www.twitch.tv/javidx9
|
||||||
|
GitHub: https://www.github.com/onelonecoder
|
||||||
|
Homepage: https://www.onelonecoder.com
|
||||||
|
|
||||||
|
Author |
||||||
|
~~~~~~ |
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020 |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Example |
||||||
|
~~~~~~~ |
||||||
|
|
||||||
|
#define OLC_PGEX_POPUPMENU |
||||||
|
#include "olcPGEX_PopUpMenu.h" |
||||||
|
|
||||||
|
NOTE: Requires a 9-patch sprite, by default each patch is |
||||||
|
8x8 pixels, patches are as follows: |
||||||
|
|
||||||
|
| PANEL TL | PANEL T | PANEL TR | SCROLL UP | CURSOR TL | CURSOR TR | |
||||||
|
| PANEL L | PANEL M | PANEL R | SUBMENU | CURSOR BL | CURSOR BR | |
||||||
|
| PANEL BL | PANEL B | PANEL BR | SCROLL DOWN | UNUSED | UNUSED | |
||||||
|
|
||||||
|
You can find an example sprite here: |
||||||
|
https://github.com/OneLoneCoder/olcPixelGameEngine/blob/master/Videos/RetroMenu.png
|
||||||
|
|
||||||
|
Constructing A Menu |
||||||
|
~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
// Declaration (presumably inside class)
|
||||||
|
olc::popup::Menu m; |
||||||
|
|
||||||
|
// Construction (root menu is a 1x5 table)
|
||||||
|
m.SetTable(1, 5); |
||||||
|
|
||||||
|
// Add first item to root menu (A 1x5 submenu)
|
||||||
|
m["Menu1"].SetTable(1, 5); |
||||||
|
|
||||||
|
// Add items to first item
|
||||||
|
m["Menu1"]["Item1"]; |
||||||
|
m["Menu1"]["Item2"]; |
||||||
|
|
||||||
|
// Add a 4x3 submenu
|
||||||
|
m["Menu1"]["Item3"].SetTable(4, 3); |
||||||
|
m["Menu1"]["Item3"]["Option1"]; |
||||||
|
m["Menu1"]["Item3"]["Option2"]; |
||||||
|
|
||||||
|
// Set properties of specific item
|
||||||
|
m["Menu1"]["Item3"]["Option3"].Enable(false); |
||||||
|
m["Menu1"]["Item3"]["Option4"]; |
||||||
|
m["Menu1"]["Item3"]["Option5"]; |
||||||
|
m["Menu1"]["Item4"]; |
||||||
|
|
||||||
|
// Add second item to root menu
|
||||||
|
m["Menu2"].SetTable(3, 3); |
||||||
|
m["Menu2"]["Item1"]; |
||||||
|
m["Menu2"]["Item2"].SetID(1001).Enable(true); |
||||||
|
m["Menu2"]["Item3"]; |
||||||
|
|
||||||
|
// Construct the menu structure
|
||||||
|
m.Build(); |
||||||
|
|
||||||
|
|
||||||
|
Displaying a Menu |
||||||
|
~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
// Declaration of menu manager (presumably inside class)
|
||||||
|
olc::popup::Manager man; |
||||||
|
|
||||||
|
// Set the Menu object to the MenuManager (call once per pop)
|
||||||
|
man.Open(&m); |
||||||
|
|
||||||
|
// Draw Menu at position (30, 30), using "patch sprite"
|
||||||
|
man.Draw(sprGFX, { 30,30 }); |
||||||
|
|
||||||
|
|
||||||
|
Interacting with menu |
||||||
|
~~~~~~~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
// Send key events to menu
|
||||||
|
if (GetKey(olc::Key::UP).bPressed) man.OnUp(); |
||||||
|
if (GetKey(olc::Key::DOWN).bPressed) man.OnDown(); |
||||||
|
if (GetKey(olc::Key::LEFT).bPressed) man.OnLeft(); |
||||||
|
if (GetKey(olc::Key::RIGHT).bPressed) man.OnRight(); |
||||||
|
if (GetKey(olc::Key::Z).bPressed) man.OnBack(); |
||||||
|
|
||||||
|
// "Confirm/Action" Key does something, if it returns non-null
|
||||||
|
// then a menu item has been selected. The specific item will
|
||||||
|
// be returned
|
||||||
|
olc::popup::Menu* command = nullptr; |
||||||
|
if (GetKey(olc::Key::SPACE).bPressed) command = man.OnConfirm(); |
||||||
|
if (command != nullptr) |
||||||
|
{ |
||||||
|
std::string sLastAction =
|
||||||
|
"Selected: " + command->GetName() +
|
||||||
|
" ID: " + std::to_string(command->GetID()); |
||||||
|
|
||||||
|
// Optionally close menu?
|
||||||
|
man.Close(); |
||||||
|
} |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef OLC_PGEX_POPUPMENU_H |
||||||
|
#define OLC_PGEX_POPUPMENU_H |
||||||
|
|
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
namespace olc |
||||||
|
{ |
||||||
|
namespace popup |
||||||
|
{ |
||||||
|
constexpr int32_t nPatch = 8; |
||||||
|
|
||||||
|
class Menu |
||||||
|
{ |
||||||
|
public: |
||||||
|
Menu(); |
||||||
|
Menu(const std::string n); |
||||||
|
|
||||||
|
Menu& SetTable(int32_t nColumns, int32_t nRows); |
||||||
|
Menu& SetID(int32_t id); |
||||||
|
Menu& Enable(bool b); |
||||||
|
|
||||||
|
int32_t GetID(); |
||||||
|
std::string& GetName();
|
||||||
|
bool Enabled(); |
||||||
|
bool HasChildren(); |
||||||
|
int GetChildrenCount(); |
||||||
|
olc::vi2d GetSize(); |
||||||
|
olc::vi2d& GetCursorPosition(); |
||||||
|
Menu& operator[](const std::string& name); |
||||||
|
void Build(); |
||||||
|
void DrawSelf(olc::PixelGameEngine& pge, olc::Sprite* sprGFX, olc::vi2d vScreenOffset); |
||||||
|
void ClampCursor(); |
||||||
|
void Clear(); |
||||||
|
void OnUp(); |
||||||
|
void OnDown(); |
||||||
|
void OnLeft(); |
||||||
|
void OnRight(); |
||||||
|
Menu* OnConfirm(); |
||||||
|
Menu* GetSelectedItem(); |
||||||
|
|
||||||
|
protected: |
||||||
|
int32_t nID = -1; |
||||||
|
olc::vi2d vCellTable = { 1, 0 }; |
||||||
|
std::unordered_map<std::string, size_t> itemPointer; |
||||||
|
std::vector<olc::popup::Menu> items; |
||||||
|
olc::vi2d vSizeInPatches = { 0, 0 }; |
||||||
|
olc::vi2d vCellSize = { 0, 0 }; |
||||||
|
olc::vi2d vCellPadding = { 2, 0 }; |
||||||
|
olc::vi2d vCellCursor = { 0, 0 }; |
||||||
|
int32_t nCursorItem = 0; |
||||||
|
int32_t nTopVisibleRow = 0; |
||||||
|
int32_t nTotalRows = 0; |
||||||
|
const olc::vi2d vPatchSize = { nPatch, nPatch }; |
||||||
|
std::string sName; |
||||||
|
olc::vi2d vCursorPos = { 0, 0 }; |
||||||
|
bool bEnabled = true; |
||||||
|
}; |
||||||
|
|
||||||
|
class Manager : public olc::PGEX |
||||||
|
{ |
||||||
|
public: |
||||||
|
Manager(); |
||||||
|
void Open(Menu* mo); |
||||||
|
void Close(); |
||||||
|
void OnUp(); |
||||||
|
void OnDown(); |
||||||
|
void OnLeft(); |
||||||
|
void OnRight(); |
||||||
|
void OnBack(); |
||||||
|
Menu* OnConfirm(); |
||||||
|
void Draw(olc::Sprite* sprGFX, olc::vi2d vScreenOffset); |
||||||
|
|
||||||
|
private: |
||||||
|
std::list<Menu*> panels; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef OLC_PGEX_POPUPMENU |
||||||
|
#undef OLC_PGEX_POPUPMENU |
||||||
|
|
||||||
|
namespace olc |
||||||
|
{ |
||||||
|
namespace popup |
||||||
|
{ |
||||||
|
Menu::Menu() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Menu::Menu(const std::string n) |
||||||
|
{ |
||||||
|
sName = n; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Menu& Menu::SetTable(int32_t nColumns, int32_t nRows) |
||||||
|
{ |
||||||
|
vCellTable = { nColumns, nRows }; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
Menu& Menu::SetID(int32_t id) |
||||||
|
{ |
||||||
|
nID = id; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
Menu& Menu::Enable(bool b) |
||||||
|
{ |
||||||
|
bEnabled = b; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
int32_t Menu::GetID() |
||||||
|
{ |
||||||
|
return nID; |
||||||
|
} |
||||||
|
|
||||||
|
std::string& Menu::GetName() |
||||||
|
{ |
||||||
|
return sName; |
||||||
|
} |
||||||
|
|
||||||
|
bool Menu::Enabled() |
||||||
|
{ |
||||||
|
return bEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
bool Menu::HasChildren() |
||||||
|
{ |
||||||
|
return !items.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
int Menu::GetChildrenCount() |
||||||
|
{ |
||||||
|
return items.size(); |
||||||
|
} |
||||||
|
|
||||||
|
olc::vi2d Menu::GetSize() |
||||||
|
{ |
||||||
|
return { int32_t(sName.size()), 1 }; |
||||||
|
} |
||||||
|
|
||||||
|
olc::vi2d& Menu::GetCursorPosition() |
||||||
|
{ |
||||||
|
return vCursorPos; |
||||||
|
} |
||||||
|
|
||||||
|
Menu& Menu::operator[](const std::string& name) |
||||||
|
{ |
||||||
|
if (itemPointer.count(name) == 0) |
||||||
|
{ |
||||||
|
itemPointer[name] = items.size(); |
||||||
|
items.push_back(Menu(name)); |
||||||
|
} |
||||||
|
|
||||||
|
return items[itemPointer[name]]; |
||||||
|
} |
||||||
|
|
||||||
|
void Menu::Clear(){ |
||||||
|
items.clear(); |
||||||
|
itemPointer.clear(); |
||||||
|
vCursorPos=vCellCursor={0,0}; |
||||||
|
nTopVisibleRow=0; |
||||||
|
nCursorItem=0; |
||||||
|
Build(); //Tidy up the new empty menu.
|
||||||
|
} |
||||||
|
|
||||||
|
void Menu::Build() |
||||||
|
{ |
||||||
|
// Recursively build all children, so they can determine their size, use
|
||||||
|
// that size to indicate cell sizes if this object contains more than
|
||||||
|
// one item
|
||||||
|
for (auto& m : items) |
||||||
|
{ |
||||||
|
if (m.HasChildren()) |
||||||
|
{ |
||||||
|
m.Build(); |
||||||
|
} |
||||||
|
|
||||||
|
// Longest child name determines cell width
|
||||||
|
vCellSize.x = std::max(m.GetSize().x, vCellSize.x); |
||||||
|
vCellSize.y = std::max(m.GetSize().y, vCellSize.y); |
||||||
|
} |
||||||
|
|
||||||
|
// Adjust size of this object (in patches) if it were rendered as a panel
|
||||||
|
vSizeInPatches.x = vCellTable.x * vCellSize.x + (vCellTable.x - 1) * vCellPadding.x + 2; |
||||||
|
vSizeInPatches.y = vCellTable.y * vCellSize.y + (vCellTable.y - 1) * vCellPadding.y + 2; |
||||||
|
|
||||||
|
// Calculate how many rows this item has to hold
|
||||||
|
nTotalRows = (items.size() / vCellTable.x) + (((items.size() % vCellTable.x) > 0) ? 1 : 0); |
||||||
|
} |
||||||
|
|
||||||
|
void Menu::DrawSelf(olc::PixelGameEngine& pge, olc::Sprite* sprGFX, olc::vi2d vScreenOffset) |
||||||
|
{ |
||||||
|
// === Draw Panel
|
||||||
|
|
||||||
|
// Record current pixel mode user is using
|
||||||
|
olc::Pixel::Mode currentPixelMode = pge.GetPixelMode(); |
||||||
|
pge.SetPixelMode(olc::Pixel::MASK); |
||||||
|
|
||||||
|
// Draw Panel & Border
|
||||||
|
olc::vi2d vPatchPos = { 0,0 }; |
||||||
|
for (vPatchPos.x = 0; vPatchPos.x < vSizeInPatches.x; vPatchPos.x++) |
||||||
|
{ |
||||||
|
for (vPatchPos.y = 0; vPatchPos.y < vSizeInPatches.y; vPatchPos.y++) |
||||||
|
{ |
||||||
|
// Determine position in screen space
|
||||||
|
olc::vi2d vScreenLocation = vPatchPos * nPatch + vScreenOffset; |
||||||
|
|
||||||
|
// Calculate which patch is needed
|
||||||
|
olc::vi2d vSourcePatch = { 0, 0 }; |
||||||
|
if (vPatchPos.x > 0) vSourcePatch.x = 1; |
||||||
|
if (vPatchPos.x == vSizeInPatches.x - 1) vSourcePatch.x = 2; |
||||||
|
if (vPatchPos.y > 0) vSourcePatch.y = 1; |
||||||
|
if (vPatchPos.y == vSizeInPatches.y - 1) vSourcePatch.y = 2; |
||||||
|
|
||||||
|
// Draw Actual Patch
|
||||||
|
pge.DrawPartialSprite(vScreenLocation, sprGFX, vSourcePatch * nPatch, vPatchSize); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// === Draw Panel Contents
|
||||||
|
olc::vi2d vCell = { 0,0 }; |
||||||
|
vPatchPos = { 1,1 }; |
||||||
|
|
||||||
|
// Work out visible items
|
||||||
|
int32_t nTopLeftItem = nTopVisibleRow * vCellTable.x; |
||||||
|
int32_t nBottomRightItem = vCellTable.y * vCellTable.x + nTopLeftItem; |
||||||
|
|
||||||
|
// Clamp to size of child item vector
|
||||||
|
nBottomRightItem = std::min(int32_t(items.size()), nBottomRightItem); |
||||||
|
int32_t nVisibleItems = nBottomRightItem - nTopLeftItem; |
||||||
|
|
||||||
|
// Draw Scroll Markers (if required)
|
||||||
|
if (nTopVisibleRow > 0) |
||||||
|
{ |
||||||
|
vPatchPos = { vSizeInPatches.x - 2, 0 }; |
||||||
|
olc::vi2d vScreenLocation = vPatchPos * nPatch + vScreenOffset; |
||||||
|
olc::vi2d vSourcePatch = { 3, 0 }; |
||||||
|
pge.DrawPartialSprite(vScreenLocation, sprGFX, vSourcePatch * nPatch, vPatchSize); |
||||||
|
} |
||||||
|
|
||||||
|
if ((nTotalRows - nTopVisibleRow) > vCellTable.y) |
||||||
|
{ |
||||||
|
vPatchPos = { vSizeInPatches.x - 2, vSizeInPatches.y - 1 }; |
||||||
|
olc::vi2d vScreenLocation = vPatchPos * nPatch + vScreenOffset; |
||||||
|
olc::vi2d vSourcePatch = { 3, 2 }; |
||||||
|
pge.DrawPartialSprite(vScreenLocation, sprGFX, vSourcePatch * nPatch, vPatchSize); |
||||||
|
} |
||||||
|
|
||||||
|
// Draw Visible Items
|
||||||
|
for (int32_t i = 0; i < nVisibleItems; i++) |
||||||
|
{ |
||||||
|
// Cell location
|
||||||
|
vCell.x = i % vCellTable.x; |
||||||
|
vCell.y = i / vCellTable.x; |
||||||
|
|
||||||
|
// Patch location (including border offset and padding)
|
||||||
|
vPatchPos.x = vCell.x * (vCellSize.x + vCellPadding.x) + 1; |
||||||
|
vPatchPos.y = vCell.y * (vCellSize.y + vCellPadding.y) + 1; |
||||||
|
|
||||||
|
// Actual screen location in pixels
|
||||||
|
olc::vi2d vScreenLocation = vPatchPos * nPatch + vScreenOffset; |
||||||
|
|
||||||
|
// Display Item Header
|
||||||
|
pge.DrawString(vScreenLocation, items[nTopLeftItem + i].sName, items[nTopLeftItem + i].bEnabled ? olc::WHITE : olc::DARK_GREY); |
||||||
|
|
||||||
|
if (items[nTopLeftItem + i].HasChildren()) |
||||||
|
{ |
||||||
|
// Display Indicator that panel has a sub panel
|
||||||
|
vPatchPos.x = vCell.x * (vCellSize.x + vCellPadding.x) + 1 + vCellSize.x; |
||||||
|
vPatchPos.y = vCell.y * (vCellSize.y + vCellPadding.y) + 1; |
||||||
|
olc::vi2d vSourcePatch = { 3, 1 }; |
||||||
|
vScreenLocation = vPatchPos * nPatch + vScreenOffset; |
||||||
|
pge.DrawPartialSprite(vScreenLocation, sprGFX, vSourcePatch * nPatch, vPatchSize); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Calculate cursor position in screen space in case system draws it
|
||||||
|
vCursorPos.x = (vCellCursor.x * (vCellSize.x + vCellPadding.x)) * nPatch + vScreenOffset.x - nPatch; |
||||||
|
vCursorPos.y = ((vCellCursor.y - nTopVisibleRow) * (vCellSize.y + vCellPadding.y)) * nPatch + vScreenOffset.y + nPatch; |
||||||
|
} |
||||||
|
|
||||||
|
void Menu::ClampCursor() |
||||||
|
{ |
||||||
|
// Find item in children
|
||||||
|
nCursorItem = vCellCursor.y * vCellTable.x + vCellCursor.x; |
||||||
|
|
||||||
|
// Clamp Cursor
|
||||||
|
if (nCursorItem >= int32_t(items.size())) |
||||||
|
{ |
||||||
|
vCellCursor.y = (items.size() / vCellTable.x); |
||||||
|
vCellCursor.x = (items.size() % vCellTable.x) - 1; |
||||||
|
nCursorItem = items.size() - 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Menu::OnUp() |
||||||
|
{ |
||||||
|
vCellCursor.y--; |
||||||
|
if (vCellCursor.y < 0) vCellCursor.y = 0; |
||||||
|
|
||||||
|
if (vCellCursor.y < nTopVisibleRow) |
||||||
|
{ |
||||||
|
nTopVisibleRow--; |
||||||
|
if (nTopVisibleRow < 0) nTopVisibleRow = 0; |
||||||
|
} |
||||||
|
|
||||||
|
ClampCursor(); |
||||||
|
} |
||||||
|
|
||||||
|
void Menu::OnDown() |
||||||
|
{ |
||||||
|
vCellCursor.y++; |
||||||
|
if (vCellCursor.y == nTotalRows) vCellCursor.y = nTotalRows - 1; |
||||||
|
|
||||||
|
if (vCellCursor.y > (nTopVisibleRow + vCellTable.y - 1)) |
||||||
|
{ |
||||||
|
nTopVisibleRow++; |
||||||
|
if (nTopVisibleRow > (nTotalRows - vCellTable.y)) |
||||||
|
nTopVisibleRow = nTotalRows - vCellTable.y; |
||||||
|
} |
||||||
|
|
||||||
|
ClampCursor(); |
||||||
|
} |
||||||
|
|
||||||
|
void Menu::OnLeft() |
||||||
|
{ |
||||||
|
vCellCursor.x--; |
||||||
|
if (vCellCursor.x < 0) vCellCursor.x = 0; |
||||||
|
ClampCursor(); |
||||||
|
} |
||||||
|
|
||||||
|
void Menu::OnRight() |
||||||
|
{ |
||||||
|
vCellCursor.x++; |
||||||
|
if (vCellCursor.x == vCellTable.x) vCellCursor.x = vCellTable.x - 1; |
||||||
|
ClampCursor(); |
||||||
|
} |
||||||
|
|
||||||
|
Menu* Menu::OnConfirm() |
||||||
|
{ |
||||||
|
// Check if selected item has children
|
||||||
|
if (items[nCursorItem].HasChildren()) |
||||||
|
{ |
||||||
|
return &items[nCursorItem]; |
||||||
|
} |
||||||
|
else |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
Menu* Menu::GetSelectedItem() |
||||||
|
{ |
||||||
|
return &items[nCursorItem]; |
||||||
|
} |
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
|
||||||
|
Manager::Manager() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void Manager::Open(Menu* mo)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
panels.push_back(mo);
|
||||||
|
} |
||||||
|
|
||||||
|
void Manager::Close() |
||||||
|
{ |
||||||
|
panels.clear();
|
||||||
|
} |
||||||
|
|
||||||
|
void Manager::OnUp()
|
||||||
|
{
|
||||||
|
if (!panels.empty()) panels.back()->OnUp();
|
||||||
|
} |
||||||
|
|
||||||
|
void Manager::OnDown()
|
||||||
|
{
|
||||||
|
if (!panels.empty()) panels.back()->OnDown();
|
||||||
|
} |
||||||
|
|
||||||
|
void Manager::OnLeft() |
||||||
|
{
|
||||||
|
if (!panels.empty()) panels.back()->OnLeft(); |
||||||
|
} |
||||||
|
|
||||||
|
void Manager::OnRight() |
||||||
|
{
|
||||||
|
if (!panels.empty()) panels.back()->OnRight(); |
||||||
|
} |
||||||
|
|
||||||
|
void Manager::OnBack() |
||||||
|
{ |
||||||
|
if (!panels.empty()) panels.pop_back();
|
||||||
|
} |
||||||
|
|
||||||
|
Menu* Manager::OnConfirm() |
||||||
|
{ |
||||||
|
if (panels.empty()) return nullptr; |
||||||
|
|
||||||
|
Menu* next = panels.back()->OnConfirm(); |
||||||
|
if (next == panels.back()) |
||||||
|
{ |
||||||
|
if (panels.back()->GetSelectedItem()->Enabled()) |
||||||
|
return panels.back()->GetSelectedItem(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (next->Enabled()) |
||||||
|
panels.push_back(next); |
||||||
|
} |
||||||
|
|
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void Manager::Draw(olc::Sprite* sprGFX, olc::vi2d vScreenOffset) |
||||||
|
{ |
||||||
|
if (panels.empty()) return; |
||||||
|
|
||||||
|
// Draw Visible Menu System
|
||||||
|
for (auto& p : panels) |
||||||
|
{ |
||||||
|
p->DrawSelf(*pge, sprGFX, vScreenOffset); |
||||||
|
vScreenOffset += {10, 10}; |
||||||
|
} |
||||||
|
|
||||||
|
// Draw Cursor
|
||||||
|
olc::Pixel::Mode currentPixelMode = pge->GetPixelMode(); |
||||||
|
pge->SetPixelMode(olc::Pixel::ALPHA); |
||||||
|
pge->DrawPartialSprite(panels.back()->GetCursorPosition(), sprGFX, olc::vi2d(4, 0) * nPatch, { nPatch * 2, nPatch * 2 }); |
||||||
|
pge->SetPixelMode(currentPixelMode); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#endif |
||||||
|
#endif |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue