in rts/backend/raw2cmd.cc [88:199]
RawMsgStatus RawToCmd::Process(Tick tick, const GameEnv &env, const string&s, vector<CmdBPtr> *cmds, vector<UICmd> *ui_cmds) {
// Raw command:
// t 'L' i j: left click at (i, j)
// t 'R' i j: right clock at (i, j)
// t 'B' x0 y0 x1 y1: bounding box at (x0, y0, x1, y1)
// t 'S' percent: slide bar to percentage
// t 'F' : faster
// t 'W' : slower
// t 'C' : cycle through players.
// t lowercase : keyboard click.
// t is tick.
if (s.empty()) return PROCESSED;
assert(cmds != nullptr);
assert(ui_cmds != nullptr);
Tick t;
char c;
float percent;
PointF p, p2;
set<UnitId> selected;
cout << "Cmd: " << s << endl;
const RTSMap& m = env.GetMap();
istringstream ii(s);
string cc;
ii >> t >> cc;
if (cc.size() != 1) return PROCESSED;
c = cc[0];
switch(c) {
case 'L':
case 'R':
ii >> p;
if (! m.IsIn(p)) return FAILED;
{
UnitId closest_id = m.GetClosestUnitId(p, 1.5);
if (closest_id != INVALID) selected.insert(closest_id);
}
break;
case 'B':
ii >> p >> p2;
if (! m.IsIn(p) || ! m.IsIn(p2)) return FAILED;
// Reorder the four corners.
if (p.x > p2.x) swap(p.x, p2.x);
if (p.y > p2.y) swap(p.y, p2.y);
selected = m.GetUnitIdInRegion(p, p2);
break;
case 'F':
ui_cmds->push_back(UICmd::GetUIFaster());
return PROCESSED;
case 'W':
ui_cmds->push_back(UICmd::GetUISlower());
return PROCESSED;
case 'C':
ui_cmds->push_back(UICmd::GetUICyclePlayer());
return PROCESSED;
case 'S':
ii >> percent;
// cout << "Get slider bar notification " << percent << endl;
ui_cmds->push_back(UICmd::GetUISlideBar(percent));
return PROCESSED;
case 'P':
ui_cmds->push_back(UICmd::GetToggleGamePause());
return PROCESSED;
default:
break;
}
if (! is_mouse_motion(c)) _last_key = c;
// cout << "#Hotkey " << _hotkey_maps.size() << " player_id = " << _player_id << " _last_key = " << _last_key
// << " #selected = " << selected.size() << " #prev-selected: " << _sel_unit_ids.size() << endl;
// Rules:
// 1. we cannot control other player's units.
// 2. we cannot have friendly fire (enforced in the callback function)
bool cmd_success = false;
if (! _sel_unit_ids.empty() && selected.size() <= 1) {
UnitId id = (selected.empty() ? INVALID : *selected.begin());
auto it_key = _hotkey_maps.find(_last_key);
if (it_key != _hotkey_maps.end()) {
EventResp f = it_key->second;
for (auto it = _sel_unit_ids.begin(); it != _sel_unit_ids.end(); ++it) {
// cout << "Deal with unit" << *it << endl << flush;
if (Player::ExtractPlayerId(*it) != _player_id) continue;
// Only command our unit.
const Unit *u = env.GetUnit(*it);
// u has been deleted (e.g., killed)
// We won't delete it in our selection, since the selection will naturally update.
if (u == nullptr) continue;
CmdBPtr cmd = f(*u, _last_key, p, id, env).GetCmd();
if (! cmd.get() || ! env.GetGameDef().unit(u->GetUnitType()).CmdAllowed(cmd->type())) continue;
// Command successful.
cmds->emplace_back(std::move(cmd));
cmd_success = true;
}
}
}
// Command not issued. if it is a mouse event, simply reselect the unit (or deselect)
if (! cmd_success && is_mouse_motion(c)) select_new_units(selected);
if (cmd_success) _last_key = '~';
if (t > tick) return EXCEED_TICK;
return PROCESSED;
}