From 12bba40a991420585419332170eb7d9614194ca7 Mon Sep 17 00:00:00 2001
From: in1tiate <radwoodward@vikings.grayson.edu>
Date: Thu, 15 Apr 2021 10:25:25 -0500
Subject: [PATCH 1/6] implement case announcements

---
 include/aoclient.h | 29 ++++++++++++++++
 src/aoclient.cpp   |  1 +
 src/packets.cpp    | 83 ++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 111 insertions(+), 2 deletions(-)

diff --git a/include/aoclient.h b/include/aoclient.h
index 29eab62..a99c8e6 100644
--- a/include/aoclient.h
+++ b/include/aoclient.h
@@ -218,6 +218,27 @@ class AOClient : public QObject {
         {"SUPER",          ~0ULL      },
     };
 
+
+    /**
+     * @brief A structure for storing the client's casing alert preferences.
+     */
+    struct CasingPreferences {
+        QString caselist = ""; //!< The list of cases this user is willing to host (assuming they are also willing to CM) (unused)
+        bool cm = false; //!< If the user is willing to host cases (unused)
+        bool defense = false; //!< If the user is willing to defend a case / play as a defense attorney (or a co-defense attorney)
+        bool prosecution = false; //!< If the user is willing to prosecute a case / play as a prosecutor (or a co-prosecutor)
+        bool judge = false; //!< If the user is willing to judge a case
+        bool jury = false; //!< If the user is willing to be a member of the jury in a case
+        bool stenographer = false; //!< If the user is willing to be the stenographer of a case
+    };
+
+    /**
+     * @brief The client's casing alert preferences.
+     *
+     * @see The struct itself for more details.
+     */
+    CasingPreferences casing_preferences;
+
     /**
      * @brief If true, the client's in-character messages will have their word order randomised.
      */
@@ -472,6 +493,12 @@ class AOClient : public QObject {
     /// Implements [editing evidence](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#edit).
     void pktEditEvidence(AreaData* area, int argc, QStringList argv, AOPacket packet);
 
+    /// Implements [updating casing preferences](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#case-preferences-update).
+    void pktSetCase(AreaData* area, int argc, QStringList argv, AOPacket packet);
+
+    /// Implements [announcing a case](https://github.com/AttorneyOnline/docs/blob/master/docs/development/network.md#case-alert).
+    void pktAnnounceCase(AreaData* area, int argc, QStringList argv, AOPacket packet);
+
     ///@}
 
     /**
@@ -622,6 +649,8 @@ class AOClient : public QObject {
         {"PE",      {ACLFlags.value("NONE"), 3,  &AOClient::pktAddEvidence    }},
         {"DE",      {ACLFlags.value("NONE"), 1,  &AOClient::pktRemoveEvidence }},
         {"EE",      {ACLFlags.value("NONE"), 4,  &AOClient::pktEditEvidence   }},
+        {"SETCASE", {ACLFlags.value("NONE"), 7,  &AOClient::pktSetCase        }},
+        {"CASEA",   {ACLFlags.value("NONE"), 6,  &AOClient::pktAnnounceCase   }},
     };
 
     /**
diff --git a/src/aoclient.cpp b/src/aoclient.cpp
index 762d99f..4baacec 100644
--- a/src/aoclient.cpp
+++ b/src/aoclient.cpp
@@ -311,6 +311,7 @@ bool AOClient::checkAuth(unsigned long long acl_mask)
     return true;
 }
 
+
 QString AOClient::getIpid() { return ipid; }
 
 Server* AOClient::getServer() { return server; };
diff --git a/src/packets.cpp b/src/packets.cpp
index 51b32c3..221fa56 100644
--- a/src/packets.cpp
+++ b/src/packets.cpp
@@ -344,6 +344,87 @@ void AOClient::pktEditEvidence(AreaData* area, int argc, QStringList argv, AOPac
     sendEvidenceList(area);
 }
 
+void AOClient::pktSetCase(AreaData* area, int argc, QStringList argv, AOPacket packet)
+{
+    casing_preferences.caselist = argv[0];
+    QList<bool> prefs_list;
+    for (int i = 1; i <=6; i++) {
+        bool is_int = false;
+        bool pref = argv[i].toInt(&is_int);
+        if (!is_int)
+            return;
+        prefs_list.append(pref);
+    }
+    casing_preferences.cm           = prefs_list[0];
+    casing_preferences.defense      = prefs_list[1];
+    casing_preferences.prosecution  = prefs_list[2];
+    casing_preferences.judge        = prefs_list[3];
+    casing_preferences.jury         = prefs_list[4];
+    casing_preferences.stenographer = prefs_list[5];
+
+    qDebug() << casing_preferences.cm << casing_preferences.defense << casing_preferences.prosecution << casing_preferences.judge << casing_preferences.jury << casing_preferences.stenographer;
+}
+
+void AOClient::pktAnnounceCase(AreaData* area, int argc, QStringList argv, AOPacket packet)
+{
+    // the following is an example of the type of code AO2 makes me write
+    // you may wish to do something more pleasant rather than read this garbage
+    // taking a bath in battery acid, for instance
+    QString case_title = argv[0];
+    QStringList needed_roles;
+    QList<bool> needs_list;
+    for (int i = 1; i <=5; i++) {
+        bool is_int = false;
+        bool need = argv[i].toInt(&is_int);
+        if (!is_int)
+            return;
+        needs_list.append(need);
+    }
+    // this is stupid stupid stupid i hate this
+    if (needs_list[0]) {
+        needed_roles.append("defense attorney");
+    }
+    if (needs_list[1]) {
+        needed_roles.append("prosecutor");
+    }
+    if (needs_list[2]) {
+        needed_roles.append("judge");
+    }
+    if (needs_list[3]) {
+        needed_roles.append("jurors");
+    }
+    if (needs_list[4]) {
+        needed_roles.append("stenographer");
+    }
+    if (needed_roles.isEmpty()) {
+        return;
+    }
+
+    QString message = "=== Case Announcement ===\r\n" + ooc_name + " needs " + needed_roles.join(", ") + " for " + case_title + "!";
+
+    QList<AOClient*> clients_to_alert;
+    // this is morton the indented if statement
+    // please do not feed morton
+    for (AOClient* client : server->clients) {
+        if (((client->casing_preferences.defense && needed_roles.contains("defense attorney")) ||
+                    (client->casing_preferences.prosecution && needed_roles.contains("prosecutor")) ||
+                    (client->casing_preferences.judge && needed_roles.contains("judge")) ||
+                    (client->casing_preferences.jury && needed_roles.contains("jurors")) ||
+                    (client->casing_preferences.stenographer && needed_roles.contains("stenographer")))
+                && !clients_to_alert.contains(client))
+            clients_to_alert.append(client);
+    }
+    // morton is a little ugly but we love him anyway
+
+    for (AOClient* client : clients_to_alert) {
+        client->sendPacket(AOPacket("CASEA", {message, argv[1], argv[2], argv[3], argv[4], argv[5], "1"}));
+        // you may be thinking, "hey wait a minute the network protocol documentation doesn't mention that last argument!"
+        // if you are in fact thinking that, you are correct! it is not in the documentation!
+        // however for some inscrutable reason Attorney Online 2 will outright reject a CASEA packet that does not have
+        // at least 7 arguments despite only using the first 6. Cera, i kneel. you have truly broken me.
+    }
+}
+
 void AOClient::sendEvidenceList(AreaData* area)
 {
     for (AOClient* client : server->clients) {
@@ -719,6 +800,4 @@ void AOClient::updateJudgeLog(AreaData* area, AOClient* client, QString action)
         area->judgelog.append(logmessage);
     }
     else area->judgelog.append(logmessage);
-
-
 }

From c9700f236f9de518c1ac609ee0ef2ffcf0df36c3 Mon Sep 17 00:00:00 2001
From: in1tiate <radwoodward@vikings.grayson.edu>
Date: Thu, 15 Apr 2021 10:26:46 -0500
Subject: [PATCH 2/6] more cleanly handle empty input, snip debug call

---
 src/packets.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/packets.cpp b/src/packets.cpp
index 221fa56..15823cb 100644
--- a/src/packets.cpp
+++ b/src/packets.cpp
@@ -361,8 +361,6 @@ void AOClient::pktSetCase(AreaData* area, int argc, QStringList argv, AOPacket p
     casing_preferences.judge        = prefs_list[3];
     casing_preferences.jury         = prefs_list[4];
     casing_preferences.stenographer = prefs_list[5];
-
-    qDebug() << casing_preferences.cm << casing_preferences.defense << casing_preferences.prosecution << casing_preferences.judge << casing_preferences.jury << casing_preferences.stenographer;
 }
 
 void AOClient::pktAnnounceCase(AreaData* area, int argc, QStringList argv, AOPacket packet)
@@ -400,7 +398,7 @@ void AOClient::pktAnnounceCase(AreaData* area, int argc, QStringList argv, AOPac
         return;
     }
 
-    QString message = "=== Case Announcement ===\r\n" + ooc_name + " needs " + needed_roles.join(", ") + " for " + case_title + "!";
+    QString message = "=== Case Announcement ===\r\n" + ooc_name == "" ? current_char : ooc_name + " needs " + needed_roles.join(", ") + " for " + case_title == "" ? "a case" : case_title + "!";
 
     QList<AOClient*> clients_to_alert;
     // this is morton the indented if statement

From 06b70a35d520a42c0e4996488e2539c657188eef Mon Sep 17 00:00:00 2001
From: MangosArentLiterature
 <58055358+MangosArentLiterature@users.noreply.github.com>
Date: Thu, 15 Apr 2021 11:53:20 -0500
Subject: [PATCH 3/6] Make maximum_statements actually get loaded in

Also has the effect of making it changeable with /reload, hooray!
---
 src/server.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/server.cpp b/src/server.cpp
index 48d2787..f02c918 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -53,9 +53,7 @@ void Server::start()
 
     loadServerConfig();
     loadCommandConfig();
-
-    maximum_statements = config.value("maximum_statements", 50).toInt();
-
+    
     proxy = new WSProxy(port, ws_port, this);
     if(ws_port != -1)
         proxy->start();
@@ -235,6 +233,7 @@ void Server::loadServerConfig()
         zalgo_tolerance = config.value("zalgo_tolerance", "3").toInt(&zalgo_tolerance_conversion_success);
         if (!zalgo_tolerance_conversion_success)
             zalgo_tolerance = 3;
+    maximum_statements = config.value("maximum_statements", 10).toInt();
     config.endGroup();
 
     //Load dice values

From 122e993a8bda2b02a631b4f95abe8ea684662d2b Mon Sep 17 00:00:00 2001
From: in1tiate <radwoodward@vikings.grayson.edu>
Date: Thu, 15 Apr 2021 13:49:27 -0500
Subject: [PATCH 4/6] condense some overly long code (ty marisa)

---
 src/packets.cpp | 22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)

diff --git a/src/packets.cpp b/src/packets.cpp
index 15823cb..c2f77e0 100644
--- a/src/packets.cpp
+++ b/src/packets.cpp
@@ -378,25 +378,13 @@ void AOClient::pktAnnounceCase(AreaData* area, int argc, QStringList argv, AOPac
             return;
         needs_list.append(need);
     }
-    // this is stupid stupid stupid i hate this
-    if (needs_list[0]) {
-        needed_roles.append("defense attorney");
+    QStringList roles = {"defense attorney", "prosecutor", "judge", "jurors", "stenographer"};
+    for (int i = 0; i < 5; i++) {
+      if (needs_list[i])
+        needed_roles.append(roles[i]);
     }
-    if (needs_list[1]) {
-        needed_roles.append("prosecutor");
-    }
-    if (needs_list[2]) {
-        needed_roles.append("judge");
-    }
-    if (needs_list[3]) {
-        needed_roles.append("jurors");
-    }
-    if (needs_list[4]) {
-        needed_roles.append("stenographer");
-    }
-    if (needed_roles.isEmpty()) {
+    if (needed_roles.isEmpty())
         return;
-    }
 
     QString message = "=== Case Announcement ===\r\n" + ooc_name == "" ? current_char : ooc_name + " needs " + needed_roles.join(", ") + " for " + case_title == "" ? "a case" : case_title + "!";
 

From b1dfeec8f5a92c89bc77c6ba0b8e5de01ab76254 Mon Sep 17 00:00:00 2001
From: in1tiate <radwoodward@vikings.grayson.edu>
Date: Thu, 15 Apr 2021 13:58:41 -0500
Subject: [PATCH 5/6] euthanize morton, code cleanup

---
 include/aoclient.h | 19 ++-----------------
 src/packets.cpp    | 25 +++++--------------------
 2 files changed, 7 insertions(+), 37 deletions(-)

diff --git a/include/aoclient.h b/include/aoclient.h
index a99c8e6..79bcbf1 100644
--- a/include/aoclient.h
+++ b/include/aoclient.h
@@ -220,24 +220,9 @@ class AOClient : public QObject {
 
 
     /**
-     * @brief A structure for storing the client's casing alert preferences.
+     * @brief A list of 5 casing preferences (def, pro, judge, jury, steno)
      */
-    struct CasingPreferences {
-        QString caselist = ""; //!< The list of cases this user is willing to host (assuming they are also willing to CM) (unused)
-        bool cm = false; //!< If the user is willing to host cases (unused)
-        bool defense = false; //!< If the user is willing to defend a case / play as a defense attorney (or a co-defense attorney)
-        bool prosecution = false; //!< If the user is willing to prosecute a case / play as a prosecutor (or a co-prosecutor)
-        bool judge = false; //!< If the user is willing to judge a case
-        bool jury = false; //!< If the user is willing to be a member of the jury in a case
-        bool stenographer = false; //!< If the user is willing to be the stenographer of a case
-    };
-
-    /**
-     * @brief The client's casing alert preferences.
-     *
-     * @see The struct itself for more details.
-     */
-    CasingPreferences casing_preferences;
+    QList<bool> casing_preferences = {false, false, false, false, false};
 
     /**
      * @brief If true, the client's in-character messages will have their word order randomised.
diff --git a/src/packets.cpp b/src/packets.cpp
index c2f77e0..9790289 100644
--- a/src/packets.cpp
+++ b/src/packets.cpp
@@ -346,28 +346,19 @@ void AOClient::pktEditEvidence(AreaData* area, int argc, QStringList argv, AOPac
 
 void AOClient::pktSetCase(AreaData* area, int argc, QStringList argv, AOPacket packet)
 {
-    casing_preferences.caselist = argv[0];
     QList<bool> prefs_list;
-    for (int i = 1; i <=6; i++) {
+    for (int i = 2; i <=6; i++) {
         bool is_int = false;
         bool pref = argv[i].toInt(&is_int);
         if (!is_int)
             return;
         prefs_list.append(pref);
     }
-    casing_preferences.cm           = prefs_list[0];
-    casing_preferences.defense      = prefs_list[1];
-    casing_preferences.prosecution  = prefs_list[2];
-    casing_preferences.judge        = prefs_list[3];
-    casing_preferences.jury         = prefs_list[4];
-    casing_preferences.stenographer = prefs_list[5];
+    casing_preferences = prefs_list;
 }
 
 void AOClient::pktAnnounceCase(AreaData* area, int argc, QStringList argv, AOPacket packet)
 {
-    // the following is an example of the type of code AO2 makes me write
-    // you may wish to do something more pleasant rather than read this garbage
-    // taking a bath in battery acid, for instance
     QString case_title = argv[0];
     QStringList needed_roles;
     QList<bool> needs_list;
@@ -389,18 +380,12 @@ void AOClient::pktAnnounceCase(AreaData* area, int argc, QStringList argv, AOPac
     QString message = "=== Case Announcement ===\r\n" + ooc_name == "" ? current_char : ooc_name + " needs " + needed_roles.join(", ") + " for " + case_title == "" ? "a case" : case_title + "!";
 
     QList<AOClient*> clients_to_alert;
-    // this is morton the indented if statement
-    // please do not feed morton
+    // here lies morton, RIP
     for (AOClient* client : server->clients) {
-        if (((client->casing_preferences.defense && needed_roles.contains("defense attorney")) ||
-                    (client->casing_preferences.prosecution && needed_roles.contains("prosecutor")) ||
-                    (client->casing_preferences.judge && needed_roles.contains("judge")) ||
-                    (client->casing_preferences.jury && needed_roles.contains("jurors")) ||
-                    (client->casing_preferences.stenographer && needed_roles.contains("stenographer")))
-                && !clients_to_alert.contains(client))
+        QSet<bool> matches = client->casing_preferences.toSet().intersect(needs_list.toSet());
+        if (matches.isEmpty() && !clients_to_alert.contains(client))
             clients_to_alert.append(client);
     }
-    // morton is a little ugly but we love him anyway
 
     for (AOClient* client : clients_to_alert) {
         client->sendPacket(AOPacket("CASEA", {message, argv[1], argv[2], argv[3], argv[4], argv[5], "1"}));

From 38d73d92289303c5af79955cb4e97be85cb42b78 Mon Sep 17 00:00:00 2001
From: in1tiate <radwoodward@vikings.grayson.edu>
Date: Thu, 15 Apr 2021 14:10:53 -0500
Subject: [PATCH 6/6] cleanup

---
 src/packets.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/packets.cpp b/src/packets.cpp
index 9790289..6ddce8a 100644
--- a/src/packets.cpp
+++ b/src/packets.cpp
@@ -377,13 +377,14 @@ void AOClient::pktAnnounceCase(AreaData* area, int argc, QStringList argv, AOPac
     if (needed_roles.isEmpty())
         return;
 
-    QString message = "=== Case Announcement ===\r\n" + ooc_name == "" ? current_char : ooc_name + " needs " + needed_roles.join(", ") + " for " + case_title == "" ? "a case" : case_title + "!";
+    QString message = "=== Case Announcement ===\r\n" + (ooc_name == "" ? current_char : ooc_name) + " needs " + needed_roles.join(", ") + " for " + (case_title == "" ? "a case" : case_title) + "!";
 
     QList<AOClient*> clients_to_alert;
     // here lies morton, RIP
+    QSet<bool> needs_set = needs_list.toSet();
     for (AOClient* client : server->clients) {
-        QSet<bool> matches = client->casing_preferences.toSet().intersect(needs_list.toSet());
-        if (matches.isEmpty() && !clients_to_alert.contains(client))
+        QSet<bool> matches = client->casing_preferences.toSet().intersect(needs_set);
+        if (!matches.isEmpty() && !clients_to_alert.contains(client))
             clients_to_alert.append(client);
     }