1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include <boost/cstdint.hpp>
4 
5 #include <cmath>
6 #include <SDL_keycode.h>
7 
8 #include "GroupHandler.h"
9 #include "Group.h"
10 #include "Game/SelectedUnitsHandler.h"
11 #include "Game/CameraHandler.h"
12 #include "System/creg/STL_Set.h"
13 #include "System/Log/ILog.h"
14 #include "System/Input/KeyInput.h"
15 #include "System/EventHandler.h"
16 
17 std::vector<CGroupHandler*> grouphandlers;
18 
19 CR_BIND(CGroupHandler, (0))
20 CR_REG_METADATA(CGroupHandler, (
21 	CR_MEMBER(groups),
22 	CR_MEMBER(team),
23 	CR_MEMBER(freeGroups),
24 	CR_MEMBER(firstUnusedGroup),
25 	CR_MEMBER(changedGroups)
26 ))
27 
28 //////////////////////////////////////////////////////////////////////
29 // Construction/Destruction
30 //////////////////////////////////////////////////////////////////////
31 
CGroupHandler(int teamId)32 CGroupHandler::CGroupHandler(int teamId)
33 		: team(teamId),
34 		firstUnusedGroup(FIRST_SPECIAL_GROUP)
35 {
36 	for (int g = 0; g < FIRST_SPECIAL_GROUP; ++g) {
37 		groups.push_back(new CGroup(g, this));
38 	}
39 }
40 
~CGroupHandler()41 CGroupHandler::~CGroupHandler()
42 {
43 	for (int g = 0; g < firstUnusedGroup; ++g) {
44 		delete groups[g];
45 	}
46 }
47 
Update()48 void CGroupHandler::Update()
49 {
50 	{
51 		for (std::vector<CGroup*>::iterator gi = groups.begin(); gi != groups.end(); ++gi) {
52 			if ((*gi) != NULL) {
53 				// Update may invoke RemoveGroup, but this will only NULL the element, so there will be no iterator invalidation here
54 				(*gi)->Update();
55 			}
56 		}
57 	}
58 
59 	std::set<int> grpChg;
60 	{
61 		if (changedGroups.empty())
62 			return;
63 
64 		changedGroups.swap(grpChg);
65 	}
66 	// this batching mechanism is to prevent lua related readlocks for MT
67 	for (std::set<int>::iterator i = grpChg.begin(); i != grpChg.end(); ++i) {
68 		eventHandler.GroupChanged(*i);
69 	}
70 }
71 
GroupCommand(int num)72 bool CGroupHandler::GroupCommand(int num)
73 {
74 	std::string cmd = "";
75 
76 	if (KeyInput::GetKeyModState(KMOD_CTRL)) {
77 		if (!KeyInput::GetKeyModState(KMOD_SHIFT)) {
78 			cmd = "set";
79 		} else {
80 			cmd = "add";
81 		}
82 	} else if (KeyInput::GetKeyModState(KMOD_SHIFT))  {
83 		cmd = "selectadd";
84 	} else if (KeyInput::GetKeyModState(KMOD_ALT)) {
85 		cmd = "selecttoggle";
86 	}
87 
88 	return GroupCommand(num, cmd);
89 }
90 
GroupCommand(int num,const std::string & cmd)91 bool CGroupHandler::GroupCommand(int num, const std::string& cmd)
92 {
93 	CGroup* group = groups[num];
94 
95 	if ((cmd == "set") || (cmd == "add")) {
96 		if (cmd == "set") {
97 			group->ClearUnits();
98 		}
99 		const CUnitSet& selUnits = selectedUnitsHandler.selectedUnits;
100 		CUnitSet::const_iterator ui;
101 		for(ui = selUnits.begin(); ui != selUnits.end(); ++ui) {
102 			(*ui)->SetGroup(group);
103 		}
104 	}
105 	else if (cmd == "selectadd")  {
106 		// do not select the group, just add its members to the current selection
107 		CUnitSet::const_iterator ui;
108 		for (ui = group->units.begin(); ui != group->units.end(); ++ui) {
109 			selectedUnitsHandler.AddUnit(*ui);
110 		}
111 		return true;
112 	}
113 	else if (cmd == "selectclear")  {
114 		// do not select the group, just remove its members from the current selection
115 		CUnitSet::const_iterator ui;
116 		for (ui = group->units.begin(); ui != group->units.end(); ++ui) {
117 			selectedUnitsHandler.RemoveUnit(*ui);
118 		}
119 		return true;
120 	}
121 	else if (cmd == "selecttoggle")  {
122 		// do not select the group, just toggle its members with the current selection
123 		const CUnitSet& selUnits = selectedUnitsHandler.selectedUnits;
124 		CUnitSet::const_iterator ui;
125 		for (ui = group->units.begin(); ui != group->units.end(); ++ui) {
126 			if (selUnits.find(*ui) == selUnits.end()) {
127 				selectedUnitsHandler.AddUnit(*ui);
128 			} else {
129 				selectedUnitsHandler.RemoveUnit(*ui);
130 			}
131 		}
132 		return true;
133 	}
134 
135 	if (group->units.empty())
136 		return false;
137 
138 	if (selectedUnitsHandler.IsGroupSelected(num)) {
139 		const float3 groupCenter = group->CalculateCenter();
140 		camHandler->CameraTransition(0.5f);
141 		camHandler->GetCurrentController().SetPos(groupCenter);
142 	}
143 
144 	selectedUnitsHandler.SelectGroup(num);
145 	return true;
146 }
147 
CreateNewGroup()148 CGroup* CGroupHandler::CreateNewGroup()
149 {
150 	if (freeGroups.empty()) {
151 		CGroup* group = new CGroup(firstUnusedGroup++, this);
152 		groups.push_back(group);
153 		return group;
154 	} else {
155 		int id = freeGroups.back();
156 		freeGroups.pop_back();
157 		CGroup* group = new CGroup(id, this);
158 		groups[id] = group;
159 		return group;
160 	}
161 }
162 
RemoveGroup(CGroup * group)163 void CGroupHandler::RemoveGroup(CGroup* group)
164 {
165 	if (group->id < FIRST_SPECIAL_GROUP) {
166 		LOG_L(L_WARNING, "Trying to remove hot-key group %i", group->id);
167 		return;
168 	}
169 	if (selectedUnitsHandler.IsGroupSelected(group->id)) {
170 		selectedUnitsHandler.ClearSelected();
171 	}
172 	groups[group->id] = NULL;
173 	freeGroups.push_back(group->id);
174 	delete group;
175 }
176 
PushGroupChange(int id)177 void CGroupHandler::PushGroupChange(int id)
178 {
179 	changedGroups.insert(id);
180 }
181 
182