1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "ScenarioEditor/ScenarioEditor.h"
21 #include "Common/Tools.h"
22 #include "Common/Brushes.h"
23 #include "Common/MiscState.h"
24 #include "Common/ObjectSettings.h"
25 #include "GameInterface/Messages.h"
26 
27 using AtlasMessage::Position;
28 
29 static float g_DefaultAngle = (float)(M_PI*3.0/4.0);
30 
31 class PlaceObject : public StateDrivenTool<PlaceObject>
32 {
33 	DECLARE_DYNAMIC_CLASS(PlaceObject);
34 
35 	Position m_ScreenPos, m_ObjPos, m_Target;
36 	wxString m_ObjectID;
37 	unsigned int m_ActorSeed;
38 
39 public:
PlaceObject()40 	PlaceObject()
41 	{
42 		SetState(&Waiting);
43 	}
44 
SendObjectMsg(bool preview)45 	void SendObjectMsg(bool preview)
46 	{
47 		int dragDistSq =
48 			  (m_ScreenPos.type1.x-m_Target.type1.x)*(m_ScreenPos.type1.x-m_Target.type1.x)
49 			+ (m_ScreenPos.type1.y-m_Target.type1.y)*(m_ScreenPos.type1.y-m_Target.type1.y);
50 		bool useTarget = (dragDistSq >= 16*16);
51 		if (preview)
52 			POST_MESSAGE(ObjectPreview, ((std::wstring)m_ObjectID.wc_str(), GetScenarioEditor().GetObjectSettings().GetSettings(), m_ObjPos, useTarget, m_Target, g_DefaultAngle, m_ActorSeed, true));
53 		else
54 		{
55 			POST_COMMAND(CreateObject, ((std::wstring)m_ObjectID.wc_str(), GetScenarioEditor().GetObjectSettings().GetSettings(), m_ObjPos, useTarget, m_Target, g_DefaultAngle, m_ActorSeed));
56 			RandomizeActorSeed();
57 		}
58 	}
59 
Init(void * initData,ScenarioEditor * scenarioEditor)60 	virtual void Init(void* initData, ScenarioEditor* scenarioEditor)
61 	{
62 		StateDrivenTool<PlaceObject>::Init(initData, scenarioEditor);
63 
64 		wxASSERT(initData);
65 		wxString& id = *static_cast<wxString*>(initData);
66 		m_ObjectID = id;
67 		SendObjectMsg(true);
68 	}
69 
OnEnable()70 	void OnEnable()
71 	{
72 		RandomizeActorSeed();
73 	}
74 
OnDisable()75 	void OnDisable()
76 	{
77 		m_ObjectID = _T("");
78 		SendObjectMsg(true);
79 	}
80 
81 	/*
82 		Object placement:
83 		* Select unit from list
84 		* Move mouse around screen; preview of unit follows mouse
85 		* Left mouse down -> remember position, fix preview to point
86 		* Mouse move -> if moved > [limit], rotate unit to face mouse; else default orientation
87 		* Left mouse release -> finalise placement of object on map
88 
89 		* Page up/down -> rotate default orientation
90 
91 		* Escape -> cancel placement tool
92 
93 		TOOD: what happens if somebody saves while the preview is active?
94 	*/
95 
OnMouseOverride(wxMouseEvent & WXUNUSED (evt))96 	bool OnMouseOverride(wxMouseEvent& WXUNUSED(evt))
97 	{
98 		// This used to let the scroll-wheel rotate units, but that overrides
99 		// the camera zoom and makes navigation very awkward, so it doesn't
100 		// any more.
101 		return false;
102 	}
103 
OnKeyOverride(wxKeyEvent & evt,KeyEventType type)104 	bool OnKeyOverride(wxKeyEvent& evt, KeyEventType type)
105 	{
106 		if (type == KEY_CHAR && evt.GetKeyCode() == WXK_ESCAPE)
107 		{
108 			SetState(&Disabled);
109 			return true;
110 		}
111 		else
112 			return false;
113 	}
114 
RotateTick(float dt)115 	void RotateTick(float dt)
116 	{
117 		int dir = 0;
118 		if (wxGetKeyState(WXK_PAGEDOWN))  ++dir; // page-down key
119 		if (wxGetKeyState(WXK_PAGEUP)) --dir; // page-up key
120 		if (dir)
121 		{
122 			float speed = M_PI/2.f * ScenarioEditor::GetSpeedModifier(); // radians per second
123 			g_DefaultAngle += (dir * dt * speed);
124 			SendObjectMsg(true);
125 		}
126 	}
127 
RandomizeActorSeed()128 	void RandomizeActorSeed()
129 	{
130 		m_ActorSeed = (unsigned int)floor((rand() / (float)RAND_MAX) * 65535.f);
131 	}
132 
133 	struct sWaiting : public State
134 	{
OnMousePlaceObject::sWaiting135 		bool OnMouse(PlaceObject* obj, wxMouseEvent& evt)
136 		{
137 			if (obj->OnMouseOverride(evt))
138 				return true;
139 			else if (evt.LeftDown())
140 			{
141 				obj->m_ObjPos = obj->m_ScreenPos = obj->m_Target = Position(evt.GetPosition());
142 				obj->SendObjectMsg(true);
143 				obj->m_ObjPos = Position::Unchanged(); // make sure object is stationary even if the camera moves
144 				SET_STATE(Placing);
145 				return true;
146 			}
147 			else if (evt.Moving())
148 			{
149 				obj->m_ObjPos = obj->m_ScreenPos = obj->m_Target = Position(evt.GetPosition());
150 				obj->SendObjectMsg(true);
151 				return true;
152 			}
153 			else
154 				return false;
155 		}
OnKeyPlaceObject::sWaiting156 		bool OnKey(PlaceObject* obj, wxKeyEvent& evt, KeyEventType type)
157 		{
158 			if (type == KEY_CHAR && (evt.GetKeyCode() >= '0' && evt.GetKeyCode() <= '9'))
159 			{
160 				int playerID = evt.GetKeyCode() - '0';
161 				obj->GetScenarioEditor().GetObjectSettings().SetPlayerID(playerID);
162 				obj->GetScenarioEditor().GetObjectSettings().NotifyObservers();
163 				obj->SendObjectMsg(true);
164 				return true;
165 			}
166 			else
167 				return obj->OnKeyOverride(evt, type);
168 		}
OnTickPlaceObject::sWaiting169 		void OnTick(PlaceObject* obj, float dt)
170 		{
171 			obj->RotateTick(dt);
172 		}
173 	}
174 	Waiting;
175 
176 	struct sPlacing : public State
177 	{
OnMousePlaceObject::sPlacing178 		bool OnMouse(PlaceObject* obj, wxMouseEvent& evt)
179 		{
180 			if (obj->OnMouseOverride(evt))
181 				return true;
182 			else if (evt.LeftUp())
183 			{
184 				obj->m_Target = Position(evt.GetPosition());
185 				// Create the actual object
186 				obj->SendObjectMsg(false);
187 				// Go back to preview mode
188 				SET_STATE(Waiting);
189 				obj->m_ObjPos = obj->m_ScreenPos = obj->m_Target;
190 				obj->SendObjectMsg(true);
191 				return true;
192 			}
193 			else if (evt.Dragging())
194 			{
195 				obj->m_Target = Position(evt.GetPosition());
196 				obj->SendObjectMsg(true);
197 				return true;
198 			}
199 			else
200 				return false;
201 		}
OnKeyPlaceObject::sPlacing202 		bool OnKey(PlaceObject* obj, wxKeyEvent& evt, KeyEventType type)
203 		{
204 			return obj->OnKeyOverride(evt, type);
205 		}
OnTickPlaceObject::sPlacing206 		void OnTick(PlaceObject* obj, float dt)
207 		{
208 			obj->RotateTick(dt);
209 		}
210 	}
211 	Placing;
212 };
213 
214 IMPLEMENT_DYNAMIC_CLASS(PlaceObject, StateDrivenTool<PlaceObject>);
215