Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement units cycling #2474

Merged
merged 7 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions client/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,10 @@

// client
#include "audio/audio.h"
#include "chatline.h"
#include "client_main.h"
#include "climap.h"
#include "climisc.h"
#include "control.h"
#include "editor.h"
#include "goto.h"
#include "governor.h"
#include "options.h"
Expand Down Expand Up @@ -359,14 +357,34 @@ static void ask_server_for_actions(struct unit *punit)
}

/**
Return TRUE iff this unit is in focus.
Return TRUE if this unit is in focus.
*/
bool unit_is_in_focus(const struct unit *punit)
{
const auto &focus = get_units_in_focus();
return std::find(focus.begin(), focus.end(), punit) != focus.end();
}

/**
Returns TRUE if this unit is on stand by.

A unit is on stand by, if it is idle or sentried and not busy
building anything.
*/
bool unit_is_on_stand_by(const struct unit *punit)
{
if (ACTIVITY_IDLE != punit->activity
&& ACTIVITY_SENTRY != punit->activity) {
return false;
}

if (!can_unit_do_activity(punit, ACTIVITY_IDLE)) {
return false;
}

return true;
}

/**
Return TRUE iff a unit on this tile is in focus.
*/
Expand Down
1 change: 1 addition & 0 deletions client/control.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ void wakeup_sentried_units(struct tile *ptile);
void clear_unit_orders(struct unit *punit);

bool unit_is_in_focus(const struct unit *punit);
bool unit_is_on_stand_by(const struct unit *punit);
struct unit *get_focus_unit_on_tile(const struct tile *ptile);
struct unit *head_of_units_in_focus();
std::vector<unit *> &get_units_in_focus();
Expand Down
162 changes: 161 additions & 1 deletion client/hudwidget.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (c) 1996-2023 Freeciv21 and Freeciv contributors. This file is
Copyright (c) 1996-2024 Freeciv21 and Freeciv contributors. This file is
part of Freeciv21. Freeciv21 is free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
Expand Down Expand Up @@ -44,6 +44,12 @@
#include "widgets/decorations.h"

static QString popup_terrain_info(struct tile *ptile);
static bool cycle_units_predicate(const struct unit *current_unit,
const struct unit *candidate);
static std::tuple<int, int>
cycle_units_get_count_and_index(const struct unit *current_unit);
static void cycle_units_select_index(const struct unit *current_unit,
int index);

/**
Returns true if player has any unit of unit_type
Expand Down Expand Up @@ -528,6 +534,9 @@ hud_units::hud_units(QWidget *parent)
setLayout(main_layout);
mw = new move_widget(this);
setFocusPolicy(Qt::ClickFocus);

connect(&unit_label, &click_label::wheel_scrolled, this,
&hud_units::cycle_units);
}

/**
Expand Down Expand Up @@ -770,6 +779,18 @@ void hud_units::update_actions()
show();
}

/**
Cycle idle or sentried units for the unittype of the currently
selected unit.

The sign of direction determines in which direction the units will
be cycled.
*/
void hud_units::cycle_units(const int direction)
{
::cycle_units(direction);
}

/**
Custom label with extra mouse events
*/
Expand All @@ -789,6 +810,24 @@ void click_label::mousePressEvent(QMouseEvent *e)
}
}

/**
Wheel event for click_label
*/
void click_label::wheelEvent(QWheelEvent *e)
{
static int accumulatedDelta = 0;
accumulatedDelta += e->angleDelta().y();

if (abs(accumulatedDelta) < 120) {
return;
}

int steps = accumulatedDelta / 120;
accumulatedDelta = accumulatedDelta - steps * 120;

emit wheel_scrolled(steps);
}

/**
Centers on current unit
*/
Expand Down Expand Up @@ -1731,3 +1770,124 @@ void hud_battle_log::showEvent(QShowEvent *event)
m_timer.restart();
setVisible(true);
}

/**
This predicate is called by cycle_units and friends to filter
relevant units from the iterated units.

current_unit is the currently selected unit.
candidate is the candidate checked for relevance.
*/
static bool cycle_units_predicate(const struct unit *current_unit,
const struct unit *candidate)
{
fc_assert_ret_val(current_unit != nullptr && candidate != nullptr, false);

if (candidate->utype != current_unit->utype) {
return false;
}

return unit_is_on_stand_by(candidate);
}

/**
Gets the count of relevant units and determines the index of the
current unit among these units.

If current_unit is not among the relevant units, then index 0 is
assumed by default.

current_unit is the currently selected unit.
*/
static std::tuple<int, int>
cycle_units_get_count_and_index(const struct unit *current_unit)
{
fc_assert_ret_val(current_unit != nullptr, std::make_tuple(0, 0));

int unit_count = 0;
int current_unit_index = 0;

unit_list_iterate(client_player()->units, punit)
{
if (!cycle_units_predicate(current_unit, punit)) {
continue;
}

if (current_unit->id == punit->id) {
current_unit_index = unit_count;
}

unit_count++;
}
unit_list_iterate_end;

return {unit_count, current_unit_index};
}

/**
Selects the next unit when units are cycled.

current_unit is the currently selected unit.
index is the index of the unit to be selected next among the
relevant units.
*/
static void cycle_units_select_index(const struct unit *current_unit,
int index)
{
fc_assert_ret(current_unit != nullptr);

// Select the next unit.
int unit_count = 0;
unit_list_iterate(client_player()->units, punit)
{
if (!cycle_units_predicate(current_unit, punit)) {
continue;
}

if (unit_count == index) {
unit_focus_set_and_select(punit);
}

unit_count++;
}
unit_list_iterate_end;
}

/**
Cycle idle or sentried units for the unittype of the currently
selected unit.

The sign of direction determines in which direction the units will
be cycled.
*/
void cycle_units(const int direction)
{
if (get_num_units_in_focus() != 1) {
lmoureaux marked this conversation as resolved.
Show resolved Hide resolved
return;
}

struct unit *current_unit = head_of_units_in_focus();

auto [unit_count, current_unit_index] =
cycle_units_get_count_and_index(current_unit);

if (unit_count == 0) {
return;
}

// Determine the index of the unit to be selected next.
int cycle_unit_index = current_unit_index;
if (direction > 0) {
cycle_unit_index++;
} else if (direction < 0) {
cycle_unit_index--;
}

if (cycle_unit_index < 0) {
cycle_unit_index = unit_count - 1;
} else if (cycle_unit_index >= unit_count) {
cycle_unit_index = 0;
}

cycle_units_select_index(current_unit, cycle_unit_index);
}
8 changes: 7 additions & 1 deletion client/hudwidget.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**************************************************************************
Copyright (c) 1996-2020 Freeciv21 and Freeciv contributors. This file is
Copyright (c) 1996-2024 Freeciv21 and Freeciv contributors. This file is
part of Freeciv21. Freeciv21 is free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
Expand Down Expand Up @@ -43,6 +43,7 @@ struct tile;
struct unit;
struct unit_list;

void cycle_units(const int direction);
void show_new_turn_info();
bool has_player_unit_type(Unit_type_id utype);

Expand Down Expand Up @@ -144,11 +145,13 @@ class click_label : public QLabel {
click_label();
signals:
void left_clicked();
void wheel_scrolled(const int delta);
private slots:
void mouse_clicked();

protected:
void mousePressEvent(QMouseEvent *e) override;
void wheelEvent(QWheelEvent *e) override;
};

/****************************************************************************
Expand Down Expand Up @@ -221,6 +224,9 @@ class hud_units : public QFrame {
move_widget *mw;
unit_list *ul_units;
tile *current_tile;

private slots:
void cycle_units(const int delta);
};

/****************************************************************************
Expand Down
7 changes: 2 additions & 5 deletions client/views/view_units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,8 @@ void units_view::find_nearest()
if ((punit = find_nearest_unit(utype, ptile))) {
queen()->mapview_wdg->center_on_tile(punit->tile);

if (ACTIVITY_IDLE == punit->activity
|| ACTIVITY_SENTRY == punit->activity) {
if (can_unit_do_activity(punit, ACTIVITY_IDLE)) {
unit_focus_set_and_select(punit);
}
if (unit_is_on_stand_by(punit)) {
unit_focus_set_and_select(punit);
}
}

Expand Down
3 changes: 3 additions & 0 deletions docs/Manuals/Game/unit-controls.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ appear in quotes after the Unit ID value.
If the unit selected is not in your field of vision on the map, then you can click on the icon for the unit on
the left side and the game map will center on the unit for you. As with other widgets in Freeciv21, you can
click+drag the widget to move it by using the plus symbol in the upper left corner.

If you scroll your mouse wheel over the unit icon on the left side of the widget, the game will cycle through
idle or sentried units of the same unit type.
Loading