forbid changing kart group after a player confirmed his selection

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@4911 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
auria 2010-03-03 00:48:31 +00:00
parent 72396ae3be
commit c17f4f0e07
5 changed files with 365 additions and 316 deletions

View File

@ -672,13 +672,11 @@ void Skin::drawRibbonChild(const core::rect< s32 > &rect, Widget* widget, const
if (mark_selected)
{
// selected tab should be slighlty bigger than others
if(vertical_flip)
rect2.UpperLeftCorner.Y -= 10;
else
rect2.LowerRightCorner.Y += 10;
if (vertical_flip) rect2.UpperLeftCorner.Y -= 10;
else rect2.LowerRightCorner.Y += 10;
}
drawBoxFromStretchableTexture(widget, rect2, *params);
drawBoxFromStretchableTexture(widget, rect2, *params, parentRibbon->m_deactivated);
}

View File

@ -417,6 +417,8 @@ void DynamicRibbonWidget::registerHoverListener(DynamicRibbonHoverListener* list
// -----------------------------------------------------------------------------
EventPropagation DynamicRibbonWidget::rightPressed(const int playerID)
{
if (m_deactivated) return EVENT_LET;
RibbonWidget* w = getSelectedRibbon(playerID);
if (w != NULL)
{
@ -443,6 +445,8 @@ EventPropagation DynamicRibbonWidget::rightPressed(const int playerID)
// -----------------------------------------------------------------------------
EventPropagation DynamicRibbonWidget::leftPressed(const int playerID)
{
if (m_deactivated) return EVENT_LET;
RibbonWidget* w = getSelectedRibbon(playerID);
if (w != NULL)
{
@ -465,6 +469,8 @@ EventPropagation DynamicRibbonWidget::leftPressed(const int playerID)
// -----------------------------------------------------------------------------
EventPropagation DynamicRibbonWidget::transmitEvent(Widget* w, std::string& originator, const int playerID)
{
if (m_deactivated) return EVENT_LET;
if (originator=="left")
{
scroll(-1);
@ -492,6 +498,7 @@ EventPropagation DynamicRibbonWidget::transmitEvent(Widget* w, std::string& orig
// -----------------------------------------------------------------------------
EventPropagation DynamicRibbonWidget::mouseHovered(Widget* child, const int playerID)
{
if (m_deactivated) return EVENT_LET;
//std::cout << "DynamicRibbonWidget::mouseHovered " << playerID << std::endl;
updateLabel();
@ -536,6 +543,8 @@ void DynamicRibbonWidget::onRibbonWidgetScroll(const int delta_x)
void DynamicRibbonWidget::onRibbonWidgetFocus(RibbonWidget* emitter, const int playerID)
{
if (m_deactivated) return;
if (emitter->m_selection[playerID] >= emitter->m_children.size())
{
emitter->m_selection[playerID] = emitter->m_children.size()-1;

View File

@ -252,6 +252,8 @@ void RibbonWidget::select(std::string item, const int mousePlayerID)
// -----------------------------------------------------------------------------
EventPropagation RibbonWidget::rightPressed(const int playerID)
{
if (m_deactivated) return EVENT_LET;
m_selection[playerID]++;
if (m_selection[playerID] >= m_children.size())
{
@ -285,6 +287,8 @@ EventPropagation RibbonWidget::rightPressed(const int playerID)
// -----------------------------------------------------------------------------
EventPropagation RibbonWidget::leftPressed(const int playerID)
{
if (m_deactivated) return EVENT_LET;
m_selection[playerID]--;
if (m_selection[playerID] < 0)
{
@ -340,6 +344,8 @@ EventPropagation RibbonWidget::focused(const int playerID)
// -----------------------------------------------------------------------------
EventPropagation RibbonWidget::mouseHovered(Widget* child, const int mousePlayerID)
{
if (m_deactivated) return EVENT_LET;
//std::cout << "RibbonWidget::mouseHovered " << mousePlayerID << std::endl;
const int subbuttons_amount = m_children.size();
@ -417,18 +423,21 @@ void RibbonWidget::updateSelection()
// -----------------------------------------------------------------------------
EventPropagation RibbonWidget::transmitEvent(Widget* w, std::string& originator, const int playerID)
{
const int subbuttons_amount = m_children.size();
for (int i=0; i<subbuttons_amount; i++)
if (!m_deactivated)
{
if (m_children[i].m_properties[PROP_ID] == originator)
{
m_selection[playerID] = i;
break;
}
}
const int subbuttons_amount = m_children.size();
updateSelection();
for (int i=0; i<subbuttons_amount; i++)
{
if (m_children[i].m_properties[PROP_ID] == originator)
{
m_selection[playerID] = i;
break;
}
}
updateSelection();
}
// bring focus back to enclosing ribbon widget
this->setFocusForPlayer( playerID );

View File

@ -601,6 +601,14 @@ public:
}
};
/** Small utility function that returns whether the two given players chose the same kart.
* The advantage of this function is that it can handle "random kart" selection. */
bool sameKart(const PlayerKartWidget& player1, const PlayerKartWidget& player2)
{
return player1.getKartInternalName() == player2.getKartInternalName() &&
player1.getKartInternalName() != RANDOM_KART_ID;
}
#if 0
#pragma mark -
#pragma mark KartHoverListener
@ -690,6 +698,7 @@ KartHoverListener* karthoverListener = NULL;
KartSelectionScreen::KartSelectionScreen() : Screen("karts.stkgui")
{
g_dispatcher = new FocusDispatcher(this);
m_player_confirmed = false;
// Dynamically add tabs
// FIXME: it's not very well documented that RibbonWidgets can have dynamically generated contents
@ -715,15 +724,118 @@ KartSelectionScreen::KartSelectionScreen() : Screen("karts.stkgui")
item->m_properties[PROP_ID] = ALL_KART_GROUPS_ID;
tabs->m_children.push_back(item);
}
// -----------------------------------------------------------------------------
void KartSelectionScreen::init()
{
m_player_confirmed = false;
RibbonWidget* tabs = this->getWidget<RibbonWidget>("kartgroups");
assert( tabs != NULL );
tabs->m_deactivated = false;
// FIXME: Reload previous kart selection screen state
m_kart_widgets.clearAndDeleteAll();
StateManager::get()->resetActivePlayers();
input_manager->getDeviceList()->setAssignMode(DETECT_NEW);
DynamicRibbonWidget* w = this->getWidget<DynamicRibbonWidget>("karts");
assert( w != NULL );
if (karthoverListener == NULL)
{
karthoverListener = new KartHoverListener(this);
w->registerHoverListener(karthoverListener);
}
// Build kart list
// (it is built everytikme, to account for .g. locking)
w->clearItems();
std::vector<int> group = kart_properties_manager->getKartsInGroup("standard");
const int kart_amount = group.size();
// add Tux (or whatever default kart) first
std::string& default_kart = UserConfigParams::m_default_kart;
for(int n=0; n<kart_amount; n++)
{
const KartProperties* prop = kart_properties_manager->getKartById(group[n]);
if (prop->getIdent() == default_kart)
{
std::string icon_path = "/karts/" + prop->getIdent() + "/" + prop->getIconFile();
const bool locked = unlock_manager->isLocked(prop->getIdent());
w->addItem(prop->getName(), prop->getIdent().c_str(), icon_path.c_str(), locked);
//std::cout << "Add item : " << prop->getIdent().c_str() << std::endl;
break;
}
}
// add others
for(int n=0; n<kart_amount; n++)
{
const KartProperties* prop = kart_properties_manager->getKartById(group[n]);
if (prop->getIdent() != default_kart)
{
std::string icon_path = "/karts/" + prop->getIdent() + "/" + prop->getIconFile();
const bool locked = unlock_manager->isLocked(prop->getIdent());
w->addItem(prop->getName(), prop->getIdent().c_str(), icon_path.c_str(), locked);
//std::cout << "Add item : " << prop->getIdent().c_str() << std::endl;
}
}
// add random
w->addItem(_("Random Kart"), RANDOM_KART_ID, "/gui/random_kart.png");
/*
TODO: Ultimately, it'd be nice to *not* delete g_player_karts so that
when players return to the kart selection screen, it will appear as
it did when they left (at least when returning from the track menu).
Rebuilding the screen is a little tricky.
*/
if (m_kart_widgets.size() > 0)
{
// FIXME: trying to rebuild the screen
for (int n = 0; n < m_kart_widgets.size(); n++)
{
PlayerKartWidget *pkw;
pkw = m_kart_widgets.get(n);
this->manualAddWidget(pkw);
pkw->add();
}
}
else // For now this is what will happen
{
playerJoin( input_manager->getDeviceList()->getLatestUsedDevice(), true );
w->updateItemDisplay();
}
// Player 0 select first kart (Tux)
w->setSelection(0, 0, true);
}
// -----------------------------------------------------------------------------
void KartSelectionScreen::tearDown()
{
//g_player_karts.clearWithoutDeleting();
m_kart_widgets.clearAndDeleteAll();
}
// -----------------------------------------------------------------------------
void KartSelectionScreen::forgetWhatWasLoaded()
{
Screen::forgetWhatWasLoaded();
// these pointers is no more valid (have been deleted along other widgets)
// these pointers are no more valid (have been deleted along other widgets)
g_dispatcher = NULL;
karthoverListener = NULL;
}
// -----------------------------------------------------------------------------
// Return true if event was handled successfully
bool KartSelectionScreen::playerJoin(InputDevice* device, bool firstPlayer)
@ -905,98 +1017,165 @@ void KartSelectionScreen::onUpdate(float delta, irr::video::IVideoDriver*)
}
// -----------------------------------------------------------------------------
void KartSelectionScreen::tearDown()
/**
* Callback handling events from the kart selection menu
*/
void KartSelectionScreen::eventCallback(Widget* widget, const std::string& name, const int playerID)
{
//g_player_karts.clearWithoutDeleting();
m_kart_widgets.clearAndDeleteAll();
}
// -----------------------------------------------------------------------------
void KartSelectionScreen::init()
{
// FIXME: Reload previous kart selection screen state
m_kart_widgets.clearAndDeleteAll();
StateManager::get()->resetActivePlayers();
input_manager->getDeviceList()->setAssignMode(DETECT_NEW);
DynamicRibbonWidget* w = this->getWidget<DynamicRibbonWidget>("karts");
assert( w != NULL );
if (karthoverListener == NULL)
if (name == "kartgroups" && !m_player_confirmed) // don't allow changing group after someone confirmed
{
karthoverListener = new KartHoverListener(this);
w->registerHoverListener(karthoverListener);
}
RibbonWidget* tabs = this->getWidget<RibbonWidget>("kartgroups");
assert(tabs != NULL);
// Build kart list
// (it is built everytikme, to account for .g. locking)
w->clearItems();
std::vector<int> group = kart_properties_manager->getKartsInGroup("standard");
const int kart_amount = group.size();
std::string selection = tabs->getSelectionIDString(GUI_PLAYER_ID);
// add Tux (or whatever default kart) first
std::string& default_kart = UserConfigParams::m_default_kart;
for(int n=0; n<kart_amount; n++)
{
const KartProperties* prop = kart_properties_manager->getKartById(group[n]);
if (prop->getIdent() == default_kart)
DynamicRibbonWidget* w = this->getWidget<DynamicRibbonWidget>("karts");
w->clearItems();
// TODO : preserve selection of karts for all players
// FIXME: merge this code with the code that adds karts initially, copy-and-paste is ugly
if (selection == ALL_KART_GROUPS_ID)
{
std::string icon_path = "/karts/" + prop->getIdent() + "/" + prop->getIconFile();
const bool locked = unlock_manager->isLocked(prop->getIdent());
w->addItem(prop->getName(), prop->getIdent().c_str(), icon_path.c_str(), locked);
//std::cout << "Add item : " << prop->getIdent().c_str() << std::endl;
break;
}
}
const int kart_amount = kart_properties_manager->getNumberOfKarts();
// add others
for(int n=0; n<kart_amount; n++)
{
const KartProperties* prop = kart_properties_manager->getKartById(group[n]);
if (prop->getIdent() != default_kart)
for(int n=0; n<kart_amount; n++)
{
const KartProperties* prop = kart_properties_manager->getKartById(n);
std::string icon_path = "/karts/" + prop->getIdent() + "/" + prop->getIconFile();
w->addItem(prop->getName().c_str(), prop->getIdent().c_str(), icon_path.c_str());
}
}
else if (selection == "locked")
{
std::string icon_path = "/karts/" + prop->getIdent() + "/" + prop->getIconFile();
const bool locked = unlock_manager->isLocked(prop->getIdent());
w->addItem(prop->getName(), prop->getIdent().c_str(), icon_path.c_str(), locked);
//std::cout << "Add item : " << prop->getIdent().c_str() << std::endl;
unlock_manager->playLockSound();
}
}
// add random
w->addItem(_("Random Kart"), RANDOM_KART_ID, "/gui/random_kart.png");
/*
TODO: Ultimately, it'd be nice to *not* delete g_player_karts so that
when players return to the kart selection screen, it will appear as
it did when they left (at least when returning from the track menu).
Rebuilding the screen is a little tricky.
*/
if (m_kart_widgets.size() > 0)
{
// FIXME: trying to rebuild the screen
for (int n = 0; n < m_kart_widgets.size(); n++)
else if (selection == NO_ITEM_ID)
{
PlayerKartWidget *pkw;
pkw = m_kart_widgets.get(n);
this->manualAddWidget(pkw);
pkw->add();
}
else
{
std::vector<int> group = kart_properties_manager->getKartsInGroup(selection);
const int kart_amount = group.size();
for(int n=0; n<kart_amount; n++)
{
const KartProperties* prop = kart_properties_manager->getKartById(group[n]);
std::string icon_path = "/karts/" + prop->getIdent() + "/" + prop->getIconFile();
w->addItem(prop->getName().c_str(), prop->getIdent().c_str(), icon_path.c_str());
}
}
// add random
w->addItem(_("Random Kart"), RANDOM_KART_ID, "/gui/random_kart.png");
}
else // For now this is what will happen
{
playerJoin( input_manager->getDeviceList()->getLatestUsedDevice(), true );
w->updateItemDisplay();
}
// Player 0 select first kart (Tux)
w->setSelection(0, 0, true);
// update players selections
const int num_players = m_kart_widgets.size();
for (int n=0; n<num_players; n++)
{
// player 0 is the one that can change the groups, leave his focus on the tabs
if (n > 0) GUIEngine::focusNothingForPlayer(n);
const std::string& selection = m_kart_widgets[n].getKartInternalName();
if (!w->setSelection( selection, n, true ))
{
std::cout << "Player " << n << " lost their selection when switching tabs!!!\n";
// For now, select a random kart in this case (TODO : maybe do something better? )
RandomGenerator random;
const int count = w->getItems().size();
if (count > 0)
{
const int randomID = random.get( count );
w->setSelection( randomID, n, n > 0 ); // preserve selection for players > 0 (player 0 is the one that can change the groups)
}
else
{
std::cerr << "WARNING : kart selection screen has 0 items in the ribbon\n";
}
}
} // end for
}
else if (name == "karts")
{
// make sure no other player selected the same identity or kart
//std::cout << "\n\n\\\\\\\\ Kart Selected ////\n";
//std::cout << "Making sure no other player has ident " << g_player_karts[playerID].getAssociatedPlayer()->getProfile()->getName() << std::endl;
const int amount = m_kart_widgets.size();
for (int n=0; n<amount; n++)
{
if (n == playerID) continue; // don't check a kart against itself
if (m_kart_widgets[n].isReady() &&
(m_kart_widgets[n].getAssociatedPlayer()->getProfile() ==
m_kart_widgets[playerID].getAssociatedPlayer()->getProfile() ||
sameKart(m_kart_widgets[n], m_kart_widgets[playerID])))
{
printf("\n***\n*** You can't select this identity or kart, someone already took it!! ***\n***\n\n");
//SFXType sound;
//SOUND_UGH SOUND_CRASH SOUND_USE_ANVIL SOUND_EXPLOSION SOUND_MOVE_MENU SOUND_SELECT_MENU
sfx_manager->quickSound( "use_anvil" );
return;
}
// If two PlayerKart entries are associated to the same ActivePlayer, something went wrong
assert(m_kart_widgets[n].getAssociatedPlayer() != m_kart_widgets[playerID].getAssociatedPlayer());
}
// Mark this player as ready to start
m_kart_widgets[playerID].markAsReady();
m_player_confirmed = true;
RibbonWidget* tabs = this->getWidget<RibbonWidget>("kartgroups");
assert( tabs != NULL );
tabs->m_deactivated = true;
// validate choices to notify player of duplicates
const bool names_ok = validateIdentChoices();
const bool karts_ok = validateKartChoices();
if (!names_ok || !karts_ok) return;
// check if all players are ready
bool allPlayersReady = true;
for (int n=0; n<amount; n++)
{
if (!m_kart_widgets[n].isReady())
{
allPlayersReady = false;
break;
}
}
if (allPlayersReady) allPlayersDone();
}
else
{
// Transmit to all subwindows, maybe *they* care about this event
const int amount = m_kart_widgets.size();
for (int n=0; n<amount; n++)
{
m_kart_widgets[n].transmitEvent(widget, name, playerID);
}
// those events may mean that a player selection changed, so validate again
validateIdentChoices();
validateKartChoices();
}
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
#if 0
#pragma mark -
#pragma mark KartSelectionScreen (private)
#endif
void KartSelectionScreen::allPlayersDone()
{
input_manager->setMasterPlayerOnly(true);
@ -1011,7 +1190,7 @@ void KartSelectionScreen::allPlayersDone()
for (int n=0; n<players.size(); n++)
{
std::cout << " Player " << n << " is " << players[n].getConstProfile()->getName()
<< " on " << players[n].getDevice()->m_name << std::endl;
<< " on " << players[n].getDevice()->m_name << std::endl;
}
std::cout << "==========\n";
@ -1142,14 +1321,6 @@ bool KartSelectionScreen::validateIdentChoices()
// -----------------------------------------------------------------------------
/** Small utility that returns whether the two given players chose the same kart.
* The advantage of this function is that it can handle "random kart" selection. */
bool sameKart(const PlayerKartWidget& player1, const PlayerKartWidget& player2)
{
return player1.getKartInternalName() == player2.getKartInternalName() &&
player1.getKartInternalName() != RANDOM_KART_ID;
}
bool KartSelectionScreen::validateKartChoices()
{
bool ok = true;
@ -1202,153 +1373,6 @@ bool KartSelectionScreen::validateKartChoices()
}
// -----------------------------------------------------------------------------
/**
* Callback handling events from the kart selection menu
*/
void KartSelectionScreen::eventCallback(Widget* widget, const std::string& name, const int playerID)
{
if (name == "kartgroups")
{
RibbonWidget* tabs = this->getWidget<RibbonWidget>("kartgroups");
assert(tabs != NULL);
std::string selection = tabs->getSelectionIDString(GUI_PLAYER_ID);
DynamicRibbonWidget* w = this->getWidget<DynamicRibbonWidget>("karts");
w->clearItems();
// TODO : preserve selection of karts for all players
// FIXME: merge this code with the code that adds karts initially, copy-and-paste is ugly
if (selection == ALL_KART_GROUPS_ID)
{
const int kart_amount = kart_properties_manager->getNumberOfKarts();
for(int n=0; n<kart_amount; n++)
{
const KartProperties* prop = kart_properties_manager->getKartById(n);
std::string icon_path = "/karts/" + prop->getIdent() + "/" + prop->getIconFile();
w->addItem(prop->getName().c_str(), prop->getIdent().c_str(), icon_path.c_str());
}
}
else if (selection == "locked")
{
unlock_manager->playLockSound();
}
else if (selection == NO_ITEM_ID)
{
}
else
{
std::vector<int> group = kart_properties_manager->getKartsInGroup(selection);
const int kart_amount = group.size();
for(int n=0; n<kart_amount; n++)
{
const KartProperties* prop = kart_properties_manager->getKartById(group[n]);
std::string icon_path = "/karts/" + prop->getIdent() + "/" + prop->getIconFile();
w->addItem(prop->getName().c_str(), prop->getIdent().c_str(), icon_path.c_str());
}
}
// add random
w->addItem(_("Random Kart"), RANDOM_KART_ID, "/gui/random_kart.png");
w->updateItemDisplay();
// update players selections
const int num_players = m_kart_widgets.size();
for (int n=0; n<num_players; n++)
{
// player 0 is the one that can change the groups, leave his focus on the tabs
if (n > 0) GUIEngine::focusNothingForPlayer(n);
const std::string& selection = m_kart_widgets[n].getKartInternalName();
if (!w->setSelection( selection, n, true ))
{
std::cout << "Player " << n << " lost their selection when switching tabs!!!\n";
// For now, select a random kart in this case (TODO : maybe do something better? )
RandomGenerator random;
const int count = w->getItems().size();
if (count > 0)
{
const int randomID = random.get( count );
w->setSelection( randomID, n, n > 0 ); // preserve selection for players > 0 (player 0 is the one that can change the groups)
}
else
{
std::cerr << "WARNING : kart selection screen has 0 items in the ribbon\n";
}
}
} // end for
}
else if (name == "karts")
{
// make sure no other player selected the same identity or kart
//std::cout << "\n\n\\\\\\\\ Kart Selected ////\n";
//std::cout << "Making sure no other player has ident " << g_player_karts[playerID].getAssociatedPlayer()->getProfile()->getName() << std::endl;
const int amount = m_kart_widgets.size();
for (int n=0; n<amount; n++)
{
if (n == playerID) continue; // don't check a kart against itself
if (m_kart_widgets[n].isReady() &&
(m_kart_widgets[n].getAssociatedPlayer()->getProfile() ==
m_kart_widgets[playerID].getAssociatedPlayer()->getProfile() ||
sameKart(m_kart_widgets[n], m_kart_widgets[playerID])))
{
printf("\n***\n*** You can't select this identity or kart, someone already took it!! ***\n***\n\n");
//SFXType sound;
//SOUND_UGH SOUND_CRASH SOUND_USE_ANVIL SOUND_EXPLOSION SOUND_MOVE_MENU SOUND_SELECT_MENU
sfx_manager->quickSound( "use_anvil" );
return;
}
// If two PlayerKart entries are associated to the same ActivePlayer, something went wrong
assert(m_kart_widgets[n].getAssociatedPlayer() != m_kart_widgets[playerID].getAssociatedPlayer());
}
// Mark this player as ready to start
m_kart_widgets[playerID].markAsReady();
// validate choices to notify player of duplicates
const bool names_ok = validateIdentChoices();
const bool karts_ok = validateKartChoices();
if (!names_ok || !karts_ok) return;
// check if all players are ready
bool allPlayersReady = true;
for (int n=0; n<amount; n++)
{
if (!m_kart_widgets[n].isReady())
{
allPlayersReady = false;
break;
}
}
if (allPlayersReady) allPlayersDone();
}
else
{
// Transmit to all subwindows, maybe *they* care about this event
const int amount = m_kart_widgets.size();
for (int n=0; n<amount; n++)
{
m_kart_widgets[n].transmitEvent(widget, name, playerID);
}
// those events may mean that a player selection changed, so validate again
validateIdentChoices();
validateKartChoices();
}
}
// -----------------------------------------------------------------------------
void KartSelectionScreen::renumberKarts()

View File

@ -35,12 +35,19 @@ class KartSelectionScreen : public GUIEngine::Screen, public GUIEngine::ScreenSi
friend class PlayerNameSpinner;
friend class FocusDispatcher;
// ref only since we're adding them to a Screen, and the Screen will take ownership of these widgets
/** Contains the custom widget shown for every player. (ref only since we're adding them to a
* Screen, and the Screen will take ownership of these widgets)
*/
ptr_vector<PlayerKartWidget, REF> m_kart_widgets;
friend class GUIEngine::ScreenSingleton<KartSelectionScreen>;
KartSelectionScreen();
/** Stores whether any player confirmed their choice; then, some things are "frozen", for instance
* the selected kart group tab
*/
bool m_player_confirmed;
/** Called when all players selected their kart */
void allPlayersDone();
@ -48,11 +55,13 @@ class KartSelectionScreen : public GUIEngine::Screen, public GUIEngine::ScreenSi
void renumberKarts();
/** Checks identities chosen by players, making sure no duplicates are used.
\return Whether all choices are ok */
* \return Whether all choices are ok
*/
bool validateIdentChoices();
/** Checks karts chosen by players, making sure no duplicates are used.
\return Whether all choices are ok */
* \return Whether all choices are ok
*/
bool validateKartChoices();
public: