RawMsgStatus RawToCmd::Process()

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;
}