Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/Basescape/CraftSoldiersState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
#include "CraftInfoState.h"

#include "../CoopMod/CoopMenu.h"
#include "../CoopMod/connectionTCP.h" // coop
#include "../CoopMod/TransferSoldierMenu.h" // coop
#include "../Savegame/Vehicle.h"

namespace OpenXcom
Expand Down Expand Up @@ -201,6 +203,8 @@ CraftSoldiersState::CraftSoldiersState(Base *base, size_t craft)
_lstSoldiers->onRightArrowClick((ActionHandler)&CraftSoldiersState::lstItemsRightArrowClick);
_lstSoldiers->onMouseClick((ActionHandler)&CraftSoldiersState::lstSoldiersClick, 0);
_lstSoldiers->onMousePress((ActionHandler)&CraftSoldiersState::lstSoldiersMousePress);
// coop: transfer the hovered soldier to another player
_lstSoldiers->onKeyboardPress((ActionHandler)&CraftSoldiersState::lstSoldiersGiveUnitPress, Options::giveUnit);

// Coop mode: if the game is in coop and this base is not a coop base
if (_game->getCoopMod()->getCoopStatic() == true && _base->_coopBase == false && _game->getCoopMod()->getCoopCampaign() == true)
Expand Down Expand Up @@ -666,6 +670,25 @@ void CraftSoldiersState::lstSoldiersMousePress(Action *action)
}
}

/**
* Coop: opens the transfer-ownership dialog for the hovered soldier.
* @param action Pointer to an action.
*/
void CraftSoldiersState::lstSoldiersGiveUnitPress(Action *)
{
if (_game->getCoopMod()->getCoopStatic() != true || _game->getCoopMod()->getCoopCampaign() != true)
{
return;
}
unsigned int row = _lstSoldiers->getSelectedRow();
if (row >= _base->getSoldiers()->size())
{
return;
}
Soldier *soldier = _base->getSoldiers()->at(row);
_game->pushState(new TransferSoldierMenu(soldier, TransferSoldierMenu::resolveOwnerId(soldier)));
}

/**
* De-assign all soldiers from all craft located in the base (i.e. not out on a mission).
* @param action Pointer to an action.
Expand Down
2 changes: 2 additions & 0 deletions src/Basescape/CraftSoldiersState.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class CraftSoldiersState : public TouchState
void lstSoldiersClick(Action *action);
/// Handler for pressing-down a mouse-button in the list.
void lstSoldiersMousePress(Action *action);
/// Coop: handler for the give-unit key on the hovered soldier.
void lstSoldiersGiveUnitPress(Action *action);
/// Handler for clicking the De-assign All Soldiers button.
void btnDeassignAllSoldiersClick(Action *action);
void btnDeassignCraftSoldiersClick(Action *action);
Expand Down
22 changes: 22 additions & 0 deletions src/Basescape/SoldierInfoState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
#include "../Mod/RuleInterface.h"
#include "../Mod/RuleSoldier.h"
#include "../Savegame/SoldierDeath.h"
#include "../CoopMod/connectionTCP.h" // coop
#include "../CoopMod/TransferSoldierMenu.h" // coop

namespace OpenXcom
{
Expand Down Expand Up @@ -251,6 +253,8 @@ SoldierInfoState::SoldierInfoState(Base *base, size_t soldierId, bool forceLimit
_btnOk->setText(tr("STR_OK"));
_btnOk->onMouseClick((ActionHandler)&SoldierInfoState::btnOkClick);
_btnOk->onKeyboardPress((ActionHandler)&SoldierInfoState::btnOkClick, Options::keyCancel);
// coop: transfer this soldier to another player
_btnOk->onKeyboardPress((ActionHandler)&SoldierInfoState::btnGiveUnitPress, Options::giveUnit);

_btnPrev->setText("<<");
if (_base == 0)
Expand Down Expand Up @@ -688,6 +692,24 @@ void SoldierInfoState::btnOkClick(Action *)
}
}

/**
* Coop: opens the transfer-ownership dialog for this soldier.
* @param action Pointer to an action.
*/
void SoldierInfoState::btnGiveUnitPress(Action *)
{
if (_game->getCoopMod()->getCoopStatic() != true || _game->getCoopMod()->getCoopCampaign() != true)
{
return;
}
// Only living soldiers in a base can be transferred (not the memorial view).
if (!_soldier || _base == 0 || _soldier->getDeath())
{
return;
}
_game->pushState(new TransferSoldierMenu(_soldier, TransferSoldierMenu::resolveOwnerId(_soldier)));
}

/**
* Goes to the previous soldier.
* @param action Pointer to an action.
Expand Down
2 changes: 2 additions & 0 deletions src/Basescape/SoldierInfoState.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class SoldierInfoState : public State
void edtSoldierChange(Action *action);
/// Handler for clicking the OK button.
void btnOkClick(Action *action);
/// Coop: handler for the give-unit key on this soldier.
void btnGiveUnitPress(Action *action);
/// Handler for clicking the Previous button.
void btnPrevClick(Action *action);
/// Handler for clicking the Next button.
Expand Down
41 changes: 41 additions & 0 deletions src/Basescape/SoldiersState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
#include "../Engine/Unicode.h"

#include "../Savegame/Vehicle.h"
#include "../CoopMod/connectionTCP.h" // coop
#include "../CoopMod/TransferSoldierMenu.h" // coop

namespace OpenXcom
{
Expand Down Expand Up @@ -243,6 +245,8 @@ SoldiersState::SoldiersState(Base *base) : _base(base), _origSoldierOrder(*_base
_lstSoldiers->onMouseClick((ActionHandler)&SoldiersState::lstSoldiersClick);
_lstSoldiers->onMouseClick((ActionHandler)&SoldiersState::lstSoldiersClick, SDL_BUTTON_RIGHT);
_lstSoldiers->onMousePress((ActionHandler)&SoldiersState::lstSoldiersMousePress);
// coop: transfer the hovered soldier to another player
_lstSoldiers->onKeyboardPress((ActionHandler)&SoldiersState::lstSoldiersGiveUnitPress, Options::giveUnit);


// Coop mode: if the game is in coop and this base is not a coop base
Expand Down Expand Up @@ -707,6 +711,23 @@ void SoldiersState::btnOkClick(Action *)

// save changes
basehost_save->saveCoopToMemory(filename, _game->getMod(), filename);

// Fix B (Bug 1): the CoopCraft assignments above land ONLY in the
// "basehost" blob (the client's copy of the HOST world). The client's
// OWN-world blob (client_<saveID>_<host>.data) - which GeoscapeState
// reloads at mission end - is never touched here, so a guest's craft
// assignment reverts to its stale value and the soldier is unassigned
// from the skyranger after a battle. Mirror each guest's assignment
// into the own-world blob so it survives the reload.
std::map<std::string, std::pair<int, std::string>> guestCraftAssignments;
for (auto* soldier : *_base->getSoldiers())
{
Craft* assignedCraft = soldier->getCraft();
guestCraftAssignments[soldier->getName()] = std::make_pair(
assignedCraft ? assignedCraft->getId() : -1,
assignedCraft ? assignedCraft->getType() : std::string());
}
_game->getCoopMod()->syncOwnWorldGuestCraft(_base->_coop_base_id, guestCraftAssignments);

// estetaan dublikaatio tallenuksen jalkeen...
auto& soldiers = *_base->getSoldiers();
Expand Down Expand Up @@ -935,4 +956,24 @@ void SoldiersState::lstSoldiersMousePress(Action *action)
}
}


/**
* Coop: opens the transfer-ownership dialog for the hovered soldier.
* @param action Pointer to an action.
*/
void SoldiersState::lstSoldiersGiveUnitPress(Action *)
{
if (_game->getCoopMod()->getCoopStatic() != true || _game->getCoopMod()->getCoopCampaign() != true)
{
return;
}
unsigned int row = _lstSoldiers->getSelectedRow();
if (row >= _filteredListOfSoldiers.size())
{
return;
}
Soldier *soldier = _filteredListOfSoldiers[row];
_game->pushState(new TransferSoldierMenu(soldier, TransferSoldierMenu::resolveOwnerId(soldier)));
}

}
2 changes: 2 additions & 0 deletions src/Basescape/SoldiersState.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class SoldiersState : public State
void lstSoldiersClick(Action *action);
/// Handler for pressing-down a mouse-button in the list.
void lstSoldiersMousePress(Action *action);
/// Coop: handler for the give-unit key on the hovered soldier.
void lstSoldiersGiveUnitPress(Action *action);
};

}
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ set(coopmod_src
CoopMod/connectionUDP/rendezvous_client.cpp
CoopMod/connectionUDP/rendezvous_config.cpp
CoopMod/connectionUDP/rendezvous_server.cpp
CoopMod/TransferNoticeState.cpp
CoopMod/TransferSoldierMenu.cpp
)

set ( engine_src
Expand Down
114 changes: 114 additions & 0 deletions src/CoopMod/TransferNoticeState.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2010-2016 OpenXcom Developers.
* Copyright 2023-2026 XComCoopTeam (https://www.moddb.com/mods/openxcom-coop-mod)
*
* This file is part of OpenXcom.
*
* OpenXcom is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenXcom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TransferNoticeState.h"

#include "../Engine/Action.h"
#include "../Engine/Game.h"
#include "../Engine/Options.h"
#include "../Interface/Text.h"
#include "../Interface/TextButton.h"
#include "../Interface/Window.h"
#include "../Geoscape/GeoscapeState.h"
#include "../Mod/Mod.h"
#include "../Mod/RuleInterface.h"
#include "../Savegame/SavedGame.h"
#include "../Savegame/SavedBattleGame.h"

namespace OpenXcom
{

TransferNoticeState::TransferNoticeState(const std::string &message)
{
_screen = false;

_window = new Window(this, 256, 88, 32, 56, POPUP_BOTH);
_txtMessage = new Text(236, 40, 42, 68);
_btnOk = new TextButton(120, 16, 100, 118);

// Adopt the palette of whatever screen we're over - no palette swap, no
// flicker, works on geoscape, basescape and the peer-base view alike.
// Color indices come from an interface designed for that palette, or the
// text is illegible (sackSoldier's dark-blue text over PAL_GEOSCAPE).
// Skip other notices when deciding the context: with two notices stacked,
// the "top state" is the first notice, not the screen underneath.
std::string category = "sackSoldier";
std::string textElement = "text";
if (_game->getSavedGame() && _game->getSavedGame()->getSavedBattle())
{
// In the battlescape, match the coop lobby window exactly (geoscape
// interface with alterPal, saveMenus colors, under the battle palette).
setInterface("geoscape", true, _game->getSavedGame()->getSavedBattle());
category = "saveMenus";
}
else
{
// Adopt the palette of whatever screen we're over - no palette swap, no
// flicker, works on geoscape, basescape and the peer-base view alike.
// Skip other notices when deciding the context: with two notices stacked,
// the "top state" is the first notice, not the screen underneath.
State* context = nullptr;
for (auto it = _game->getStates().rbegin(); it != _game->getStates().rend(); ++it)
{
if (dynamic_cast<TransferNoticeState*>(*it) == nullptr)
{
context = *it;
break;
}
}
if (context)
{
setStatePalette(context->getPalette());
if (dynamic_cast<GeoscapeState*>(context))
{
category = "geoManufactureComplete"; // standard geoscape popup colors
textElement = "text1";
}
}
}
_category = category;

add(_window, "window", category);
add(_txtMessage, textElement, category);
add(_btnOk, "button", category);

centerAllSurfaces();
setWindowBackground(_window, category);
if (_game->getSavedGame() && _game->getSavedGame()->getSavedBattle())
{
// Same as the coop lobby in battle: uniform battlescape theme.
applyBattlescapeTheme(category);
}

_txtMessage->setAlign(ALIGN_CENTER);
_txtMessage->setWordWrap(true);
_txtMessage->setText(message);

_btnOk->setText(tr("STR_OK"));
_btnOk->onMouseClick((ActionHandler)&TransferNoticeState::btnOkClick);
_btnOk->onKeyboardPress((ActionHandler)&TransferNoticeState::btnOkClick, Options::keyOk);
_btnOk->onKeyboardPress((ActionHandler)&TransferNoticeState::btnOkClick, Options::keyCancel);
}

void TransferNoticeState::btnOkClick(Action *)
{
_game->popState();
}

}
52 changes: 52 additions & 0 deletions src/CoopMod/TransferNoticeState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once
/*
* Copyright 2010-2016 OpenXcom Developers.
* Copyright 2023-2026 XComCoopTeam (https://www.moddb.com/mods/openxcom-coop-mod)
*
* This file is part of OpenXcom.
*
* OpenXcom is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenXcom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../Engine/State.h"
#include <string>

namespace OpenXcom
{

class Window;
class Text;
class TextButton;

/**
* Small co-op notification popup ("X transferred ownership of Y to you...").
* Adopts the palette of the state it is pushed over, so it can appear on any
* screen (geoscape, basescape, peer-base view) without a palette flash. As a
* side effect, closing it re-inits the state below, refreshing soldier lists.
*/
class TransferNoticeState : public State
{
private:
Window *_window;
Text *_txtMessage;
TextButton *_btnOk;
std::string _category;

public:
TransferNoticeState(const std::string &message);
void btnOkClick(Action *action);
/// Interface category the widgets were themed with (test introspection).
const std::string &getCategory() const { return _category; }
};

}
Loading