bool WidgetInput::checkClick() { // disabled buttons can't be clicked; if (!enabled) return false; // main button already in use, new click not allowed if (inpt->lock[MAIN1]) return false; // main click released, so the button state goes back to unpressed if (pressed && !inpt->lock[MAIN1]) { pressed = false; if (isWithinRect(pos, inpt->mouse)) { // activate upon release return true; } } pressed = false; // detect new click if (inpt->pressing[MAIN1]) { if (isWithinRect(pos, inpt->mouse)) { inpt->lock[MAIN1] = true; pressed = true; } } return false; }
/** * On mouseover, show tooltip for buttons */ TooltipData MenuActionBar::checkTooltip(const Point& mouse) { TooltipData tip; if (isWithinRect(menus[MENU_CHARACTER]->pos, mouse)) { if (COLORBLIND && requires_attention[MENU_CHARACTER]) tip.addText(msg->get("Character") + " (*)"); else tip.addText(msg->get("Character")); tip.addText(menu_labels[MENU_CHARACTER]); return tip; } if (isWithinRect(menus[MENU_INVENTORY]->pos, mouse)) { if (COLORBLIND && requires_attention[MENU_INVENTORY]) tip.addText(msg->get("Inventory") + " (*)"); else tip.addText(msg->get("Inventory")); tip.addText(menu_labels[MENU_INVENTORY]); return tip; } if (isWithinRect(menus[MENU_POWERS]->pos, mouse)) { if (COLORBLIND && requires_attention[MENU_POWERS]) tip.addText(msg->get("Powers") + " (*)"); else tip.addText(msg->get("Powers")); tip.addText(menu_labels[MENU_POWERS]); return tip; } if (isWithinRect(menus[MENU_LOG]->pos, mouse)) { if (COLORBLIND && requires_attention[MENU_LOG]) tip.addText(msg->get("Log") + " (*)"); else tip.addText(msg->get("Log")); tip.addText(menu_labels[MENU_LOG]); return tip; } for (unsigned i = 0; i < slots_count; i++) { if (slots[i] && isWithinRect(slots[i]->pos, mouse)) { if (hotkeys_mod[i] != 0) { tip.addText(powers->powers[hotkeys_mod[i]].name); } tip.addText(labels[i]); } } return tip; }
/** * Click on the map to pick up loot. We need the camera position to translate * screen coordinates to map locations. */ ItemStack LootManager::checkPickup(const Point& mouse, const FPoint& cam, const FPoint& hero_pos) { Rect r; ItemStack loot_stack; // check left mouse click if (inpt->usingMouse()) { // I'm starting at the end of the loot list so that more recently-dropped // loot is picked up first. If a player drops several loot in the same // location, picking it back up will work like a stack. std::vector<Loot>::iterator it; for (it = loot.end(); it != loot.begin(); ) { --it; // loot close enough to pickup? if (fabs(hero_pos.x - it->pos.x) < INTERACT_RANGE && fabs(hero_pos.y - it->pos.y) < INTERACT_RANGE && !it->isFlying()) { Point p = map_to_screen(it->pos.x, it->pos.y, cam.x, cam.y); r.x = p.x - TILE_W_HALF; r.y = p.y - TILE_H_HALF; r.w = TILE_W; r.h = TILE_H; // clicked in pickup hotspot? if ((it->tip_visible && isWithinRect(it->tip_bounds, mouse)) || isWithinRect(r, mouse)) { curs->setCursor(CURSOR_INTERACT); if (inpt->pressing[MAIN1] && !inpt->lock[MAIN1]) { inpt->lock[MAIN1] = true; if (!it->stack.empty()) { loot_stack = it->stack; it = loot.erase(it); return loot_stack; } } } } } } // check pressing Enter/Return if (inpt->pressing[ACCEPT] && !inpt->lock[ACCEPT]) { loot_stack = checkNearestPickup(hero_pos); if (!loot_stack.empty()) { inpt->lock[ACCEPT] = true; } } return loot_stack; }
bool MenuActionBar::isWithinSlots(const Point& mouse) { for (unsigned i=0; i<slots_count; i++) { if (slots[i] && isWithinRect(slots[i]->pos, mouse)) return true; } return false; }
/** * Show mouseover descriptions of disciplines and powers */ TooltipData MenuPowers::checkTooltip(const Point& mouse) { TooltipData tip; for (size_t i=0; i<power_cell.size(); i++) { if (tab_control && (tab_control->getActiveTab() != power_cell[i].tab)) continue; int cell_index = getCellByPowerIndex(power_cell[i].id, power_cell_all); if (!checkCellVisible(cell_index)) continue; if (slots[i] && isWithinRect(slots[i]->pos, mouse)) { bool base_unlocked = checkUnlocked(cell_index) || std::find(stats->powers_list.begin(), stats->powers_list.end(), power_cell[i].id) != stats->powers_list.end(); createTooltip(&tip, static_cast<int>(i), power_cell, !base_unlocked); if (!power_cell[i].upgrades.empty()) { int next_level = getNextLevelCell(static_cast<int>(i)); if (next_level != -1) { tip.addText("\n" + msg->get("Next Level:")); createTooltip(&tip, next_level, power_cell_upgrade, base_unlocked); } } return tip; } } return tip; }
bool MenuActionBar::isWithinMenus(const Point& mouse) { for (unsigned i=0; i<4; i++) { if (isWithinRect(menus[i]->pos, mouse)) return true; } return false; }
/** * If mousing-over an item with a tooltip, return that tooltip data. * * @param mouse The x,y screen coordinates of the mouse cursor */ TooltipData WidgetButton::checkTooltip(const Point& mouse) { TooltipData _tip; if (isWithinRect(pos, mouse) && tooltip != "") { _tip.addText(tooltip); } return _tip; }
int MenuInventory::areaOver(const Point& position) { if (isWithinRect(carried_area, position)) { return CARRIED; } else { for (unsigned int i=0; i<equipped_area.size(); i++) { if (isWithinRect(equipped_area[i], position)) { return EQUIPMENT; } } } // point is inside the inventory menu, but not over a slot if (isWithinRect(window_area, position)) { return INV_WINDOW; } return -2; }
/** * CTRL-click a hotkey to clear it */ void MenuActionBar::remove(const Point& mouse) { for (unsigned i=0; i<slots_count; i++) { if (slots[i] && isWithinRect(slots[i]->pos, mouse)) { if (locked[i]) return; hotkeys[i] = 0; updated = true; return; } } }
/** * Sets and releases the "pressed" visual state of the button * If press and release, activate (return true) */ bool WidgetButton::checkClick(int x, int y) { Point mouse(x,y); // Change the hover state hover = isWithinRect(pos, mouse); // Check the tooltip tip_new = checkTooltip(mouse); // disabled buttons can't be clicked; if (!enabled) return false; // main button already in use, new click not allowed if (inpt->lock[MAIN1]) return false; if (inpt->lock[ACCEPT]) return false; // main click released, so the button state goes back to unpressed if (pressed && !inpt->lock[MAIN1] && !inpt->lock[ACCEPT]) { pressed = false; return true; } pressed = false; // detect new click if (inpt->pressing[MAIN1]) { if (isWithinRect(pos, mouse)) { inpt->lock[MAIN1] = true; pressed = true; } } return false; }
/** * Click-to-drag a power (to the action bar) */ int MenuPowers::click(const Point& mouse) { int active_tab = (tab_control) ? tab_control->getActiveTab() : 0; for (size_t i=0; i<power_cell.size(); i++) { if (slots[i] && isWithinRect(slots[i]->pos, mouse) && (power_cell[i].tab == active_tab)) { if (TOUCHSCREEN) { if (!slots[i]->in_focus) { slots[i]->in_focus = true; if (!tabs.empty()) { tablist_pow[active_tab].setCurrent(slots[i]); } else { tablist.setCurrent(slots[i]); } return 0; } } int cell_index = getCellByPowerIndex(power_cell[i].id, power_cell_all); if (checkUnlock(cell_index) && points_left > 0 && power_cell[i].requires_point) { // unlock power stats->powers_list.push_back(power_cell[i].id); stats->check_title = true; setUnlockedPowers(); action_bar->addPower(power_cell[i].id, 0); return 0; } else if (checkUnlocked(cell_index) && !powers->powers[power_cell[i].id].passive) { // pick up and drag power slots[i]->defocus(); if (!tabs.empty()) { tablist_pow[active_tab].setCurrent(NULL); } else { tablist.setCurrent(NULL); } return power_cell[i].id; } else return 0; } } // nothing selected, defocus everything defocusTabLists(); return 0; }
/** * If mousing-over an item with a tooltip, return that tooltip data. * * @param mouse The x,y screen coordinates of the mouse cursor */ TooltipData WidgetListBox::checkTooltip(const Point& mouse) { TooltipData _tip; if (!inpt->usingMouse()) return _tip; for(unsigned i=0; i<rows.size(); i++) { if (i<items.size()) { if (isWithinRect(rows[i], mouse) && items[i+cursor].tooltip != "") { _tip.addText(items[i+cursor].tooltip); break; } } } return _tip; }
/** * If clicking while a menu is open, assume the player wants to rearrange the action bar */ int MenuActionBar::checkDrag(const Point& mouse) { int power_index; for (unsigned i=0; i<slots_count; i++) { if (slots[i] && isWithinRect(slots[i]->pos, mouse)) { drag_prev_slot = i; power_index = hotkeys[i]; hotkeys[i] = 0; last_mouse = mouse; updated = true; twostep_slot = -1; return power_index; } } return 0; }
/** * After dragging a power or item onto the action bar, set as new hotkey */ void MenuActionBar::drop(const Point& mouse, int power_index, bool rearranging) { for (unsigned i = 0; i < slots_count; i++) { if (slots[i] && isWithinRect(slots[i]->pos, mouse)) { if (rearranging) { if ((locked[i] && !locked[drag_prev_slot]) || (!locked[i] && locked[drag_prev_slot])) { locked[i] = !locked[i]; locked[drag_prev_slot] = !locked[drag_prev_slot]; } hotkeys[drag_prev_slot] = hotkeys[i]; } else if (locked[i]) return; hotkeys[i] = power_index; updated = true; return; } } }
/** * Perform one frame of logic * Age messages */ void MenuHUDLog::logic() { for (unsigned i=0; i<msg_age.size(); i++) { if (msg_age[i] > 0) msg_age[i]--; else remove(i); } // click to dismiss messages when rendered on top of other menus if (overlay_bg && click_to_dismiss) { if (inpt->pressing[MAIN1] && !inpt->lock[MAIN1]) { Rect overlay_area; overlay_area.x = static_cast<int>(overlay_bg->getDest().x); overlay_area.y = static_cast<int>(overlay_bg->getDest().y); overlay_area.w = overlay_bg->getGraphicsWidth(); overlay_area.h = overlay_bg->getGraphicsHeight(); if (isWithinRect(overlay_area, inpt->mouse)) { inpt->lock[MAIN1] = true; hide_overlay = true; } } } }
/** * Show all tooltips for loot on the floor */ void LootManager::renderTooltips(const FPoint& cam) { if (!SHOW_HUD) return; Point dest; bool tooltip_below = true; std::vector<Loot>::iterator it; for (it = loot.begin(); it != loot.end(); ) { it->tip_visible = false; if (it->on_ground) { Point p = map_to_screen(it->pos.x, it->pos.y, cam.x, cam.y); dest.x = p.x; dest.y = p.y + TILE_H_HALF; // adjust dest.y so that the tooltip floats above the item dest.y -= tooltip_margin; // set hitbox for mouse hover Rect hover; hover.x = p.x - TILE_W_HALF; hover.y = p.y - TILE_H_HALF; hover.w = TILE_W; hover.h = TILE_H; if ((LOOT_TOOLTIPS && !inpt->pressing[ALT]) || (!LOOT_TOOLTIPS && inpt->pressing[ALT]) || isWithinRect(hover, inpt->mouse)) { it->tip_visible = true; // create tooltip data if needed if (it->tip.isEmpty()) { if (!it->stack.empty()) { it->tip = items->getShortTooltip(it->stack); } } // try to prevent tooltips from overlapping tip->prerender(it->tip, dest, STYLE_TOPLABEL); std::vector<Loot>::iterator test_it; for (test_it = loot.begin(); test_it != it; ) { if (rectsOverlap(test_it->tip_bounds, tip->bounds)) { if (tooltip_below) dest.y = test_it->tip_bounds.y + test_it->tip_bounds.h + TOOLTIP_OFFSET; else dest.y = test_it->tip_bounds.y - test_it->tip_bounds.h + TOOLTIP_OFFSET; tip->bounds.y = dest.y; } ++test_it; } tip->render(it->tip, dest, STYLE_TOPLABEL); it->tip_bounds = tip->bounds; // only display one tooltip if we got it from hovering if (!LOOT_TOOLTIPS && !inpt->pressing[ALT]) break; } } tooltip_below = !tooltip_below; ++it; } }
void GameStateLoad::logic() { if (inpt->window_resized) refreshWidgets(); for (size_t i = 0; i < game_slots.size(); ++i) { if (static_cast<int>(i) == selected_slot) { if (game_slots[i]->preview_turn_ticks > 0) game_slots[i]->preview_turn_ticks--; if (game_slots[i]->preview_turn_ticks == 0) { game_slots[i]->preview_turn_ticks = GAMESLOT_PREVIEW_TURN_DURATION; game_slots[i]->stats.direction++; if (game_slots[i]->stats.direction > 7) game_slots[i]->stats.direction = 0; } } game_slots[i]->preview.logic(); } if (!confirm->visible) { tablist.logic(true); if (button_exit->checkClick() || (inpt->pressing[CANCEL] && !inpt->lock[CANCEL])) { inpt->lock[CANCEL] = true; showLoading(); setRequestedGameState(new GameStateTitle()); } if (loading_requested) { loading = true; loading_requested = false; logicLoading(); } bool outside_scrollbar = true; if (button_new->checkClick()) { // create a new game showLoading(); GameStateNew* newgame = new GameStateNew(); newgame->game_slot = (game_slots.empty() ? 1 : game_slots.back()->id+1); delete_items = false; setRequestedGameState(newgame); } else if (button_load->checkClick()) { loading_requested = true; } else if (button_delete->checkClick()) { // Display pop-up to make sure save should be deleted confirm->visible = true; confirm->render(); } else if (game_slots.size() > 0) { Rect scroll_area = slot_pos[0]; scroll_area.h = slot_pos[0].h * game_slot_max; if (isWithinRect(scroll_area, inpt->mouse)) { if (inpt->pressing[MAIN1] && !inpt->lock[MAIN1]) { for (int i=0; i<visible_slots; ++i) { if (isWithinRect(slot_pos[i], inpt->mouse)) { inpt->lock[MAIN1] = true; setSelectedSlot(i + scroll_offset); updateButtons(); break; } } } else if (inpt->scroll_up) { scrollUp(); } else if (inpt->scroll_down) { scrollDown(); } } else if (has_scroll_bar) { switch (scrollbar->checkClick(inpt->mouse.x, inpt->mouse.y)) { case 1: scrollUp(); outside_scrollbar = false; break; case 2: scrollDown(); outside_scrollbar = false; break; case 3: scroll_offset = scrollbar->getValue(); if (scroll_offset >= static_cast<int>(game_slots.size()) - visible_slots) { scroll_offset = static_cast<int>(game_slots.size()) - visible_slots; } outside_scrollbar = false; break; default: break; } } if (outside_scrollbar && inpt->pressing[MAIN1] && !inpt->lock[MAIN1]) { inpt->lock[MAIN1] = true; setSelectedSlot(-1); updateButtons(); } // Allow characters to be navigateable via up/down keys if (inpt->pressing[UP] && !inpt->lock[UP]) { inpt->lock[UP] = true; setSelectedSlot((selected_slot - 1 < 0) ? static_cast<int>(game_slots.size()) - 1 : selected_slot - 1); scroll_offset = std::min(static_cast<int>(game_slots.size()) - visible_slots, selected_slot); updateButtons(); } else if (inpt->pressing[DOWN] && !inpt->lock[DOWN]) { inpt->lock[DOWN] = true; setSelectedSlot((selected_slot + 1 == static_cast<int>(game_slots.size())) ? 0 : selected_slot + 1); scroll_offset = std::max(0, selected_slot-visible_slots+1); updateButtons(); } } } else if (confirm->visible) { confirm->logic(); if (confirm->confirmClicked) { removeSaveDir(game_slots[selected_slot]->id); delete game_slots[selected_slot]; game_slots[selected_slot] = NULL; game_slots.erase(game_slots.begin()+selected_slot); visible_slots = (game_slot_max > static_cast<int>(game_slots.size()) ? static_cast<int>(game_slots.size()) : game_slot_max); setSelectedSlot(-1); while (scroll_offset + visible_slots > static_cast<int>(game_slots.size())) { scroll_offset--; } updateButtons(); confirm->visible = false; confirm->confirmClicked = false; } } }
/** * Sets and releases the "pressed" visual state of the ListBox * If press and release, activate (return true) */ bool WidgetListBox::checkClick(int x, int y) { Point mouse(x, y); refresh(); // check scroll wheel Rect scroll_area; scroll_area.x = rows[0].x; scroll_area.y = rows[0].y; scroll_area.w = rows[0].w; scroll_area.h = rows[0].h * static_cast<int>(rows.size()); if (isWithinRect(scroll_area,mouse)) { inpt->lock_scroll = true; if (inpt->scroll_up) scrollUp(); if (inpt->scroll_down) scrollDown(); } else { inpt->lock_scroll = false; } // check ScrollBar clicks if (has_scroll_bar) { switch (scrollbar->checkClick(mouse.x,mouse.y)) { case 1: scrollUp(); break; case 2: scrollDown(); break; case 3: cursor = scrollbar->getValue(); refresh(); break; default: break; } } // main ListBox already in use, new click not allowed if (inpt->lock[MAIN1]) return false; // main click released, so the ListBox state goes back to unpressed if (pressed && !inpt->lock[MAIN1] && can_select) { pressed = false; for(unsigned i=0; i<rows.size(); i++) { if (i<items.size()) { if (isWithinRect(rows[i], mouse) && items[i+cursor].value != "") { // deselect other options if multi-select is disabled if (!multi_select) { for (unsigned j=0; j<items.size(); j++) { if (j!=i+cursor) items[j].selected = false; } } // activate upon release if (items[i+cursor].selected) { if (can_deselect) items[i+cursor].selected = false; } else { items[i+cursor].selected = true; } refresh(); return true; } } } } pressed = false; // detect new click if (inpt->pressing[MAIN1]) { for (unsigned i=0; i<rows.size(); i++) { if (isWithinRect(rows[i], mouse)) { inpt->lock[MAIN1] = true; pressed = true; } } } return false; }
bool WidgetInput::logic(int x, int y) { Point mouse(x, y); // Change the hover state hover = isWithinRect(pos, mouse); if (checkClick()) { edit_mode = true; } // if clicking elsewhere unfocus the text box if (inpt->pressing[MAIN1]) { if (!isWithinRect(pos, inpt->mouse)) { edit_mode = false; } } if (edit_mode) { inpt->slow_repeat[DEL] = true; inpt->slow_repeat[LEFT] = true; inpt->slow_repeat[RIGHT] = true; inpt->startTextInput(); if (inpt->inkeys != "") { // handle text input // only_numbers will restrict our input to 0-9 characters if (!only_numbers || (inpt->inkeys[0] >= 48 && inpt->inkeys[0] <= 57)) { text.insert(cursor_pos, inpt->inkeys); cursor_pos += inpt->inkeys.length(); trimText(); } // HACK: this prevents normal keys from triggering common menu shortcuts for (size_t i = 0; i < inpt->key_count; ++i) { if (inpt->pressing[i]) { inpt->lock[i] = true; inpt->repeat_ticks[i] = 1; } } } // handle backspaces if (inpt->pressing[DEL] && inpt->repeat_ticks[DEL] == 0) { if (!text.empty() && cursor_pos > 0) { // remove utf-8 character // size_t old_cursor_pos = cursor_pos; size_t n = cursor_pos-1; while (n > 0 && ((text[n] & 0xc0) == 0x80)) { n--; } text = text.substr(0, n) + text.substr(cursor_pos, text.length()); cursor_pos -= (cursor_pos) - n; trimText(); } } // cursor movement if (!text.empty() && cursor_pos > 0 && inpt->pressing[LEFT] && inpt->repeat_ticks[LEFT] == 0) { cursor_pos--; trimText(); } else if (!text.empty() && cursor_pos < text.length() && inpt->pressing[RIGHT] && inpt->repeat_ticks[RIGHT] == 0) { inpt->lock[RIGHT] = true; cursor_pos++; trimText(); } // defocus with Enter or Escape if (accept_to_defocus && inpt->pressing[ACCEPT] && !inpt->lock[ACCEPT]) { inpt->lock[ACCEPT] = true; edit_mode = false; } else if (inpt->pressing[CANCEL] && !inpt->lock[CANCEL]) { inpt->lock[CANCEL] = true; edit_mode = false; } } else { inpt->slow_repeat[DEL] = false; inpt->slow_repeat[LEFT] = false; inpt->slow_repeat[RIGHT] = false; inpt->stopTextInput(); } return true; }
/** * If pressing an action key (keyboard or mouseclick) and the power can be used, * add that power to the action queue */ void MenuActionBar::checkAction(std::vector<ActionData> &action_queue) { if (inpt->pressing[ACTIONBAR_USE] && tablist.getCurrent() == -1 && slots_count > 10) { tablist.setCurrent(slots[10]); slots[10]->in_focus = true; } // check click and hotkey actions for (unsigned i = 0; i < slots_count; i++) { ActionData action; action.hotkey = i; bool have_aim = false; slot_activated[i] = false; if (!slots[i]) continue; if (slot_enabled[i]) { // part two of two step activation if (static_cast<unsigned>(twostep_slot) == i && inpt->pressing[MAIN1] && !inpt->lock[MAIN1]) { have_aim = true; action.power = hotkeys_mod[i]; twostep_slot = -1; inpt->lock[MAIN1] = true; } // mouse/touch click else if (!NO_MOUSE && slots[i]->checkClick() == ACTIVATED) { have_aim = false; slot_activated[i] = true; action.power = hotkeys_mod[i]; // if a power requires a fixed target (like teleportation), break up activation into two parts // the first step is to mark the slot that was clicked on if (action.power > 0) { const Power &power = powers->powers[action.power]; if (power.starting_pos == STARTING_POS_TARGET || power.buff_teleport) { twostep_slot = i; action.power = 0; } else { twostep_slot = -1; } } } // joystick/keyboard action button else if (inpt->pressing[ACTIONBAR_USE] && static_cast<unsigned>(tablist.getCurrent()) == i) { have_aim = false; slot_activated[i] = true; action.power = hotkeys_mod[i]; twostep_slot = -1; } // pressing hotkey else if (i<10 && inpt->pressing[i+BAR_1]) { have_aim = true; action.power = hotkeys_mod[i]; twostep_slot = -1; } else if (i==10 && inpt->pressing[MAIN1] && !inpt->lock[MAIN1] && !isWithinRect(window_area, inpt->mouse)) { have_aim = true; action.power = hotkeys_mod[10]; twostep_slot = -1; } else if (i==11 && inpt->pressing[MAIN2] && !inpt->lock[MAIN2] && !isWithinRect(window_area, inpt->mouse)) { have_aim = true; action.power = hotkeys_mod[11]; twostep_slot = -1; } } // a power slot was activated if (action.power > 0 && static_cast<unsigned>(action.power) < powers->powers.size()) { const Power &power = powers->powers[action.power]; bool can_use_power = true; action.instant_item = (power.new_state == POWSTATE_INSTANT && power.requires_item > 0); // check if we can add this power to the action queue for (unsigned j=0; j<action_queue.size(); j++) { if (action_queue[j].hotkey == i) { // this power is already in the action queue, update its target action_queue[j].target = setTarget(have_aim, power.aim_assist); can_use_power = false; break; } else if (!action.instant_item && !action_queue[j].instant_item) { can_use_power = false; break; } } if (!can_use_power) continue; // set the target depending on how the power was triggered action.target = setTarget(have_aim, power.aim_assist); // add it to the queue action_queue.push_back(action); } else { // if we're not triggering an action that is currently in the queue, // remove it from the queue for (unsigned j=0; j<action_queue.size(); j++) { if (action_queue[j].hotkey == i) action_queue.erase(action_queue.begin()+j); } } } }