Skip to content
Merged
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
3 changes: 3 additions & 0 deletions Changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4056,3 +4056,6 @@ When setting a property like MORE to the a spell or skill defname, trying to rea

24-09-2025, Mulambo
- Added: ARGN3 for trigger @PersonalSpace [R/W], allowing to bypass maximum stamina requirement (default 1 = require maximum stamina) when stepping over players.

26-09-2025, Mulambo
- Changed: When the party leader gets disconnected (or deleted), new party leader is appointed, instead of disbanding the party.
11 changes: 2 additions & 9 deletions src/game/chars/CChar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ CChar::~CChar()

if (m_pParty)
{
m_pParty->RemoveMember( GetUID(), GetUID() );
m_pParty->RemoveMember(GetUID(), GetUID(), false);
m_pParty = nullptr;
}
//Guild_Resign(MEMORY_GUILD); Moved to the ClearPlayer method otherwise it will cause a server crash because the deleted player will still be found in the guild list.
Expand Down Expand Up @@ -493,13 +493,6 @@ void CChar::ClientDetach()
if ( !IsClientActive() )
return;

if ( m_pParty && m_pParty->IsPartyMaster( this ))
{
// Party must disband if the master is logged out.
m_pParty->Disband(GetUID());
m_pParty = nullptr;
}

// If this char is on a IT_SHIP then we need to stop the ship !
if ( m_pArea && m_pArea->IsFlag( REGION_FLAG_SHIP ))
{
Expand Down Expand Up @@ -548,7 +541,7 @@ void CChar::SetDisconnected(CSector* pNewSector)

if (m_pParty)
{
m_pParty->RemoveMember( GetUID(), GetUID() );
m_pParty->RemoveMember(GetUID(), GetUID(), false);
m_pParty = nullptr;
}

Expand Down
62 changes: 43 additions & 19 deletions src/game/clients/CParty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,43 +293,60 @@ void CPartyDef::AcceptMember( CChar *pChar )
SendAddList(nullptr);
}

bool CPartyDef::RemoveMember( CUID uidRemove, CUID uidCommand )
bool CPartyDef::RemoveMember(const CUID& uidRemove, const CUID &uidCommand, const bool fDisband)
{
ADDTOCALLSTACK("CPartyDef::RemoveMember");
// ARGS:
// uidRemove = Who is being removed.
// uidCommand = who removed this person (only the master or self can remove)
//
// NOTE: remove of the master will cause the party to disband.

if ( m_Chars.GetCharCount() <= 0 )
if (m_Chars.GetCharCount() <= 0)
return false;

CUID uidMaster(GetMaster());
if ( (uidRemove != uidCommand) && (uidCommand != uidMaster) )
const CUID uidMaster(GetMaster());
if (uidRemove != uidCommand && uidCommand != uidMaster)
return false;

CChar *pCharRemove(uidRemove.CharFind());
if ( !pCharRemove )
if (!pCharRemove)
return false;
if ( !IsInParty(pCharRemove) )
if (!IsInParty(pCharRemove))
return false;
if ( uidRemove == uidMaster )
if (fDisband && uidRemove == uidMaster)
return Disband(uidMaster);

CChar *pSrc = uidCommand.CharFind();
if ( pSrc && IsTrigUsed(TRIGGER_PARTYREMOVE) )
if (pSrc && IsTrigUsed(TRIGGER_PARTYREMOVE))
{
if ( pCharRemove->OnTrigger(CTRIG_PartyRemove, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc) == TRIGRET_RET_TRUE )
if (pCharRemove->OnTrigger(CTRIG_PartyRemove, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pSrc) == TRIGRET_RET_TRUE)
return false;
}
if ( IsTrigUsed(TRIGGER_PARTYLEAVE) )
if (IsTrigUsed(TRIGGER_PARTYLEAVE))
{
if ( pCharRemove->OnTrigger(CTRIG_PartyLeave, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharRemove) == TRIGRET_RET_TRUE )
if (pCharRemove->OnTrigger(CTRIG_PartyLeave, CScriptParserBufs::GetCScriptTriggerArgsPtr(), pCharRemove) == TRIGRET_RET_TRUE)
return false;
}

// Remove it from the party
// If the party leader left, try to promote new character to be the leader.
tchar *pszChangeLeader = Str_GetTemp();
if (uidRemove == uidMaster)
{
// No valid character on second position in the party, disband it.
if (!m_Chars.IsValidIndex(1))
return Disband(uidMaster);

const CUID newMaster(m_Chars.GetChar(1));
CChar *pCharNewMaster(newMaster.CharFind());

// Cannot find new leader's character, disband it.
if (!pCharNewMaster)
return Disband(uidMaster);

// If new master wasn't appointed, disband the party.
if (!SetMaster(pCharNewMaster))
return Disband(uidMaster);

snprintf(pszChangeLeader, Str_TempLength(), g_Cfg.GetDefaultMsg(DEFMSG_PARTY_CHANGE_LEADER), pCharNewMaster->GetName());
}

// Remove the character from the party.
SendRemoveList(pCharRemove, true);
DetachChar(pCharRemove);
pCharRemove->SysMessageDefault(DEFMSG_PARTY_LEAVE_2);
Expand All @@ -338,13 +355,20 @@ bool CPartyDef::RemoveMember( CUID uidRemove, CUID uidCommand )
snprintf(pszMsg, Str_TempLength(), g_Cfg.GetDefaultMsg(DEFMSG_PARTY_LEAVE_1), pCharRemove->GetName());
SysMessageAll(pszMsg);

if ( m_Chars.GetCharCount() <= 1 )
// Disband the party if I'm alone.
if (m_Chars.GetCharCount() <= 1)
{
// Disband the party
SysMessageAll(g_Cfg.GetDefaultMsg(DEFMSG_PARTY_LEAVE_LAST_PERSON));
return Disband(uidMaster);
}

// Notify about change in party leadership (we need to do it here, so it doesn't get sent to the previous leader).
if (uidRemove == uidMaster)
SysMessageAll(pszChangeLeader);

// We still have a party, notify other members about removal.
SendRemoveList(pCharRemove, false);

return true;
}

Expand Down
15 changes: 14 additions & 1 deletion src/game/clients/CParty.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,20 @@ class CPartyDef : public CSObjListRec, public CScriptObj

// Commands
bool Disband( CUID uidMaster );
bool RemoveMember( CUID uidRemove, CUID uidCommand );

/**
* Removes a member from party. If leader is removed, it tries to pass leader to another character.
*
* @note We need to switch characters at leader position before we remove him, because original client doesn't like removing leader and thinks, the party
* was disbanded (Classic UO doesn't care though).
*
* @param uidRemove Character being removed.
* @param uidCommand Character, who issued the removal (leader or self).
* @param fDisband If set to false, we try to change leader instead of disbanding the party.
*
* @return True if successfully removed, false otherwise.
*/
bool RemoveMember(const CUID& uidRemove, const CUID &uidCommand, bool fDisband = true);
void AcceptMember( CChar * pChar );
void SetLootFlag( CChar * pChar, bool fSet );
bool GetLootFlag( const CChar * pChar );
Expand Down
1 change: 1 addition & 0 deletions src/network/receive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2655,6 +2655,7 @@ bool PacketPartyMessage::onReceive(CNetState* net)
client->addTarget( CLIMODE_TARG_PARTY_ADD, g_Cfg.GetDefaultMsg(DEFMSG_PARTY_TARG_WHO), false, false);
break;

// This doesn't happen, since disbanding the party is handled by client sending a packet to remove the party master.
case PARTYMSG_Disband:
if (character->m_pParty == nullptr)
return false;
Expand Down
1 change: 1 addition & 0 deletions src/tables/defmessages.tbl
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,7 @@ MSG(PARTY_ADDED, "You have been added to the party.")
MSG(PARTY_DECLINE_1, "%s: Does not wish to join the party.")
MSG(PARTY_DECLINE_2, "You notify %s that you do not wish to join the party.")
MSG(PARTY_DISBANDED, "Your party has disbanded.")
MSG(PARTY_CHANGE_LEADER, "%s was appointed the new party leader.")
MSG(PARTY_INVITE, "You have invited %s to join the party.")
MSG(PARTY_INVITE_TARG, "%s: You are invited to join the party. Type /accept to join or /decline to decline the offer.")
MSG(PARTY_JOINED, "%s: joined the party.")
Expand Down