1#include "layer_sounds.h"
2
3#include <generated/client_data.h>
4
5#include <game/editor/editor.h>
6#include <game/editor/editor_actions.h>
7
8static const float s_SourceVisualSize = 32.0f;
9
10CLayerSounds::CLayerSounds(CEditorMap *pMap) :
11 CLayer(pMap, LAYERTYPE_SOUNDS)
12{
13 m_aName[0] = '\0';
14 m_Sound = -1;
15}
16
17CLayerSounds::CLayerSounds(const CLayerSounds &Other) :
18 CLayer(Other)
19{
20 m_Sound = Other.m_Sound;
21 m_vSources = Other.m_vSources;
22}
23
24CLayerSounds::~CLayerSounds() = default;
25
26void CLayerSounds::Render(bool Tileset)
27{
28 // TODO: nice texture
29 Graphics()->TextureClear();
30 Graphics()->BlendNormal();
31 Graphics()->QuadsBegin();
32
33 // draw falloff distance
34 Graphics()->SetColor(r: 0.6f, g: 0.8f, b: 1.0f, a: 0.4f);
35 for(const auto &Source : m_vSources)
36 {
37 ColorRGBA Offset = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f);
38 Editor()->EnvelopeEval(TimeOffsetMillis: Source.m_PosEnvOffset, Env: Source.m_PosEnv, Result&: Offset, Channels: 2);
39 const vec2 Position = vec2(fx2f(v: Source.m_Position.x) + Offset.r, fx2f(v: Source.m_Position.y) + Offset.g);
40 const float Falloff = Source.m_Falloff / 255.0f;
41
42 switch(Source.m_Shape.m_Type)
43 {
44 case CSoundShape::SHAPE_CIRCLE:
45 {
46 Graphics()->DrawCircle(CenterX: Position.x, CenterY: Position.y, Radius: Source.m_Shape.m_Circle.m_Radius, Segments: 32);
47 if(Falloff > 0.0f)
48 {
49 Graphics()->DrawCircle(CenterX: Position.x, CenterY: Position.y, Radius: Source.m_Shape.m_Circle.m_Radius * Falloff, Segments: 32);
50 }
51 break;
52 }
53 case CSoundShape::SHAPE_RECTANGLE:
54 {
55 const float Width = fx2f(v: Source.m_Shape.m_Rectangle.m_Width);
56 const float Height = fx2f(v: Source.m_Shape.m_Rectangle.m_Height);
57 Graphics()->DrawRectExt(x: Position.x - Width / 2, y: Position.y - Height / 2, w: Width, h: Height, r: 0.0f, Corners: IGraphics::CORNER_NONE);
58 if(Falloff > 0.0f)
59 {
60 Graphics()->DrawRectExt(x: Position.x - Falloff * Width / 2, y: Position.y - Falloff * Height / 2, w: Width * Falloff, h: Height * Falloff, r: 0.0f, Corners: IGraphics::CORNER_NONE);
61 }
62 break;
63 }
64 }
65 }
66
67 Graphics()->QuadsEnd();
68
69 // draw handles
70 Graphics()->TextureSet(Texture: g_pData->m_aImages[IMAGE_AUDIO_SOURCE].m_Id);
71 Graphics()->QuadsBegin();
72
73 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f);
74 Graphics()->SelectSprite(Id: SPRITE_AUDIO_SOURCE);
75 for(const auto &Source : m_vSources)
76 {
77 ColorRGBA Offset = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f);
78 Editor()->EnvelopeEval(TimeOffsetMillis: Source.m_PosEnvOffset, Env: Source.m_PosEnv, Result&: Offset, Channels: 2);
79 const vec2 Position = vec2(fx2f(v: Source.m_Position.x) + Offset.r, fx2f(v: Source.m_Position.y) + Offset.g);
80 Graphics()->DrawSprite(x: Position.x, y: Position.y, Size: Editor()->MapView()->ScaleLength(Value: s_SourceVisualSize));
81 }
82
83 Graphics()->QuadsEnd();
84}
85
86CSoundSource *CLayerSounds::NewSource(int x, int y)
87{
88 Map()->OnModify();
89
90 m_vSources.emplace_back();
91 CSoundSource *pSource = &m_vSources[m_vSources.size() - 1];
92
93 pSource->m_Position.x = f2fx(v: x);
94 pSource->m_Position.y = f2fx(v: y);
95
96 pSource->m_Loop = 1;
97 pSource->m_Pan = 1;
98 pSource->m_TimeDelay = 0;
99
100 pSource->m_PosEnv = -1;
101 pSource->m_PosEnvOffset = 0;
102 pSource->m_SoundEnv = -1;
103 pSource->m_SoundEnvOffset = 0;
104
105 pSource->m_Falloff = 80;
106
107 pSource->m_Shape.m_Type = CSoundShape::SHAPE_CIRCLE;
108 pSource->m_Shape.m_Circle.m_Radius = 1500;
109
110 return pSource;
111}
112
113void CLayerSounds::BrushSelecting(CUIRect Rect)
114{
115 Rect.DrawOutline(Color: ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
116}
117
118int CLayerSounds::BrushGrab(CLayerGroup *pBrush, CUIRect Rect)
119{
120 // create new layer
121 std::shared_ptr<CLayerSounds> pGrabbed = std::make_shared<CLayerSounds>(args: pBrush->Map());
122 pGrabbed->m_Sound = m_Sound;
123 pBrush->AddLayer(pLayer: pGrabbed);
124
125 for(const auto &Source : m_vSources)
126 {
127 float SourceX = fx2f(v: Source.m_Position.x);
128 float SourceY = fx2f(v: Source.m_Position.y);
129
130 if(SourceX > Rect.x && SourceX < Rect.x + Rect.w && SourceY > Rect.y && SourceY < Rect.y + Rect.h)
131 {
132 CSoundSource NewSource = Source;
133 NewSource.m_Position.x -= f2fx(v: Rect.x);
134 NewSource.m_Position.y -= f2fx(v: Rect.y);
135
136 pGrabbed->m_vSources.push_back(x: NewSource);
137 }
138 }
139
140 return pGrabbed->m_vSources.empty() ? 0 : 1;
141}
142
143void CLayerSounds::BrushPlace(CLayer *pBrush, vec2 WorldPos)
144{
145 CLayerSounds *pSoundLayer = static_cast<CLayerSounds *>(pBrush);
146 std::vector<CSoundSource> vAddedSources;
147 for(const auto &Source : pSoundLayer->m_vSources)
148 {
149 CSoundSource NewSource = Source;
150 NewSource.m_Position.x += f2fx(v: WorldPos.x);
151 NewSource.m_Position.y += f2fx(v: WorldPos.y);
152
153 m_vSources.push_back(x: NewSource);
154 vAddedSources.push_back(x: NewSource);
155 }
156 Map()->m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionSoundPlace>(args: Map(), args&: Editor()->m_SelectedGroup, args&: Editor()->m_vSelectedLayers[0], args&: vAddedSources));
157 Map()->OnModify();
158}
159
160CUi::EPopupMenuFunctionResult CLayerSounds::RenderProperties(CUIRect *pToolBox)
161{
162 CProperty aProps[] = {
163 {"Sound", m_Sound, PROPTYPE_SOUND, -1, 0},
164 {nullptr},
165 };
166
167 static int s_aIds[(int)ELayerSoundsProp::NUM_PROPS] = {0};
168 int NewVal = 0;
169 auto [State, Prop] = Editor()->DoPropertiesWithState<ELayerSoundsProp>(pToolbox: pToolBox, pProps: aProps, pIds: s_aIds, pNewVal: &NewVal);
170 if(Prop != ELayerSoundsProp::PROP_NONE && (State == EEditState::END || State == EEditState::ONE_GO))
171 {
172 Map()->OnModify();
173 }
174
175 Map()->m_LayerSoundsPropTracker.Begin(pObject: this, Prop, State);
176
177 if(Prop == ELayerSoundsProp::PROP_SOUND)
178 {
179 if(NewVal >= 0)
180 m_Sound = NewVal % Map()->m_vpSounds.size();
181 else
182 m_Sound = -1;
183 }
184
185 Map()->m_LayerSoundsPropTracker.End(Prop, State);
186
187 return CUi::POPUP_KEEP_OPEN;
188}
189
190void CLayerSounds::ModifySoundIndex(const FIndexModifyFunction &IndexModifyFunction)
191{
192 IndexModifyFunction(&m_Sound);
193}
194
195void CLayerSounds::ModifyEnvelopeIndex(const FIndexModifyFunction &IndexModifyFunction)
196{
197 for(auto &Source : m_vSources)
198 {
199 IndexModifyFunction(&Source.m_SoundEnv);
200 IndexModifyFunction(&Source.m_PosEnv);
201 }
202}
203
204std::shared_ptr<CLayer> CLayerSounds::Duplicate() const
205{
206 return std::make_shared<CLayerSounds>(args: *this);
207}
208
209const char *CLayerSounds::TypeName() const
210{
211 return "sounds";
212}
213