1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include <engine/graphics.h>
4#include <engine/shared/config.h>
5#include <game/generated/protocol.h>
6
7#include "chat.h"
8#include "emoticon.h"
9#include <game/client/animstate.h>
10#include <game/client/render.h>
11#include <game/client/ui.h>
12
13#include <game/client/gameclient.h>
14
15CEmoticon::CEmoticon()
16{
17 OnReset();
18}
19
20void CEmoticon::ConKeyEmoticon(IConsole::IResult *pResult, void *pUserData)
21{
22 CEmoticon *pSelf = (CEmoticon *)pUserData;
23 if(!pSelf->m_pClient->m_Snap.m_SpecInfo.m_Active && pSelf->Client()->State() != IClient::STATE_DEMOPLAYBACK)
24 pSelf->m_Active = pResult->GetInteger(Index: 0) != 0;
25}
26
27void CEmoticon::ConEmote(IConsole::IResult *pResult, void *pUserData)
28{
29 ((CEmoticon *)pUserData)->Emote(Emoticon: pResult->GetInteger(Index: 0));
30}
31
32void CEmoticon::OnConsoleInit()
33{
34 Console()->Register(pName: "+emote", pParams: "", Flags: CFGFLAG_CLIENT, pfnFunc: ConKeyEmoticon, pUser: this, pHelp: "Open emote selector");
35 Console()->Register(pName: "emote", pParams: "i[emote-id]", Flags: CFGFLAG_CLIENT, pfnFunc: ConEmote, pUser: this, pHelp: "Use emote");
36}
37
38void CEmoticon::OnReset()
39{
40 m_WasActive = false;
41 m_Active = false;
42 m_SelectedEmote = -1;
43 m_SelectedEyeEmote = -1;
44}
45
46void CEmoticon::OnRelease()
47{
48 m_Active = false;
49}
50
51bool CEmoticon::OnCursorMove(float x, float y, IInput::ECursorType CursorType)
52{
53 if(!m_Active)
54 return false;
55
56 Ui()->ConvertMouseMove(pX: &x, pY: &y, CursorType);
57 m_SelectorMouse += vec2(x, y);
58 return true;
59}
60
61void CEmoticon::OnRender()
62{
63 if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK)
64 return;
65
66 if(!m_Active)
67 {
68 if(m_WasActive && m_SelectedEmote != -1)
69 Emote(Emoticon: m_SelectedEmote);
70 if(m_WasActive && m_SelectedEyeEmote != -1)
71 EyeEmote(EyeEmote: m_SelectedEyeEmote);
72 m_WasActive = false;
73 return;
74 }
75
76 if(m_pClient->m_Snap.m_SpecInfo.m_Active)
77 {
78 m_Active = false;
79 m_WasActive = false;
80 return;
81 }
82
83 m_WasActive = true;
84
85 if(length(a: m_SelectorMouse) > 170.0f)
86 m_SelectorMouse = normalize(v: m_SelectorMouse) * 170.0f;
87
88 float SelectedAngle = angle(a: m_SelectorMouse) + 2 * pi / 24;
89 if(SelectedAngle < 0)
90 SelectedAngle += 2 * pi;
91
92 m_SelectedEmote = -1;
93 m_SelectedEyeEmote = -1;
94 if(length(a: m_SelectorMouse) > 110.0f)
95 m_SelectedEmote = (int)(SelectedAngle / (2 * pi) * NUM_EMOTICONS);
96 else if(length(a: m_SelectorMouse) > 40.0f)
97 m_SelectedEyeEmote = (int)(SelectedAngle / (2 * pi) * NUM_EMOTES);
98
99 CUIRect Screen = *Ui()->Screen();
100
101 Ui()->MapScreen();
102
103 Graphics()->BlendNormal();
104
105 Graphics()->TextureClear();
106 Graphics()->QuadsBegin();
107 Graphics()->SetColor(r: 0, g: 0, b: 0, a: 0.3f);
108 Graphics()->DrawCircle(CenterX: Screen.w / 2, CenterY: Screen.h / 2, Radius: 190.0f, Segments: 64);
109 Graphics()->QuadsEnd();
110
111 Graphics()->WrapClamp();
112 for(int i = 0; i < NUM_EMOTICONS; i++)
113 {
114 float Angle = 2 * pi * i / NUM_EMOTICONS;
115 if(Angle > pi)
116 Angle -= 2 * pi;
117
118 bool Selected = m_SelectedEmote == i;
119
120 float Size = Selected ? 80.0f : 50.0f;
121
122 Graphics()->TextureSet(Texture: GameClient()->m_EmoticonsSkin.m_aSpriteEmoticons[i]);
123 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
124
125 Graphics()->QuadsBegin();
126 const vec2 Nudge = direction(angle: Angle) * 150.0f;
127 IGraphics::CQuadItem QuadItem(Screen.w / 2 + Nudge.x, Screen.h / 2 + Nudge.y, Size, Size);
128 Graphics()->QuadsDraw(pArray: &QuadItem, Num: 1);
129 Graphics()->QuadsEnd();
130 }
131 Graphics()->WrapNormal();
132
133 if(GameClient()->m_GameInfo.m_AllowEyeWheel && g_Config.m_ClEyeWheel)
134 {
135 Graphics()->TextureClear();
136 Graphics()->QuadsBegin();
137 Graphics()->SetColor(r: 1.0, g: 1.0, b: 1.0, a: 0.3f);
138 Graphics()->DrawCircle(CenterX: Screen.w / 2, CenterY: Screen.h / 2, Radius: 100.0f, Segments: 64);
139 Graphics()->QuadsEnd();
140
141 CTeeRenderInfo TeeInfo = m_pClient->m_aClients[m_pClient->m_aLocalIds[g_Config.m_ClDummy]].m_RenderInfo;
142
143 for(int i = 0; i < NUM_EMOTES; i++)
144 {
145 float Angle = 2 * pi * i / NUM_EMOTES;
146 if(Angle > pi)
147 Angle -= 2 * pi;
148
149 const bool Selected = m_SelectedEyeEmote == i;
150
151 const vec2 Nudge = direction(angle: Angle) * 70.0f;
152 TeeInfo.m_Size = Selected ? 64.0f : 48.0f;
153 RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &TeeInfo, Emote: i, Dir: vec2(-1, 0), Pos: vec2(Screen.w / 2 + Nudge.x, Screen.h / 2 + Nudge.y));
154 }
155
156 Graphics()->TextureClear();
157 Graphics()->QuadsBegin();
158 Graphics()->SetColor(r: 0, g: 0, b: 0, a: 0.3f);
159 Graphics()->DrawCircle(CenterX: Screen.w / 2, CenterY: Screen.h / 2, Radius: 30.0f, Segments: 64);
160 Graphics()->QuadsEnd();
161 }
162 else
163 m_SelectedEyeEmote = -1;
164
165 RenderTools()->RenderCursor(Center: m_SelectorMouse + vec2(Screen.w, Screen.h) / 2, Size: 24.0f);
166}
167
168void CEmoticon::Emote(int Emoticon)
169{
170 CNetMsg_Cl_Emoticon Msg;
171 Msg.m_Emoticon = Emoticon;
172 Client()->SendPackMsgActive(pMsg: &Msg, Flags: MSGFLAG_VITAL);
173
174 if(g_Config.m_ClDummyCopyMoves)
175 {
176 CMsgPacker MsgDummy(NETMSGTYPE_CL_EMOTICON, false);
177 MsgDummy.AddInt(i: Emoticon);
178 Client()->SendMsg(Conn: !g_Config.m_ClDummy, pMsg: &MsgDummy, Flags: MSGFLAG_VITAL);
179 }
180}
181
182void CEmoticon::EyeEmote(int Emote)
183{
184 char aBuf[32];
185 switch(Emote)
186 {
187 case EMOTE_NORMAL:
188 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "/emote normal %d", g_Config.m_ClEyeDuration);
189 break;
190 case EMOTE_PAIN:
191 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "/emote pain %d", g_Config.m_ClEyeDuration);
192 break;
193 case EMOTE_HAPPY:
194 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "/emote happy %d", g_Config.m_ClEyeDuration);
195 break;
196 case EMOTE_SURPRISE:
197 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "/emote surprise %d", g_Config.m_ClEyeDuration);
198 break;
199 case EMOTE_ANGRY:
200 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "/emote angry %d", g_Config.m_ClEyeDuration);
201 break;
202 case EMOTE_BLINK:
203 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "/emote blink %d", g_Config.m_ClEyeDuration);
204 break;
205 }
206 GameClient()->m_Chat.SendChat(Team: 0, pLine: aBuf);
207}
208