Skip to content

Commit

Permalink
Character (RoRbot) is now a mod type (select in Settings UI).
Browse files Browse the repository at this point in the history
The usage is equivalent to other mods: a ZIP/directory under 'ROR_HOMEDIR/mods' containing a '*.character' file.
To select character, open Settings UI, go to Gameplay tab, scroll to "Player character:" at the bottom and press "Select" button - the standard Selector UI will appear.
Character is always loaded with terrain. If the character fails to load, a "Cannot load terrain" messagebox appears and user remains in main menu.

The classic character resources were deleted from /resources and will be added to /content (a Git submodule).
  • Loading branch information
ohlidalp committed Nov 3, 2022
1 parent 6314ad0 commit b97e60d
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 104 deletions.
30 changes: 0 additions & 30 deletions resources/materials/character.material

This file was deleted.

23 changes: 0 additions & 23 deletions resources/meshes/character.material

This file was deleted.

Binary file removed resources/meshes/character.mesh
Binary file not shown.
Binary file removed resources/meshes/character.skeleton
Binary file not shown.
1 change: 1 addition & 0 deletions source/main/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ CVar* sim_no_self_collisions;
CVar* sim_gearbox_mode;
CVar* sim_soft_reset_mode;
CVar* sim_quickload_dialog;
CVar* sim_player_character;

// Multiplayer
CVar* mp_state;
Expand Down
2 changes: 2 additions & 0 deletions source/main/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ enum VisibilityMasks
enum LoaderType //!< Operation mode for GUI::MainSelector
{
LT_None,
LT_Character, // No script alias, invoked from Settings UI.
LT_Terrain, // Invocable from GUI; No script alias, used in main menu
LT_Vehicle, // Script "vehicle", ext: truck car
LT_Truck, // Script "truck", ext: truck car
Expand Down Expand Up @@ -294,6 +295,7 @@ extern CVar* sim_no_self_collisions;
extern CVar* sim_gearbox_mode;
extern CVar* sim_soft_reset_mode;
extern CVar* sim_quickload_dialog;
extern CVar* sim_player_character;

// Multiplayer
extern CVar* mp_state;
Expand Down
11 changes: 10 additions & 1 deletion source/main/GameContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,10 +693,17 @@ void GameContext::OnLoaderGuiApply(LoaderType type, CacheEntry* entry, std::stri
// --------------------------------
// Characters

void GameContext::CreatePlayerCharacter()
bool GameContext::CreatePlayerCharacter()
{
m_character_factory.CreateLocalCharacter();

if (!this->GetPlayerCharacter())
{
App::GetGuiManager()->ShowMessageBox(_L("Terrain loading error"),
"Failed to create player character, see console or 'RoR.log' for more info.");
return false;
}

// Adjust character position
Ogre::Vector3 spawn_pos = App::GetSimTerrain()->getSpawnPos();
float spawn_rot = 0.0f;
Expand Down Expand Up @@ -744,6 +751,8 @@ void GameContext::CreatePlayerCharacter()
{
App::GetCameraManager()->UpdateInputEvents(0.02f);
}

return true;
}

Character* GameContext::GetPlayerCharacter() // Convenience ~ counterpart of `GetPlayerActor()`
Expand Down
2 changes: 1 addition & 1 deletion source/main/GameContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class GameContext
/// @name Characters
/// @{

void CreatePlayerCharacter(); //!< Terrain must be loaded
bool CreatePlayerCharacter(); //!< Terrain must be loaded
Character* GetPlayerCharacter();
CharacterFactory* GetCharacterFactory() { return &m_character_factory; }

Expand Down
62 changes: 59 additions & 3 deletions source/main/gameplay/CharacterFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
#include "CharacterFactory.h"

#include "Application.h"
#include "CacheSystem.h"
#include "Character.h"
#include "Console.h"
#include "GfxScene.h"
#include "Utils.h"

Expand All @@ -32,6 +34,26 @@ CharacterFactory::CharacterFactory()
{
}

CharacterDocumentPtr CharacterFactory::FetchCharacterDef(CacheEntry* cache_entry)
{
if (!cache_entry->character_def)
{
try
{
Ogre::DataStreamPtr datastream = Ogre::ResourceGroupManager::getSingleton().openResource(cache_entry->fname, cache_entry->resource_group);
CharacterParser character_parser;
cache_entry->character_def = character_parser.ProcessOgreStream(datastream);
}
catch (Ogre::Exception& eeh)
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR,
fmt::format("Could not load character, message: {}", eeh.getFullDescription()));
}
}

return cache_entry->character_def;
}

Character* CharacterFactory::CreateLocalCharacter()
{
int colourNum = -1;
Expand All @@ -46,7 +68,21 @@ Character* CharacterFactory::CreateLocalCharacter()
}
#endif // USE_SOCKETW

m_local_character = std::unique_ptr<Character>(new Character(m_character_defs[0], -1, 0, playerName, colourNum, false));
CacheEntry* cache_entry = App::GetCacheSystem()->FindEntryByFilename(LT_Character, /*partial:*/false, App::sim_player_character->getStr());
if (!cache_entry)
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR,
fmt::format("Could not find character '{}' in mod cache.", App::sim_player_character->getStr()));
return nullptr;
}

CharacterDocumentPtr document = this->FetchCharacterDef(cache_entry);
if (!document)
{
return nullptr; // Error already reported
}

m_local_character = std::unique_ptr<Character>(new Character(document, -1, 0, playerName, colourNum, false));
App::GetGfxScene()->RegisterGfxCharacter(m_local_character.get());
return m_local_character.get();
}
Expand All @@ -59,9 +95,29 @@ void CharacterFactory::createRemoteInstance(int sourceid, int streamid)
int colour = info.colournum;
Ogre::UTFString name = tryConvertUTF(info.username);

LOG(" new character for " + TOSTRING(sourceid) + ":" + TOSTRING(streamid) + ", colour: " + TOSTRING(colour));
std::string info_str = fmt::format("player '{}' ({}:{}), colour: {}", info.clientname, sourceid, streamid, colour);

LOG(" new character for " + info_str);

std::string filename = App::sim_player_character->getStr(); // TBD: transmit and use the actual character used by the player

CacheEntry* cache_entry = App::GetCacheSystem()->FindEntryByFilename(LT_Character, /*partial:*/false, filename);
if (!cache_entry)
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR,
fmt::format("Could not create character for {} - character '{}' not found in mod cache.", info_str, filename));
return;
}

CharacterDocumentPtr document = this->FetchCharacterDef(cache_entry);
if (!document)
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR,
fmt::format("Could not create character for {} - cannot load file '{}'.", info_str, cache_entry->fname));
return;
}

Character* ch = new Character(m_character_defs[0], sourceid, streamid, name, colour, true);
Character* ch = new Character(document, sourceid, streamid, name, colour, true);
App::GetGfxScene()->RegisterGfxCharacter(ch);
m_remote_characters.push_back(std::unique_ptr<Character>(ch));
#endif // USE_SOCKETW
Expand Down
4 changes: 1 addition & 3 deletions source/main/gameplay/CharacterFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,13 @@ class CharacterFactory
void DeleteAllCharacters();
void UndoRemoteActorCoupling(Actor* actor);
void Update(float dt);
void DefineCharacter(CharacterDocumentPtr doc) { m_character_defs.push_back(doc); }
CharacterDocumentPtr FetchCharacterDef(CacheEntry* cache_entry);
#ifdef USE_SOCKETW
void handleStreamData(std::vector<RoR::NetRecvPacket> packet);
#endif // USE_SOCKETW

private:

std::vector<CharacterDocumentPtr> m_character_defs;

std::unique_ptr<Character> m_local_character;
std::vector<std::unique_ptr<Character>> m_remote_characters;

Expand Down
11 changes: 11 additions & 0 deletions source/main/gui/panels/GUI_GameSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,17 @@ void GameSettings::DrawGameplaySettings()
DrawGCheckbox(App::io_discord_rpc, _LC("GameSettings", "Discord Rich Presence"));

DrawGCheckbox(App::sim_quickload_dialog, _LC("GameSettings", "Show confirm. UI dialog for quickload"));

// Character
ImGui::TextDisabled("%s:", _LC("GameSettings", "Player character"));
ImGui::SameLine();
ImGui::Text("%s", App::sim_player_character->getStr().c_str());
ImGui::SameLine();
if (ImGui::Button(_LC("GameSettings", "Select")))
{
LoaderType* payload = new LoaderType(LoaderType::LT_Character);
App::GetGameContext()->PushMessage(Message(MSG_GUI_OPEN_SELECTOR_REQUESTED, (void*)payload));
}
}

void GameSettings::DrawAudioSettings()
Expand Down
54 changes: 36 additions & 18 deletions source/main/gui/panels/GUI_MainSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -582,28 +582,46 @@ void MainSelector::Apply()
ROR_ASSERT(m_selected_entry > -1); // Programmer error
DisplayEntry& sd_entry = m_display_entries[m_selected_entry];

if (m_loader_type == LT_Terrain &&
App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
{
App::GetGameContext()->PushMessage(Message(MSG_SIM_LOAD_TERRN_REQUESTED, sd_entry.sde_entry->fname));
this->Close();
}
else if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
{
LoaderType type = m_loader_type;
std::string sectionconfig;
if (sd_entry.sde_entry->sectionconfigs.size() > 0)
switch (m_loader_type)
{
case LT_Character: // Invoked by Settings UI button
App::sim_player_character->setStr(sd_entry.sde_entry->fname);
this->Close();
break;

case LT_Terrain: // Invoked by Main menu button
if (App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
{
sectionconfig = sd_entry.sde_entry->sectionconfigs[m_selected_sectionconfig];
}
this->Close();
App::GetGameContext()->PushMessage(Message(MSG_SIM_LOAD_TERRN_REQUESTED, sd_entry.sde_entry->fname));
this->Close();
}
break;

if (m_loader_type == LT_Skin && sd_entry.sde_entry == &m_dummy_skin)
default: // Vehicle in simulation (Invoked by: top menubar, hotkey, or spawner)
if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
{
sd_entry.sde_entry = nullptr;
}
// Make a copy because `Close()` will reset it.
LoaderType orig_loader_type = m_loader_type;

App::GetGameContext()->OnLoaderGuiApply(type, sd_entry.sde_entry, sectionconfig);
// If no config was selected, use the first one.
std::string sectionconfig;
if (sd_entry.sde_entry->sectionconfigs.size() > 0)
{
sectionconfig = sd_entry.sde_entry->sectionconfigs[m_selected_sectionconfig];
}

// Close the UI so that GameContext can reopen it if needed (used for skins)
this->Close();

// If the dummy item was selected, clear the selection.
if (orig_loader_type == LT_Skin && sd_entry.sde_entry == &m_dummy_skin)
{
sd_entry.sde_entry = nullptr;
}

App::GetGameContext()->OnLoaderGuiApply(orig_loader_type, sd_entry.sde_entry, sectionconfig);
}
break;
}
}

Expand Down
29 changes: 11 additions & 18 deletions source/main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,20 +220,6 @@ int main(int argc, char *argv[])
App::GetGameContext()->PushMessage(Message(MSG_APP_MODCACHE_LOAD_REQUESTED));
}

// Load classic character
try
{
Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource("classic.character");
CharacterParser character_parser;
App::GetGameContext()->GetCharacterFactory()->DefineCharacter(
character_parser.ProcessOgreStream(stream));
}
catch (Ogre::Exception& eeh)
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_ERROR,
fmt::format("error loading classic character, message:{}", eeh.getFullDescription()));
}

// Load startup scripts (console, then RoR.cfg)
if (App::cli_custom_scripts->getStr() != "")
{
Expand Down Expand Up @@ -534,9 +520,9 @@ int main(int argc, char *argv[])
App::GetGuiManager()->LoadingWindow.SetProgress(5, _L("Loading resources"));
App::GetContentManager()->LoadGameplayResources();

if (App::GetGameContext()->LoadTerrain(m.description))
if (App::GetGameContext()->LoadTerrain(m.description)
&& App::GetGameContext()->CreatePlayerCharacter())
{
App::GetGameContext()->CreatePlayerCharacter();
// Spawn preselected vehicle; commandline has precedence
if (App::cli_preset_vehicle->getStr() != "")
App::GetGameContext()->SpawnPreselectedActor(App::cli_preset_vehicle->getStr(), App::cli_preset_veh_config->getStr()); // Needs character for position
Expand All @@ -563,8 +549,8 @@ int main(int argc, char *argv[])
if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE,
fmt::format(_LC("ChatBox", "Press {} to start chatting"),
App::GetInputEngine()->getEventCommandTrimmed(EV_COMMON_ENTER_CHATMODE)), "lightbulb.png");
fmt::format(_LC("ChatBox", "Press {} to start chatting"),
App::GetInputEngine()->getEventCommandTrimmed(EV_COMMON_ENTER_CHATMODE)), "lightbulb.png");
}
#endif // USE_SOCKETW
if (App::io_outgauge_mode->getInt() > 0)
Expand All @@ -574,6 +560,13 @@ int main(int argc, char *argv[])
}
else
{
// Failed to load terrain or character - messagebox is already displayed
if (App::GetSimTerrain())
{
delete App::GetSimTerrain();
App::SetSimTerrain(nullptr);
}

if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
{
App::GetGameContext()->PushMessage(Message(MSG_NET_DISCONNECT_REQUESTED));
Expand Down
Loading

0 comments on commit b97e60d

Please sign in to comment.