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 | |
15 | CEmoticon::CEmoticon() |
16 | { |
17 | OnReset(); |
18 | } |
19 | |
20 | void 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 | |
27 | void CEmoticon::ConEmote(IConsole::IResult *pResult, void *pUserData) |
28 | { |
29 | ((CEmoticon *)pUserData)->Emote(Emoticon: pResult->GetInteger(Index: 0)); |
30 | } |
31 | |
32 | void 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 | |
38 | void CEmoticon::OnReset() |
39 | { |
40 | m_WasActive = false; |
41 | m_Active = false; |
42 | m_SelectedEmote = -1; |
43 | m_SelectedEyeEmote = -1; |
44 | } |
45 | |
46 | void CEmoticon::OnRelease() |
47 | { |
48 | m_Active = false; |
49 | } |
50 | |
51 | bool 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 | |
61 | void 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 | |
168 | void 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 | |
182 | void 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 | |