summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrubidium <rubidium@openttd.org>2008-08-11 22:45:11 +0000
committerrubidium <rubidium@openttd.org>2008-08-11 22:45:11 +0000
commitd0c1a989a4226cc06a50b1a8c95b9ca8f0b9599e (patch)
tree96b3b7c215db821df9b9268eb9af5a55ee1200ce /src
parent6995365535370da08116d49a30ebd84d56e7d8ff (diff)
downloadopenttd-d0c1a989a4226cc06a50b1a8c95b9ca8f0b9599e.tar.xz
(svn r14047) -Codechange: move chatmessage handling to the network directory as that's the only case chat messages are used. Furthermore remove any trace of chatmessages when compiling without network support.
Diffstat (limited to 'src')
-rw-r--r--src/date.cpp13
-rw-r--r--src/gfx.cpp11
-rw-r--r--src/misc.cpp5
-rw-r--r--src/network/network.cpp2
-rw-r--r--src/network/network.h2
-rw-r--r--src/network/network_chat_gui.cpp487
-rw-r--r--src/network/network_func.h5
-rw-r--r--src/network/network_gui.cpp248
-rw-r--r--src/texteff.cpp226
-rw-r--r--src/texteff.hpp5
-rw-r--r--src/video/cocoa/event.mm2
-rw-r--r--src/video/sdl_v.cpp2
-rw-r--r--src/video/win32_v.cpp2
-rw-r--r--src/window.cpp3
14 files changed, 522 insertions, 491 deletions
diff --git a/src/date.cpp b/src/date.cpp
index f6f4f19aa..0e99e314a 100644
--- a/src/date.cpp
+++ b/src/date.cpp
@@ -33,10 +33,6 @@ void SetDate(Date date)
ConvertDateToYMD(date, &ymd);
_cur_year = ymd.year;
_cur_month = ymd.month;
-#ifdef ENABLE_NETWORK
- _network_last_advertise_frame = 0;
- _network_need_advertise = true;
-#endif /* ENABLE_NETWORK */
}
#define M(a, b) ((a << 5) | b)
@@ -161,7 +157,6 @@ Date ConvertYMDToDate(Year year, Month month, Day day)
/** Functions used by the IncreaseDate function */
extern void WaypointsDailyLoop();
-extern void ChatMessageDailyLoop();
extern void EnginesDailyLoop();
extern void DisasterDailyLoop();
@@ -228,7 +223,9 @@ void IncreaseDate()
/* yeah, increase day counter and call various daily loops */
_date++;
- ChatMessageDailyLoop();
+#ifdef ENABLE_NETWORK
+ NetworkChatMessageDailyLoop();
+#endif /* ENABLE_NETWORK */
DisasterDailyLoop();
WaypointsDailyLoop();
@@ -296,9 +293,11 @@ void IncreaseDate()
_date -= days_this_year;
FOR_ALL_VEHICLES(v) v->date_of_last_service -= days_this_year;
+#ifdef ENABLE_NETWORK
/* Because the _date wraps here, and text-messages expire by game-days, we have to clean out
* all of them if the date is set back, else those messages will hang for ever */
- InitChatMessage();
+ NetworkInitChatMessage();
+#endif /* ENABLE_NETWORK */
}
if (_settings_client.gui.auto_euro) CheckSwitchToEuro();
diff --git a/src/gfx.cpp b/src/gfx.cpp
index 3da6d25a2..79e67586e 100644
--- a/src/gfx.cpp
+++ b/src/gfx.cpp
@@ -20,6 +20,7 @@
#include "core/alloc_func.hpp"
#include "core/sort_func.hpp"
#include "landscape_type.h"
+#include "network/network_func.h"
#include "table/palettes.h"
#include "table/sprites.h"
@@ -81,7 +82,10 @@ void GfxScroll(int left, int top, int width, int height, int xo, int yo)
if (xo == 0 && yo == 0) return;
if (_cursor.visible) UndrawMouseCursor();
- UndrawChatMessage();
+
+#ifdef ENABLE_NETWORK
+ NetworkUndrawChatMessage();
+#endif /* ENABLE_NETWORK */
blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
/* This part of the screen is now dirty. */
@@ -1252,7 +1256,10 @@ void RedrawScreenRect(int left, int top, int right, int bottom)
UndrawMouseCursor();
}
}
- UndrawChatMessage();
+
+#ifdef ENABLE_NETWORK
+ NetworkUndrawChatMessage();
+#endif /* ENABLE_NETWORK */
DrawOverlappedWindowForAll(left, top, right, bottom);
diff --git a/src/misc.cpp b/src/misc.cpp
index 3e89366b9..463b05707 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -29,6 +29,7 @@
#include "animated_tile_func.h"
#include "tilehighlight_func.h"
#include "core/bitmath_func.hpp"
+#include "network/network_func.h"
#include "table/strings.h"
#include "table/sprites.h"
@@ -105,7 +106,9 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date)
InitializeCheats();
InitTextEffects();
- InitChatMessage();
+#ifdef ENABLE_NETWORK
+ NetworkInitChatMessage();
+#endif /* ENABLE_NETWORK */
InitializeAnimatedTiles();
InitializeLandscapeVariables(false);
diff --git a/src/network/network.cpp b/src/network/network.cpp
index 437023636..54f6dd1b4 100644
--- a/src/network/network.cpp
+++ b/src/network/network.cpp
@@ -230,7 +230,7 @@ void CDECL NetworkTextMessage(NetworkAction action, ConsoleColour color, bool se
DebugDumpCommands("ddc:cmsg:%d;%d;%s\n", _date, _date_fract, message);
IConsolePrintF(color, "%s", message);
- AddChatMessage(color, duration, "%s", message);
+ NetworkAddChatMessage(color, duration, "%s", message);
}
// Calculate the frame-lag of a client
diff --git a/src/network/network.h b/src/network/network.h
index 8de34f112..9c913ceb6 100644
--- a/src/network/network.h
+++ b/src/network/network.h
@@ -11,6 +11,7 @@
void NetworkStartUp();
void NetworkShutDown();
+void NetworkDrawChatMessage();
extern bool _networking; ///< are we in networking mode?
extern bool _network_server; ///< network-server is active
@@ -23,6 +24,7 @@ extern bool _is_network_server; ///< Does this client wants to be a network-ser
static inline void NetworkStartUp() {}
static inline void NetworkShutDown() {}
+static inline void NetworkDrawChatMessage() {}
#define _networking 0
#define _network_server 0
diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp
new file mode 100644
index 000000000..2d4289ffb
--- /dev/null
+++ b/src/network/network_chat_gui.cpp
@@ -0,0 +1,487 @@
+/* $Id$ */
+
+/** @file network_chat_gui.cpp GUI for handling chat messages. */
+
+#include <stdarg.h> /* va_list */
+
+#ifdef ENABLE_NETWORK
+
+#include "../stdafx.h"
+#include "network.h"
+#include "network_type.h"
+#include "../date_func.h"
+#include "../gfx_func.h"
+#include "../strings_func.h"
+#include "../blitter/factory.hpp"
+#include "../console_func.h"
+#include "../video/video_driver.hpp"
+#include "../table/sprites.h"
+#include "../window_gui.h"
+#include "../textbuf_gui.h"
+#include "../querystring_gui.h"
+#include "../town.h"
+#include "../window_func.h"
+#include "network_internal.h"
+#include "network_client.h"
+
+#include "table/strings.h"
+
+enum {
+ MAX_CHAT_MESSAGES = 10,
+};
+
+struct ChatMessage {
+ char message[NETWORK_CHAT_LENGTH];
+ uint16 color;
+ Date end_date;
+};
+
+/* used for chat window */
+static ChatMessage _chatmsg_list[MAX_CHAT_MESSAGES];
+static bool _chatmessage_dirty = false;
+static bool _chatmessage_visible = false;
+static bool _chat_tab_completion_active;
+
+/* The chatbox grows from the bottom so the coordinates are pixels from
+ * the left and pixels from the bottom. The height is the maximum height */
+static const PointDimension _chatmsg_box = {10, 30, 500, 150};
+static uint8 _chatmessage_backup[150 * 500 * 6]; // (height * width)
+
+static inline uint GetChatMessageCount()
+{
+ uint i = 0;
+ for (; i < MAX_CHAT_MESSAGES; i++) {
+ if (_chatmsg_list[i].message[0] == '\0') break;
+ }
+
+ return i;
+}
+
+/**
+ * Add a text message to the 'chat window' to be shown
+ * @param color The colour this message is to be shown in
+ * @param duration The duration of the chat message in game-days
+ * @param message message itself in printf() style
+ */
+void CDECL NetworkAddChatMessage(uint16 color, uint8 duration, const char *message, ...)
+{
+ char buf[NETWORK_CHAT_LENGTH];
+ const char *bufp;
+ va_list va;
+ uint msg_count;
+ uint16 lines;
+
+ va_start(va, message);
+ vsnprintf(buf, lengthof(buf), message, va);
+ va_end(va);
+
+
+ Utf8TrimString(buf, NETWORK_CHAT_LENGTH);
+
+ /* Force linebreaks for strings that are too long */
+ lines = GB(FormatStringLinebreaks(buf, _chatmsg_box.width - 8), 0, 16) + 1;
+ if (lines >= MAX_CHAT_MESSAGES) return;
+
+ msg_count = GetChatMessageCount();
+ /* We want to add more chat messages than there is free space for, remove 'old' */
+ if (lines > MAX_CHAT_MESSAGES - msg_count) {
+ int i = lines - (MAX_CHAT_MESSAGES - msg_count);
+ memmove(&_chatmsg_list[0], &_chatmsg_list[i], sizeof(_chatmsg_list[0]) * (msg_count - i));
+ msg_count = MAX_CHAT_MESSAGES - lines;
+ }
+
+ for (bufp = buf; lines != 0; lines--) {
+ ChatMessage *cmsg = &_chatmsg_list[msg_count++];
+ ttd_strlcpy(cmsg->message, bufp, sizeof(cmsg->message));
+
+ /* The default colour for a message is player colour. Replace this with
+ * white for any additional lines */
+ cmsg->color = (bufp == buf && color & IS_PALETTE_COLOR) ? color : (0x1D - 15) | IS_PALETTE_COLOR;
+ cmsg->end_date = _date + duration;
+
+ bufp += strlen(bufp) + 1; // jump to 'next line' in the formatted string
+ }
+
+ _chatmessage_dirty = true;
+}
+
+void NetworkInitChatMessage()
+{
+ for (uint i = 0; i < MAX_CHAT_MESSAGES; i++) {
+ _chatmsg_list[i].message[0] = '\0';
+ }
+}
+
+/** Hide the chatbox */
+void NetworkUndrawChatMessage()
+{
+ if (_chatmessage_visible) {
+ Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
+ /* Sometimes we also need to hide the cursor
+ * This is because both textmessage and the cursor take a shot of the
+ * screen before drawing.
+ * Now the textmessage takes his shot and paints his data before the cursor
+ * does, so in the shot of the cursor is the screen-data of the textmessage
+ * included when the cursor hangs somewhere over the textmessage. To
+ * avoid wrong repaints, we undraw the cursor in that case, and everything
+ * looks nicely ;)
+ * (and now hope this story above makes sense to you ;))
+ */
+
+ if (_cursor.visible) {
+ if (_cursor.draw_pos.x + _cursor.draw_size.x >= _chatmsg_box.x &&
+ _cursor.draw_pos.x <= _chatmsg_box.x + _chatmsg_box.width &&
+ _cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _chatmsg_box.y - _chatmsg_box.height &&
+ _cursor.draw_pos.y <= _screen.height - _chatmsg_box.y) {
+ UndrawMouseCursor();
+ }
+ }
+
+ int x = _chatmsg_box.x;
+ int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
+ int width = _chatmsg_box.width;
+ int height = _chatmsg_box.height;
+ if (y < 0) {
+ height = max(height + y, min(_chatmsg_box.height, _screen.height));
+ y = 0;
+ }
+ if (x + width >= _screen.width) {
+ width = _screen.width - x;
+ }
+ if (width <= 0 || height <= 0) return;
+
+ _chatmessage_visible = false;
+ /* Put our 'shot' back to the screen */
+ blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
+ /* And make sure it is updated next time */
+ _video_driver->MakeDirty(x, y, width, height);
+
+ _chatmessage_dirty = true;
+ }
+}
+
+/** Check if a message is expired every day */
+void NetworkChatMessageDailyLoop()
+{
+ for (uint i = 0; i < MAX_CHAT_MESSAGES; i++) {
+ ChatMessage *cmsg = &_chatmsg_list[i];
+ if (cmsg->message[0] == '\0') continue;
+
+ /* Message has expired, remove from the list */
+ if (cmsg->end_date < _date) {
+ /* Move the remaining messages over the current message */
+ if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1));
+
+ /* Mark the last item as empty */
+ _chatmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0';
+ _chatmessage_dirty = true;
+
+ /* Go one item back, because we moved the array 1 to the left */
+ i--;
+ }
+ }
+}
+
+/** Draw the chat message-box */
+void NetworkDrawChatMessage()
+{
+ Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
+ if (!_chatmessage_dirty) return;
+
+ /* First undraw if needed */
+ NetworkUndrawChatMessage();
+
+ if (_iconsole_mode == ICONSOLE_FULL) return;
+
+ /* Check if we have anything to draw at all */
+ uint count = GetChatMessageCount();
+ if (count == 0) return;
+
+ int x = _chatmsg_box.x;
+ int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
+ int width = _chatmsg_box.width;
+ int height = _chatmsg_box.height;
+ if (y < 0) {
+ height = max(height + y, min(_chatmsg_box.height, _screen.height));
+ y = 0;
+ }
+ if (x + width >= _screen.width) {
+ width = _screen.width - x;
+ }
+ if (width <= 0 || height <= 0) return;
+
+ assert(blitter->BufferSize(width, height) < (int)sizeof(_chatmessage_backup));
+
+ /* Make a copy of the screen as it is before painting (for undraw) */
+ blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
+
+ _cur_dpi = &_screen; // switch to _screen painting
+
+ /* Paint a half-transparent box behind the chat messages */
+ GfxFillRect(
+ _chatmsg_box.x,
+ _screen.height - _chatmsg_box.y - count * 13 - 2,
+ _chatmsg_box.x + _chatmsg_box.width - 1,
+ _screen.height - _chatmsg_box.y - 2,
+ PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOR // black, but with some alpha for background
+ );
+
+ /* Paint the chat messages starting with the lowest at the bottom */
+ for (uint y = 13; count-- != 0; y += 13) {
+ DoDrawString(_chatmsg_list[count].message, _chatmsg_box.x + 3, _screen.height - _chatmsg_box.y - y + 1, _chatmsg_list[count].color);
+ }
+
+ /* Make sure the data is updated next flush */
+ _video_driver->MakeDirty(x, y, width, height);
+
+ _chatmessage_visible = true;
+ _chatmessage_dirty = false;
+}
+
+
+static void SendChat(const char *buf, DestType type, int dest)
+{
+ if (StrEmpty(buf)) return;
+ if (!_network_server) {
+ SEND_COMMAND(PACKET_CLIENT_CHAT)((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf);
+ } else {
+ NetworkServerSendChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, NETWORK_SERVER_INDEX);
+ }
+}
+
+
+struct NetworkChatWindow : public QueryStringBaseWindow {
+ DestType dtype;
+ int dest;
+
+ NetworkChatWindow (const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(NETWORK_CHAT_LENGTH, desc)
+ {
+ this->LowerWidget(2);
+ this->dtype = type;
+ this->dest = dest;
+ this->afilter = CS_ALPHANUMERAL;
+ InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 0);
+
+ InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height);
+ SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys
+
+ _chat_tab_completion_active = false;
+
+ this->FindWindowPlacementAndResize(desc);
+ }
+
+ ~NetworkChatWindow ()
+ {
+ InvalidateWindowData(WC_NEWS_WINDOW, 0, 0);
+ ClrBit(_no_scroll, SCROLL_CHAT);
+ }
+
+ /**
+ * Find the next item of the list of things that can be auto-completed.
+ * @param item The current indexed item to return. This function can, and most
+ * likely will, alter item, to skip empty items in the arrays.
+ * @return Returns the char that matched to the index.
+ */
+ const char *ChatTabCompletionNextItem(uint *item)
+ {
+ static char chat_tab_temp_buffer[64];
+
+ /* First, try clients */
+ if (*item < MAX_CLIENT_INFO) {
+ /* Skip inactive clients */
+ while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++;
+ if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name;
+ }
+
+ /* Then, try townnames */
+ /* Not that the following assumes all town indices are adjacent, ie no
+ * towns have been deleted. */
+ if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) {
+ const Town *t;
+
+ FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
+ /* Get the town-name via the string-system */
+ SetDParam(0, t->index);
+ GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer));
+ return &chat_tab_temp_buffer[0];
+ }
+ }
+
+ return NULL;
+ }
+
+ /**
+ * Find what text to complete. It scans for a space from the left and marks
+ * the word right from that as to complete. It also writes a \0 at the
+ * position of the space (if any). If nothing found, buf is returned.
+ */
+ static char *ChatTabCompletionFindText(char *buf)
+ {
+ char *p = strrchr(buf, ' ');
+ if (p == NULL) return buf;
+
+ *p = '\0';
+ return p + 1;
+ }
+
+ /**
+ * See if we can auto-complete the current text of the user.
+ */
+ void ChatTabCompletion()
+ {
+ static char _chat_tab_completion_buf[NETWORK_CHAT_LENGTH];
+ assert(this->edit_str_size == lengthof(_chat_tab_completion_buf));
+
+ Textbuf *tb = &this->text;
+ size_t len, tb_len;
+ uint item;
+ char *tb_buf, *pre_buf;
+ const char *cur_name;
+ bool second_scan = false;
+
+ item = 0;
+
+ /* Copy the buffer so we can modify it without damaging the real data */
+ pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf);
+
+ tb_buf = ChatTabCompletionFindText(pre_buf);
+ tb_len = strlen(tb_buf);
+
+ while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) {
+ item++;
+
+ if (_chat_tab_completion_active) {
+ /* We are pressing TAB again on the same name, is there an other name
+ * that starts with this? */
+ if (!second_scan) {
+ size_t offset;
+ size_t length;
+
+ /* If we are completing at the begin of the line, skip the ': ' we added */
+ if (tb_buf == pre_buf) {
+ offset = 0;
+ length = tb->length - 2;
+ } else {
+ /* Else, find the place we are completing at */
+ offset = strlen(pre_buf) + 1;
+ length = tb->length - offset;
+ }
+
+ /* Compare if we have a match */
+ if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
+
+ continue;
+ }
+
+ /* Now any match we make on _chat_tab_completion_buf after this, is perfect */
+ }
+
+ len = strlen(cur_name);
+ if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
+ /* Save the data it was before completion */
+ if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf);
+ _chat_tab_completion_active = true;
+
+ /* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
+ if (pre_buf == tb_buf) {
+ snprintf(tb->buf, this->edit_str_size, "%s: ", cur_name);
+ } else {
+ snprintf(tb->buf, this->edit_str_size, "%s %s", pre_buf, cur_name);
+ }
+
+ /* Update the textbuffer */
+ UpdateTextBufferSize(&this->text);
+
+ this->SetDirty();
+ free(pre_buf);
+ return;
+ }
+ }
+
+ if (second_scan) {
+ /* We walked all posibilities, and the user presses tab again.. revert to original text */
+ strcpy(tb->buf, _chat_tab_completion_buf);
+ _chat_tab_completion_active = false;
+
+ /* Update the textbuffer */
+ UpdateTextBufferSize(&this->text);
+
+ this->SetDirty();
+ }
+ free(pre_buf);
+ }
+
+ virtual void OnPaint()
+ {
+ static const StringID chat_captions[] = {
+ STR_NETWORK_CHAT_ALL_CAPTION,
+ STR_NETWORK_CHAT_COMPANY_CAPTION,
+ STR_NETWORK_CHAT_CLIENT_CAPTION
+ };
+
+ this->DrawWidgets();
+
+ assert((uint)this->dtype < lengthof(chat_captions));
+ DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK);
+ this->DrawEditBox(2);
+ }
+
+ virtual void OnClick(Point pt, int widget)
+ {
+ switch (widget) {
+ case 2:
+ ShowOnScreenKeyboard(this, 2, 0, 3);
+ break;
+
+ case 3: /* Send */
+ SendChat(this->text.buf, this->dtype, this->dest);
+ /* FALLTHROUGH */
+ case 0: /* Cancel */ delete this; break;
+ }
+ }
+
+ virtual void OnMouseLoop()
+ {
+ this->HandleEditBox(2);
+ }
+
+ virtual EventState OnKeyPress(uint16 key, uint16 keycode)
+ {
+ EventState state = ES_NOT_HANDLED;
+ if (keycode == WKC_TAB) {
+ ChatTabCompletion();
+ } else {
+ _chat_tab_completion_active = false;
+ switch (this->HandleEditBoxKey(2, key, keycode, state)) {
+ case 1: /* Return */
+ SendChat(this->text.buf, this->dtype, this->dest);
+ /* FALLTHROUGH */
+ case 2: /* Escape */ delete this; break;
+ }
+ }
+ return state;
+ }
+};
+
+static const Widget _chat_window_widgets[] = {
+{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
+{ WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 11, 319, 0, 13, 0x0, STR_NULL}, // background
+{ WWT_EDITBOX, RESIZE_RIGHT, COLOUR_GREY, 75, 257, 1, 12, STR_NETWORK_CHAT_OSKTITLE, STR_NULL}, // text box
+{ WWT_PUSHTXTBTN, RESIZE_LR, COLOUR_GREY, 258, 319, 1, 12, STR_NETWORK_SEND, STR_NULL}, // send button
+{ WIDGETS_END},
+};
+
+static const WindowDesc _chat_window_desc = {
+ WDP_CENTER, -26, 320, 14, 640, 14, // x, y, width, height
+ WC_SEND_NETWORK_MSG, WC_NONE,
+ WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
+ _chat_window_widgets,
+};
+
+void ShowNetworkChatQueryWindow(DestType type, int dest)
+{
+ DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
+ new NetworkChatWindow (&_chat_window_desc, type, dest);
+}
+
+#endif /* ENABLE_NETWORK */
diff --git a/src/network/network_func.h b/src/network/network_func.h
index 3f7612751..cd1a5aa5d 100644
--- a/src/network/network_func.h
+++ b/src/network/network_func.h
@@ -57,6 +57,11 @@ void NetworkServerSendRcon(uint16 client_index, ConsoleColour colour_code, const
void NetworkServerSendError(uint16 client_index, NetworkErrorCode error);
void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const char *msg, uint16 from_index);
+void NetworkInitChatMessage();
+void CDECL NetworkAddChatMessage(uint16 color, uint8 duration, const char *message, ...);
+void NetworkUndrawChatMessage();
+void NetworkChatMessageDailyLoop();
+
#define FOR_ALL_ACTIVE_CLIENT_INFOS(ci) for (ci = _network_client_info; ci != endof(_network_client_info); ci++) if (ci->client_index != NETWORK_EMPTY_INDEX)
#endif /* ENABLE_NETWORK */
diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp
index 70ddccd92..d8ebfffea 100644
--- a/src/network/network_gui.cpp
+++ b/src/network/network_gui.cpp
@@ -15,11 +15,9 @@
#include "network_gamelist.h"
#include "../gui.h"
#include "../window_gui.h"
-#include "../textbuf_gui.h"
#include "../variables.h"
#include "network_server.h"
#include "network_udp.h"
-#include "../town.h"
#include "../newgrf.h"
#include "../functions.h"
#include "../window_func.h"
@@ -37,8 +35,6 @@
#include "../table/sprites.h"
-static bool _chat_tab_completion_active;
-
static void ShowNetworkStartServerWindow();
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
extern void SwitchMode(int new_mode);
@@ -1732,250 +1728,6 @@ void ShowJoinStatusWindow()
new NetworkJoinStatusWindow(&_network_join_status_window_desc);
}
-static void SendChat(const char *buf, DestType type, int dest)
-{
- if (StrEmpty(buf)) return;
- if (!_network_server) {
- SEND_COMMAND(PACKET_CLIENT_CHAT)((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf);
- } else {
- NetworkServerSendChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, NETWORK_SERVER_INDEX);
- }
-}
-
-
-struct NetworkChatWindow : public QueryStringBaseWindow {
- DestType dtype;
- int dest;
-
- NetworkChatWindow (const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(NETWORK_CHAT_LENGTH, desc)
- {
- this->LowerWidget(2);
- this->dtype = type;
- this->dest = dest;
- this->afilter = CS_ALPHANUMERAL;
- InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 0);
-
- InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height);
- SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys
-
- _chat_tab_completion_active = false;
-
- this->FindWindowPlacementAndResize(desc);
- }
-
- ~NetworkChatWindow ()
- {
- InvalidateWindowData(WC_NEWS_WINDOW, 0, 0);
- ClrBit(_no_scroll, SCROLL_CHAT);
- }
-
- /**
- * Find the next item of the list of things that can be auto-completed.
- * @param item The current indexed item to return. This function can, and most
- * likely will, alter item, to skip empty items in the arrays.
- * @return Returns the char that matched to the index.
- */
- const char *ChatTabCompletionNextItem(uint *item)
- {
- static char chat_tab_temp_buffer[64];
-
- /* First, try clients */
- if (*item < MAX_CLIENT_INFO) {
- /* Skip inactive clients */
- while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++;
- if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name;
- }
-
- /* Then, try townnames */
- /* Not that the following assumes all town indices are adjacent, ie no
- * towns have been deleted. */
- if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) {
- const Town *t;
-
- FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
- /* Get the town-name via the string-system */
- SetDParam(0, t->index);
- GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer));
- return &chat_tab_temp_buffer[0];
- }
- }
-
- return NULL;
- }
-
- /**
- * Find what text to complete. It scans for a space from the left and marks
- * the word right from that as to complete. It also writes a \0 at the
- * position of the space (if any). If nothing found, buf is returned.
- */
- static char *ChatTabCompletionFindText(char *buf)
- {
- char *p = strrchr(buf, ' ');
- if (p == NULL) return buf;
-
- *p = '\0';
- return p + 1;
- }
-
- /**
- * See if we can auto-complete the current text of the user.
- */
- void ChatTabCompletion()
- {
- static char _chat_tab_completion_buf[NETWORK_CHAT_LENGTH];
- assert(this->edit_str_size == lengthof(_chat_tab_completion_buf));
-
- Textbuf *tb = &this->text;
- size_t len, tb_len;
- uint item;
- char *tb_buf, *pre_buf;
- const char *cur_name;
- bool second_scan = false;
-
- item = 0;
-
- /* Copy the buffer so we can modify it without damaging the real data */
- pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf);
-
- tb_buf = ChatTabCompletionFindText(pre_buf);
- tb_len = strlen(tb_buf);
-
- while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) {
- item++;
-
- if (_chat_tab_completion_active) {
- /* We are pressing TAB again on the same name, is there an other name
- * that starts with this? */
- if (!second_scan) {
- size_t offset;
- size_t length;
-
- /* If we are completing at the begin of the line, skip the ': ' we added */
- if (tb_buf == pre_buf) {
- offset = 0;
- length = tb->length - 2;
- } else {
- /* Else, find the place we are completing at */
- offset = strlen(pre_buf) + 1;
- length = tb->length - offset;
- }
-
- /* Compare if we have a match */
- if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
-
- continue;
- }
-
- /* Now any match we make on _chat_tab_completion_buf after this, is perfect */
- }
-
- len = strlen(cur_name);
- if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
- /* Save the data it was before completion */
- if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf);
- _chat_tab_completion_active = true;
-
- /* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
- if (pre_buf == tb_buf) {
- snprintf(tb->buf, this->edit_str_size, "%s: ", cur_name);
- } else {
- snprintf(tb->buf, this->edit_str_size, "%s %s", pre_buf, cur_name);
- }
-
- /* Update the textbuffer */
- UpdateTextBufferSize(&this->text);
-
- this->SetDirty();
- free(pre_buf);
- return;
- }
- }
-
- if (second_scan) {
- /* We walked all posibilities, and the user presses tab again.. revert to original text */
- strcpy(tb->buf, _chat_tab_completion_buf);
- _chat_tab_completion_active = false;
-
- /* Update the textbuffer */
- UpdateTextBufferSize(&this->text);
-
- this->SetDirty();
- }
- free(pre_buf);
- }
-
- virtual void OnPaint()
- {
- static const StringID chat_captions[] = {
- STR_NETWORK_CHAT_ALL_CAPTION,
- STR_NETWORK_CHAT_COMPANY_CAPTION,
- STR_NETWORK_CHAT_CLIENT_CAPTION
- };
-
- this->DrawWidgets();
-
- assert((uint)this->dtype < lengthof(chat_captions));
- DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK);
- this->DrawEditBox(2);
- }
-
- virtual void OnClick(Point pt, int widget)
- {
- switch (widget) {
- case 2:
- ShowOnScreenKeyboard(this, 2, 0, 3);
- break;
-
- case 3: /* Send */
- SendChat(this->text.buf, this->dtype, this->dest);
- /* FALLTHROUGH */
- case 0: /* Cancel */ delete this; break;
- }
- }
-
- virtual void OnMouseLoop()
- {
- this->HandleEditBox(2);
- }
-
- virtual EventState OnKeyPress(uint16 key, uint16 keycode)
- {
- EventState state = ES_NOT_HANDLED;
- if (keycode == WKC_TAB) {
- ChatTabCompletion();
- } else {
- _chat_tab_completion_active = false;
- switch (this->HandleEditBoxKey(2, key, keycode, state)) {
- case 1: /* Return */
- SendChat(this->text.buf, this->dtype, this->dest);
- /* FALLTHROUGH */
- case 2: /* Escape */ delete this; break;
- }
- }
- return state;
- }
-};
-
-static const Widget _chat_window_widgets[] = {
-{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
-{ WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 11, 319, 0, 13, 0x0, STR_NULL}, // background
-{ WWT_EDITBOX, RESIZE_RIGHT, COLOUR_GREY, 75, 257, 1, 12, STR_NETWORK_CHAT_OSKTITLE, STR_NULL}, // text box
-{ WWT_PUSHTXTBTN, RESIZE_LR, COLOUR_GREY, 258, 319, 1, 12, STR_NETWORK_SEND, STR_NULL}, // send button
-{ WIDGETS_END},
-};
-
-static const WindowDesc _chat_window_desc = {
- WDP_CENTER, -26, 320, 14, 640, 14, // x, y, width, height
- WC_SEND_NETWORK_MSG, WC_NONE,
- WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
- _chat_window_widgets,
-};
-
-void ShowNetworkChatQueryWindow(DestType type, int dest)
-{
- DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
- new NetworkChatWindow (&_chat_window_desc, type, dest);
-}
/** Enum for NetworkGameWindow, referring to _network_game_window_widgets */
enum NetworkCompanyPasswordWindowWidgets {
diff --git a/src/texteff.cpp b/src/texteff.cpp
index c6b8daa47..cf71d9c5d 100644
--- a/src/texteff.cpp
+++ b/src/texteff.cpp
@@ -6,27 +6,18 @@
#include "openttd.h"
#include "landscape.h"
#include "gfx_func.h"
-#include "console_func.h"
#include "variables.h"
-#include "blitter/factory.hpp"
#include "texteff.hpp"
-#include "video/video_driver.hpp"
+#include "core/bitmath_func.hpp"
#include "transparency.h"
#include "strings_func.h"
#include "core/alloc_func.hpp"
-#include "date_func.h"
#include "functions.h"
#include "viewport_func.h"
#include "settings_type.h"
-#include "table/sprites.h"
-
-#include <stdarg.h> /* va_list */
-
enum {
- MAX_TEXTMESSAGE_LENGTH = 200,
- INIT_NUM_TEXT_MESSAGES = 20,
- MAX_CHAT_MESSAGES = 10,
+ INIT_NUM_TEXT_EFFECTS = 20,
};
struct TextEffect {
@@ -41,220 +32,9 @@ struct TextEffect {
TextEffectMode mode;
};
-
-struct ChatMessage {
- char message[MAX_TEXTMESSAGE_LENGTH];
- uint16 color;
- Date end_date;
-};
-
/* used for text effects */
static TextEffect *_text_effect_list = NULL;
-static uint16 _num_text_effects = INIT_NUM_TEXT_MESSAGES;
-
-/* used for chat window */
-static ChatMessage _chatmsg_list[MAX_CHAT_MESSAGES];
-static bool _chatmessage_dirty = false;
-static bool _chatmessage_visible = false;
-
-/* The chatbox grows from the bottom so the coordinates are pixels from
- * the left and pixels from the bottom. The height is the maximum height */
-static const PointDimension _chatmsg_box = {10, 30, 500, 150};
-static uint8 _chatmessage_backup[150 * 500 * 6]; // (height * width)
-
-static inline uint GetChatMessageCount()
-{
- uint i;
-
- for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
- if (_chatmsg_list[i].message[0] == '\0') break;
- }
-
- return i;
-}
-
-/* Add a text message to the 'chat window' to be shown
- * @param color The colour this message is to be shown in
- * @param duration The duration of the chat message in game-days
- * @param message message itself in printf() style */
-void CDECL AddChatMessage(uint16 color, uint8 duration, const char *message, ...)
-{
- char buf[MAX_TEXTMESSAGE_LENGTH];
- const char *bufp;
- va_list va;
- uint msg_count;
- uint16 lines;
-
- va_start(va, message);
- vsnprintf(buf, lengthof(buf), message, va);
- va_end(va);
-
-
- Utf8TrimString(buf, MAX_TEXTMESSAGE_LENGTH);
-
- /* Force linebreaks for strings that are too long */
- lines = GB(FormatStringLinebreaks(buf, _chatmsg_box.width - 8), 0, 16) + 1;
- if (lines >= MAX_CHAT_MESSAGES) return;
-
- msg_count = GetChatMessageCount();
- /* We want to add more chat messages than there is free space for, remove 'old' */
- if (lines > MAX_CHAT_MESSAGES - msg_count) {
- int i = lines - (MAX_CHAT_MESSAGES - msg_count);
- memmove(&_chatmsg_list[0], &_chatmsg_list[i], sizeof(_chatmsg_list[0]) * (msg_count - i));
- msg_count = MAX_CHAT_MESSAGES - lines;
- }
-
- for (bufp = buf; lines != 0; lines--) {
- ChatMessage *cmsg = &_chatmsg_list[msg_count++];
- ttd_strlcpy(cmsg->message, bufp, sizeof(cmsg->message));
-
- /* The default colour for a message is player colour. Replace this with
- * white for any additional lines */
- cmsg->color = (bufp == buf && color & IS_PALETTE_COLOR) ? color : (0x1D - 15) | IS_PALETTE_COLOR;
- cmsg->end_date = _date + duration;
-
- bufp += strlen(bufp) + 1; // jump to 'next line' in the formatted string
- }
-
- _chatmessage_dirty = true;
-}
-
-void InitChatMessage()
-{
- uint i;
-
- for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
- _chatmsg_list[i].message[0] = '\0';
- }
-}
-
-/** Hide the chatbox */
-void UndrawChatMessage()
-{
- if (_chatmessage_visible) {
- Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
- /* Sometimes we also need to hide the cursor
- * This is because both textmessage and the cursor take a shot of the
- * screen before drawing.
- * Now the textmessage takes his shot and paints his data before the cursor
- * does, so in the shot of the cursor is the screen-data of the textmessage
- * included when the cursor hangs somewhere over the textmessage. To
- * avoid wrong repaints, we undraw the cursor in that case, and everything
- * looks nicely ;)
- * (and now hope this story above makes sense to you ;))
- */
-
- if (_cursor.visible) {
- if (_cursor.draw_pos.x + _cursor.draw_size.x >= _chatmsg_box.x &&
- _cursor.draw_pos.x <= _chatmsg_box.x + _chatmsg_box.width &&
- _cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _chatmsg_box.y - _chatmsg_box.height &&
- _cursor.draw_pos.y <= _screen.height - _chatmsg_box.y) {
- UndrawMouseCursor();
- }
- }
-
- int x = _chatmsg_box.x;
- int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
- int width = _chatmsg_box.width;
- int height = _chatmsg_box.height;
- if (y < 0) {
- height = max(height + y, min(_chatmsg_box.height, _screen.height));
- y = 0;
- }
- if (x + width >= _screen.width) {
- width = _screen.width - x;
- }
- if (width <= 0 || height <= 0) return;
-
- _chatmessage_visible = false;
- /* Put our 'shot' back to the screen */
- blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
- /* And make sure it is updated next time */
- _video_driver->MakeDirty(x, y, width, height);
-
- _chatmessage_dirty = true;
- }
-}
-
-/** Check if a message is expired every day */
-void ChatMessageDailyLoop()
-{
- uint i;
-
- for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
- ChatMessage *cmsg = &_chatmsg_list[i];
- if (cmsg->message[0] == '\0') continue;
-
- /* Message has expired, remove from the list */
- if (cmsg->end_date < _date) {
- /* Move the remaining messages over the current message */
- if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1));
-
- /* Mark the last item as empty */
- _chatmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0';
- _chatmessage_dirty = true;
-
- /* Go one item back, because we moved the array 1 to the left */
- i--;
- }
- }
-}
-
-/** Draw the chat message-box */
-void DrawChatMessage()
-{
- Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
- if (!_chatmessage_dirty) return;
-
- /* First undraw if needed */
- UndrawChatMessage();
-
- if (_iconsole_mode == ICONSOLE_FULL) return;
-
- /* Check if we have anything to draw at all */
- uint count = GetChatMessageCount();
- if (count == 0) return;
-
- int x = _chatmsg_box.x;
- int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height;
- int width = _chatmsg_box.width;
- int height = _chatmsg_box.height;
- if (y < 0) {
- height = max(height + y, min(_chatmsg_box.height, _screen.height));
- y = 0;
- }
- if (x + width >= _screen.width) {
- width = _screen.width - x;
- }
- if (width <= 0 || height <= 0) return;
-
- assert(blitter->BufferSize(width, height) < (int)sizeof(_chatmessage_backup));
-
- /* Make a copy of the screen as it is before painting (for undraw) */
- blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height);
-
- _cur_dpi = &_screen; // switch to _screen painting
-
- /* Paint a half-transparent box behind the chat messages */
- GfxFillRect(
- _chatmsg_box.x,
- _screen.height - _chatmsg_box.y - count * 13 - 2,
- _chatmsg_box.x + _chatmsg_box.width - 1,
- _screen.height - _chatmsg_box.y - 2,
- PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOR // black, but with some alpha for background
- );
-
- /* Paint the chat messages starting with the lowest at the bottom */
- for (uint y = 13; count-- != 0; y += 13) {
- DoDrawString(_chatmsg_list[count].message, _chatmsg_box.x + 3, _screen.height - _chatmsg_box.y - y + 1, _chatmsg_list[count].color);
- }
-
- /* Make sure the data is updated next flush */
- _video_driver->MakeDirty(x, y, width, height);
-
- _chatmessage_visible = true;
- _chatmessage_dirty = false;
-}
+static uint16 _num_text_effects = INIT_NUM_TEXT_EFFECTS;
/* Text Effects */
/**
diff --git a/src/texteff.hpp b/src/texteff.hpp
index 36692a49a..7b59def8c 100644
--- a/src/texteff.hpp
+++ b/src/texteff.hpp
@@ -26,11 +26,6 @@ void DrawTextEffects(DrawPixelInfo *dpi);
void UpdateTextEffect(TextEffectID effect_id, StringID msg);
void RemoveTextEffect(TextEffectID effect_id);
-void InitChatMessage();
-void DrawChatMessage();
-void CDECL AddChatMessage(uint16 color, uint8 duration, const char *message, ...);
-void UndrawChatMessage();
-
/* misc_gui.cpp */
TextEffectID ShowFillingPercent(int x, int y, int z, uint8 percent, StringID color);
void UpdateFillingPercent(TextEffectID te_id, uint8 percent, StringID color);
diff --git a/src/video/cocoa/event.mm b/src/video/cocoa/event.mm
index cee4e8fe2..a196ca4b0 100644
--- a/src/video/cocoa/event.mm
+++ b/src/video/cocoa/event.mm
@@ -694,7 +694,7 @@ void QZ_GameLoop()
st += GetTick() - st0;
#endif
_screen.dst_ptr = _cocoa_subdriver->GetPixelBuffer();
- DrawChatMessage();
+ NetworkDrawChatMessage();
DrawMouseCursor();
_cocoa_subdriver->Draw();
}
diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp
index d6e274fc0..4ccf90abe 100644
--- a/src/video/sdl_v.cpp
+++ b/src/video/sdl_v.cpp
@@ -518,7 +518,7 @@ void VideoDriver_SDL::MainLoop()
} else {
SDL_CALL SDL_Delay(1);
_screen.dst_ptr = _sdl_screen->pixels;
- DrawChatMessage();
+ NetworkDrawChatMessage();
DrawMouseCursor();
DrawSurfaceToScreen();
}
diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp
index 8dcab88e9..9f5c32045 100644
--- a/src/video/win32_v.cpp
+++ b/src/video/win32_v.cpp
@@ -890,7 +890,7 @@ void VideoDriver_Win32::MainLoop()
GdiFlush();
#endif
_screen.dst_ptr = _wnd.buffer_bits;
- DrawChatMessage();
+ NetworkDrawChatMessage();
DrawMouseCursor();
}
}
diff --git a/src/window.cpp b/src/window.cpp
index c33b48f06..3686b0e14 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -23,6 +23,7 @@
#include "cheat_func.h"
#include "window_func.h"
#include "tilehighlight_func.h"
+#include "network/network.h"
#include "table/sprites.h"
@@ -1984,7 +1985,7 @@ void UpdateWindows()
FOR_ALL_WINDOWS(wz) {
if ((*wz)->viewport != NULL) UpdateViewportPosition(*wz);
}
- DrawChatMessage();
+ NetworkDrawChatMessage();
/* Redraw mouse cursor in case it was hidden */
DrawMouseCursor();
}