diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2025-06-15 22:25:12 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2025-06-15 22:25:12 -0300 |
| commit | 5bdedeecea69a5892e3ce8854e85f78537cf7ca4 (patch) | |
| tree | 8e3517fea9c641a5f2a10a4d5c98ddb479876f3a /src/operate.cc | |
| parent | 3cc587ab8df80d42d9dab1a73a37701f565a3499 (diff) | |
| download | game-5bdedeecea69a5892e3ce8854e85f78537cf7ca4.tar.gz game-5bdedeecea69a5892e3ce8854e85f78537cf7ca4.zip | |
channel functions
Diffstat (limited to 'src/operate.cc')
| -rw-r--r-- | src/operate.cc | 391 |
1 files changed, 390 insertions, 1 deletions
diff --git a/src/operate.cc b/src/operate.cc index 2224ed4..7d18a94 100644 --- a/src/operate.cc +++ b/src/operate.cc @@ -3387,6 +3387,10 @@ void TChannel::operator=(const TChannel &Other){ } } +int GetNumberOfChannels(void){ + return Channels; +} + bool ChannelActive(int ChannelID){ if(ChannelID < 0 || ChannelID >= Channels){ error("ChannelActive: Ungültige Kanalnummer %d.\n", ChannelID); @@ -3396,6 +3400,44 @@ bool ChannelActive(int ChannelID){ return ChannelID <= 7 || Channel.at(ChannelID)->Moderator != 0; } +bool ChannelAvailable(int ChannelID, uint32 CharacterID){ + if(!ChannelActive(ChannelID)){ + return false; + } + + TPlayer *Player = GetPlayer(CharacterID); + if(Player == NULL){ + error("ChannelAvailable: Spieler existiert nicht.\n"); + return false; + } + + switch(ChannelID){ + case 0: return Player->Guild[0] != 0; + case 1: return CheckRight(Player->ID, READ_GAMEMASTER_CHANNEL); + case 2: return CheckRight(Player->ID, READ_TUTOR_CHANNEL); + case 3: return CheckRight(Player->ID, READ_GAMEMASTER_CHANNEL); + case 4: return true; + case 5: return true; + case 6: return true; + case 7: return true; + default:{ + if(ChannelID > 7){ + TChannel *Chan = Channel.at(ChannelID); + if(Chan->Moderator == CharacterID){ + return true; + } + + for(int i = 0; i < Chan->InvitedPlayers; i += 1){ + if(*Chan->InvitedPlayer.at(i) == CharacterID){ + return true; + } + } + } + return false; + } + } +} + const char *GetChannelName(int ChannelID, uint32 CharacterID){ static char ChannelName[40]; @@ -3410,7 +3452,7 @@ const char *GetChannelName(int ChannelID, uint32 CharacterID){ } switch(ChannelID){ - case 0: return Player->Guild; + case 0: return Player->Guild; case 1: return "Gamemaster"; case 2: return "Tutor"; case 3: return "Rule Violations"; @@ -3431,6 +3473,353 @@ const char *GetChannelName(int ChannelID, uint32 CharacterID){ } } +bool ChannelSubscribed(int ChannelID, uint32 CharacterID){ + if(!ChannelActive(ChannelID)){ + return false; + } + + TChannel *Chan = Channel.at(ChannelID); + for(int i = 0; i < Chan->Subscribers; i += 1){ + if(*Chan->Subscriber.at(i) == CharacterID){ + return true; + } + } + + return false; +} + +// TODO(fusion): This could have been a simple for loop? +uint32 GetFirstSubscriber(int ChannelID){ + CurrentChannelID = ChannelID; + CurrentSubscriberNumber = 0; + return GetNextSubscriber(); +} + +uint32 GetNextSubscriber(void){ + if(!ChannelActive(CurrentChannelID)){ + return 0; + } + + uint32 Subscriber = 0; + TChannel *Chan = Channel.at(CurrentChannelID); + if(CurrentSubscriberNumber < Chan->Subscribers){ + Subscriber = *Chan->Subscriber.at(CurrentSubscriberNumber); + CurrentSubscriberNumber += 1; + } + + return Subscriber; +} + +bool MayOpenChannel(uint32 CharacterID){ + if(Channels >= 0xFFFF){ + return false; + } + + if(!CheckRight(CharacterID, PREMIUM_ACCOUNT)){ + return false; + } + + // NOTE(fusion): Check if character is already moderator of any non-public channel. + for(int ChannelID = 8; ChannelID < Channels; ChannelID += 1){ + if(Channel.at(ChannelID)->Moderator == CharacterID){ + return false; + } + } + + return true; +} + +void OpenChannel(uint32 CharacterID){ + TPlayer *Player = GetPlayer(CharacterID); + if(Player == NULL){ + error("OpenChannel: Kreatur %u existiert nicht.\n", CharacterID); + throw ERROR; + } + + // TODO(fusion): Shouldn't we scan for a free channel before returning here? + if(Channels >= 0xFFFF){ + error("OpenChannel: Zu viele Kanäle.\n"); + throw ERROR; + } + + if(!CheckRight(CharacterID, PREMIUM_ACCOUNT)){ + throw NOPREMIUMACCOUNT; + } + + // NOTE(fusion): Check if character already has an open channel. + for(int ChannelID = 8; ChannelID < Channels; ChannelID += 1){ + if(Channel.at(ChannelID)->Moderator == CharacterID){ + SendOpenOwnChannel(Player->Connection, ChannelID); + return; + } + } + + // NOTE(fusion): Assign free channel to character. + int ChannelID = 8; + while(ChannelID < Channels){ + if(Channel.at(ChannelID)->Moderator == 0){ + break; + } + ChannelID += 1; + } + + if(ChannelID == Channels){ + Channels += 1; + } + + TChannel *Chan = Channel.at(ChannelID); + Chan->Moderator = CharacterID; + strcpy(Chan->ModeratorName, Player->Name); + *Chan->Subscriber.at(0) = CharacterID; + Chan->Subscribers = 1; + Chan->InvitedPlayers = 0; + SendOpenOwnChannel(Player->Connection, ChannelID); +} + +void CloseChannel(int ChannelID){ + if(ChannelID <= 7 || ChannelID >= Channels){ + error("CloseChannel: Ungültige ChannelID %d.\n", ChannelID); + return; + } + + TChannel *Chan = Channel.at(ChannelID); + if(Chan->Moderator == 0){ + error("CloseChannel: Kanal ist schon geschlossen.\n"); + return; + } + + Chan->Moderator = 0; +} + +void InviteToChannel(uint32 CharacterID, const char *Name){ + TPlayer *Player = GetPlayer(CharacterID); + if(Player == NULL){ + error("InviteToChannel: Kreatur %u existiert nicht.\n", CharacterID); + return; + } + + int ChannelID = 8; + while(ChannelID < Channels){ + if(Channel.at(ChannelID)->Moderator == CharacterID){ + break; + } + ChannelID += 1; + } + + if(ChannelID >= Channels){ + return; + } + + TPlayer *Other = NULL; + uint32 OtherID = 0; + bool IgnoreGamemasters = !CheckRight(Player->ID, READ_GAMEMASTER_CHANNEL); + switch(IdentifyPlayer(Name, false, IgnoreGamemasters, &Other)){ + case 0:{ // PLAYERFOUND ? + OtherID = Other->ID; + Name = Other->Name; + break; + } + + case -1:{ // PLAYERNOTONLINE ? + OtherID = GetCharacterID(Name); + if(OtherID == 0){ + throw PLAYERNOTEXISTING; + } + + Name = GetCharacterName(Name); + break; + } + + case -2:{ // NAMEAMBIGUOUS ? + OtherID = GetCharacterID(Name); + if(OtherID == 0){ + throw NAMEAMBIGUOUS; + } + + Name = GetCharacterName(Name); + break; + } + + default:{ + // TODO(fusion): This wasn't here but I don't think `IdentifyPlayer` + // can return anything else than 0, -1, and -2 anyways. + error("InviteToChannel: Ungültiger Rückgabewert von IdentifyPlayer.\n"); + throw ERROR; + } + } + + print(3, "Lade Spieler %s (%u) in Private Channel ein.\n", Name, OtherID); + if(CharacterID != OtherID){ + TChannel *Chan = Channel.at(ChannelID); + for(int i = 0; i < Chan->InvitedPlayers; i += 1){ + if(*Chan->InvitedPlayer.at(i) == OtherID){ + SendMessage(Player->Connection, TALK_INFO_MESSAGE, + "%s has already been invited.", Name); + return; + } + } + + *Chan->InvitedPlayer.at(Chan->InvitedPlayers) = OtherID; + Chan->InvitedPlayers += 1; + + SendMessage(Player->Connection, TALK_INFO_MESSAGE, + "%s has been invited.", Name); + + if(Other != NULL){ + SendMessage(Other->Connection, TALK_INFO_MESSAGE, + "%s invites you to %s private chat channel.", + Player->Name, (Player->Sex == 1 ? "his" : "her")); + } + } +} + +void ExcludeFromChannel(uint32 CharacterID, const char *Name){ + TPlayer *Player = GetPlayer(CharacterID); + if(Player == NULL){ + error("ExcludeFromChannel: Kreatur %u existiert nicht.\n", CharacterID); + return; + } + + int ChannelID = 8; + while(ChannelID < Channels){ + if(Channel.at(ChannelID)->Moderator == CharacterID){ + break; + } + ChannelID += 1; + } + + if(ChannelID >= Channels){ + return; + } + + // TODO(fusion): Why don't we call `GetCharacterName` as we do in + // `InviteToChannel`, when the player is not found? + TPlayer *Other = NULL; + uint32 OtherID = 0; + bool IgnoreGamemasters = !CheckRight(Player->ID, READ_GAMEMASTER_CHANNEL); + switch(IdentifyPlayer(Name, false, IgnoreGamemasters, &Other)){ + case 0:{ // PLAYERFOUND ? + OtherID = Other->ID; + Name = Other->Name; + break; + } + + case -1:{ // PLAYERNOTONLINE ? + OtherID = GetCharacterID(Name); + if(OtherID == 0){ + throw PLAYERNOTEXISTING; + } + break; + } + + case -2:{ // NAMEAMBIGUOUS ? + OtherID = GetCharacterID(Name); + if(OtherID == 0){ + throw NAMEAMBIGUOUS; + } + break; + } + + default:{ + // TODO(fusion): Same as `InviteToChannel`. + error("ExcludeFromChannel: Ungültiger Rückgabewert von IdentifyPlayer.\n"); + throw ERROR; + } + } + + print(3, "Schließe Spieler %s (%u) aus Private Channel aus.\n", Name, OtherID); + if(CharacterID != OtherID){ + bool Removed = false; + TChannel *Chan = Channel.at(ChannelID); + for(int i = 0; i < Chan->InvitedPlayers; i += 1){ + if(*Chan->InvitedPlayer.at(i) == OtherID){ + // NOTE(fusion): A little swap and pop action. + Chan->InvitedPlayers -= 1; + *Chan->InvitedPlayer.at(i) = *Chan->InvitedPlayer.at(Chan->InvitedPlayers); + Removed = true; + break; + } + } + + if(!Removed){ + SendMessage(Player->Connection, TALK_INFO_MESSAGE, + "%s has not been invited.", Name); + }else{ + SendMessage(Player->Connection, TALK_INFO_MESSAGE, + "%s has been excluded.", Name); + if(ChannelSubscribed(ChannelID, OtherID)){ + LeaveChannel(ChannelID, OtherID, false); + + // TODO(fusion): We didn't check if `Other` was NULL here. It + // seems that the player destructor will call `LeaveAllChannels` + // to make sure a player is only subscribed if it is still valid. + // Nevertheless, this check should become invisible to the branch + // predictor and it's always best to be safe. + if(Other != NULL){ + SendCloseChannel(Other->Connection, ChannelID); + } + } + } + } +} + +bool JoinChannel(int ChannelID, uint32 CharacterID){ + if(!ChannelActive(ChannelID)){ + throw ERROR; + } + + if(!ChannelAvailable(ChannelID, CharacterID)){ + throw NOTACCESSIBLE; + } + + TChannel *Chan = Channel.at(ChannelID); + if(!ChannelSubscribed(ChannelID, CharacterID)){ + *Chan->Subscriber.at(Chan->Subscribers) = CharacterID; + Chan->Subscribers += 1; + } + + return CharacterID == Chan->Moderator; +} + +void LeaveChannel(int ChannelID, uint32 CharacterID, bool Close){ + if(!ChannelActive(ChannelID)){ + return; + } + + TChannel *Chan = Channel.at(ChannelID); + for(int i = 0; i < Chan->Subscribers; i += 1){ + if(*Chan->Subscriber.at(i) == CharacterID){ + // NOTE(fusion): A little swap and pop action. + Chan->Subscribers -= 1; + *Chan->Subscriber.at(i) = *Chan->Subscriber.at(Chan->Subscribers); + break; + } + } + + if(Close && CharacterID == Chan->Moderator){ + for(int i = 0; i < Chan->Subscribers; i += 1){ + uint32 SubscriberID = *Chan->Subscriber.at(i); + TPlayer *Subscriber = GetPlayer(SubscriberID); + if(Subscriber != NULL){ + SendCloseChannel(Subscriber->Connection, ChannelID); + } + } + Chan->Subscribers = 0; + } + + if(ChannelID > 7 && Chan->Subscribers == 0){ + CloseChannel(ChannelID); + } +} + +void LeaveAllChannels(uint32 CharacterID){ + for(int ChannelID = 0; ChannelID < Channels; ChannelID += 1){ + if(ChannelActive(ChannelID)){ + LeaveChannel(ChannelID, CharacterID, false); + } + } +} + // Party // ============================================================================= TParty::TParty(void) : Member(0, 10, 10), InvitedPlayer(0, 10, 10) { |
