/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program 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 License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ultima/nuvie/core/nuvie_defs.h" #include "ultima/nuvie/screen/screen.h" #include "ultima/nuvie/misc/u6_llist.h" #include "ultima/nuvie/misc/u6_misc.h" #include "ultima/nuvie/files/u6_bmp.h" #include "ultima/nuvie/gui/gui.h" #include "ultima/nuvie/gui/gui_button.h" #include "ultima/nuvie/views/doll_widget.h" #include "ultima/nuvie/views/inventory_widget.h" #include "ultima/nuvie/views/spell_view.h" #include "ultima/nuvie/core/party.h" #include "ultima/nuvie/fonts/font.h" #include "ultima/nuvie/actors/actor.h" #include "ultima/nuvie/core/events.h" #include "ultima/nuvie/gui/widgets/map_window.h" #include "ultima/nuvie/gui/widgets/msg_scroll.h" #include "ultima/nuvie/usecode/usecode.h" #include "ultima/nuvie/views/view_manager.h" #include "ultima/nuvie/script/script.h" #include "ultima/nuvie/core/u6_objects.h" #include "ultima/nuvie/core/magic.h" #include "ultima/nuvie/keybinding/keys.h" namespace Ultima { namespace Nuvie { static const char circle_num_tbl[][8] = {"1ST", "2ND", "3RD", "4TH", "5TH", "6TH", "7TH", "8TH"}; static const int obj_n_reagent[8] = {OBJ_U6_MANDRAKE_ROOT, OBJ_U6_NIGHTSHADE, OBJ_U6_BLACK_PEARL, OBJ_U6_BLOOD_MOSS, OBJ_U6_SPIDER_SILK, OBJ_U6_GARLIC, OBJ_U6_GINSENG, OBJ_U6_SULFUROUS_ASH}; static const int NEWMAGIC_BMP_W = 144; static const int NEWMAGIC_BMP_H = 82; SpellView::SpellView(const Configuration *cfg) : DraggableView(cfg), spell_container(nullptr), background(nullptr), level(1), all_spells_mode(false), spell_num(0), event_mode(false), num_spells_per_page(8), caster(nullptr) { } SpellView::~SpellView() { if (background) { delete background; } } bool SpellView::init(Screen *tmp_screen, void *view_manager, uint16 x, uint16 y, Font *f, Party *p, TileManager *tm, ObjManager *om) { View::init(x, y, f, p, tm, om); SetRect(area.left, area.top, NEWMAGIC_BMP_W, NEWMAGIC_BMP_H + 16); Common::Path filename; config_get_path(config, "newmagic.bmp", filename); background = new U6Bmp(); if (background->load(filename) == false) return false; add_command_icons(tmp_screen, view_manager); return true; } void SpellView::PlaceOnScreen(Screen *s, GUI_DragManager *dm, int x, int y) { GUI_Widget::PlaceOnScreen(s, dm, x, y); } void SpellView::set_spell_caster(Actor *actor, Obj *s_container, bool eventMode) { caster = actor; spell_container = s_container; event_mode = eventMode; for (int shift = 0; shift < 8; shift++) { caster_reagents[shift] = caster->inventory_count_object(obj_n_reagent[shift]); } level = (spell_container->quality / 16) + 1; spell_num = spell_container->quality - (16 * level); if (Game::get_game()->has_unlimited_casting() || spell_container->find_in_container(OBJ_U6_SPELL, MAGIC_ALL_SPELLS, OBJ_MATCH_QUALITY)) all_spells_mode = true; else all_spells_mode = false; fill_cur_spell_list(); update_buttons(); Game::get_game()->set_mouse_pointer(1); // crosshairs } void SpellView::Display(bool full_redraw) { if (full_redraw || update_display) { screen->fill(bg_color, area.left, area.top + NEWMAGIC_BMP_H, area.width(), area.height() - NEWMAGIC_BMP_H); screen->blit(area.left, area.top, background->get_data(), 8, NEWMAGIC_BMP_W, NEWMAGIC_BMP_H, NEWMAGIC_BMP_W, true); } display_level_text(); display_spell_list_text(); DisplayChildren(full_redraw); #if 1 // FIXME: This shouldn't need to be in the loop update_buttons(); // It doesn't seem to hurt speed though screen->update(area.left, area.top, area.width(), area.height()); #else if (full_redraw || update_display) { update_display = false; screen->update(area.left, area.top, area.width(), area.height()); } #endif } uint8 SpellView::fill_cur_spell_list() { Magic *m = Game::get_game()->get_magic(); int j = 0; for (int i = 0; i < 16; i++) { cur_spells[i] = -1; if (m->get_spell((level - 1) * 16 + i) != nullptr && (all_spells_mode || spell_container->find_in_container(OBJ_U6_SPELL, (level - 1) * 16 + i, OBJ_MATCH_QUALITY))) cur_spells[j++] = (level - 1) * 16 + i; } return j; } sint8 SpellView::get_selected_index() const { for (uint8 i = 0; i < 16; i++) { if (cur_spells[i] == spell_container->quality) { return (sint8)i; } } return -1; } void SpellView::set_prev_level() { if (level == 1) return; uint8 old_level = level; uint8 num_spells = 0; for (; num_spells == 0;) { level--; if (level == 0) break; num_spells = fill_cur_spell_list(); } if (num_spells == 0) { level = old_level; fill_cur_spell_list(); } if (num_spells > num_spells_per_page) spell_container->quality = cur_spells[num_spells_per_page]; else { spell_container->quality = cur_spells[0]; } } void SpellView::set_next_level() { if (level == 8) return; uint8 old_level = level; uint8 num_spells = 0; for (; num_spells == 0;) { level++; if (level == 9) break; num_spells = fill_cur_spell_list(); } if (num_spells == 0) { level = old_level; fill_cur_spell_list(); } else spell_container->quality = cur_spells[0]; } void SpellView::move_left() { sint8 index = get_selected_index(); if (index < 0) index = 0; if (index >= num_spells_per_page) { spell_container->quality = cur_spells[0]; } else { set_prev_level(); } update_buttons(); update_display = true; } void SpellView::move_right() { sint8 index = get_selected_index(); if (index < 0) index = 0; if (index >= num_spells_per_page || cur_spells[num_spells_per_page] == -1) { set_next_level(); } else { spell_container->quality = cur_spells[num_spells_per_page]; } update_buttons(); update_display = true; } GUI_status SpellView::move_up() { sint8 index = get_selected_index(); if (index > 0 && index != num_spells_per_page) { spell_container->quality = cur_spells[index - 1]; update_display = true; } else move_left(); return GUI_YUM; } GUI_status SpellView::move_down() { sint8 index = get_selected_index(); if (index != -1 && index < 15 && index != (num_spells_per_page - 1)) { if (cur_spells[index + 1] != -1) { spell_container->quality = cur_spells[index + 1]; update_display = true; } else move_right(); } else move_right(); return GUI_YUM; } void SpellView::display_level_text() { font->drawString(screen, circle_num_tbl[level - 1], area.left + 96 + 8, area.top + NEWMAGIC_BMP_H); font->drawString(screen, "level", area.left + 96, area.top + NEWMAGIC_BMP_H + 8); return; } void SpellView::display_spell_list_text() { Magic *m = Game::get_game()->get_magic(); sint8 index = get_selected_index(); if (index >= num_spells_per_page) index = num_spells_per_page; else index = 0; for (uint8 i = 0; i < num_spells_per_page; i++) { sint16 spellNum = cur_spells[i + index]; if (spellNum != -1) { Spell *spell = m->get_spell((uint8)spellNum); display_spell_text(spell, i, spell_container->quality); } } } void SpellView::display_spell_text(Spell *spell, uint16 line_num, uint8 selected_spell) { line_num++; font->drawString(screen, spell->name, area.left + 16, area.top + (line_num * 8)); font->drawString(screen, Common::String::format("%d", get_available_spell_count(spell)).c_str(), area.left + NEWMAGIC_BMP_W - 24, area.top + (line_num * 8)); if (spell->num == selected_spell) font->drawChar(screen, 26, area.left + 8, area.top + (line_num * 8)); } uint16 SpellView::get_available_spell_count(const Spell *s) const { if (s->reagents == 0) // Help and Armageddon return 1; if (Game::get_game()->has_unlimited_casting()) return 99; sint32 min_reagents = -1; for (int shift = 0; shift < 8; shift++) { if (1 << shift & s->reagents) { if (min_reagents == -1 || caster_reagents[shift] < min_reagents) min_reagents = caster_reagents[shift]; } } if (min_reagents == -1) min_reagents = 0; return (uint16)min_reagents; } void SpellView::add_command_icons(Screen *tmp_screen, void *view_manager) { Tile *tile; Graphics::ManagedSurface *button_image; Graphics::ManagedSurface *button_image2; tile = tile_manager->get_tile(412); //left arrow icon button_image = tmp_screen->create_sdl_surface_from(tile->data, 8, 16, 16, 16); button_image2 = tmp_screen->create_sdl_surface_from(tile->data, 8, 16, 16, 16); left_button = new GUI_Button(this, 2 * 16, NEWMAGIC_BMP_H, button_image, button_image2, this); this->AddWidget(left_button); tile = tile_manager->get_tile(413); //right arrow icon button_image = tmp_screen->create_sdl_surface_from(tile->data, 8, 16, 16, 16); button_image2 = tmp_screen->create_sdl_surface_from(tile->data, 8, 16, 16, 16); right_button = new GUI_Button(this, 3 * 16, NEWMAGIC_BMP_H, button_image, button_image2, this); this->AddWidget(right_button); } void SpellView::event_mode_select_spell() { sint16 spellNum = get_selected_spell(); Game::get_game()->get_event()->select_spell_num(spellNum); release_focus(); } /* Move the cursor around */ GUI_status SpellView::KeyDown(const Common::KeyState &key) { KeyBinder *keybinder = Game::get_game()->get_keybinder(); ActionType a = keybinder->get_ActionType(key); switch (keybinder->GetActionKeyType(a)) { case NORTH_KEY: return move_up(); case SOUTH_KEY: return move_down(); case WEST_KEY: case PREVIOUS_PARTY_MEMBER_KEY: move_left(); break; case EAST_KEY: case NEXT_PARTY_MEMBER_KEY: move_right(); break; case HOME_KEY: // TODO - add going to first viable page break; case END_KEY: // TODO - add going to last viable page break; case DO_ACTION_KEY: if (Game::get_game()->get_event()->is_looking_at_spellbook()) { show_spell_description(); return GUI_YUM; } if (event_mode) { event_mode_select_spell(); return GUI_YUM; } return GUI_PASS; case CANCEL_ACTION_KEY: return cancel_spell(); case TOGGLE_CURSOR_KEY : break; default: return GUI_PASS; } return GUI_YUM; } GUI_status SpellView::cancel_spell() { Events *event = Game::get_game()->get_event(); if (event->is_looking_at_spellbook()) { close_look(); return GUI_YUM; } else if (event_mode) { event->select_spell_num(-1); release_focus(); return GUI_YUM; } event->set_mode(CAST_MODE); event->cancelAction(); return GUI_YUM; } GUI_status SpellView::MouseWheel(sint32 x, sint32 y) { if (y > 0) return move_up(); if (y < 0) return move_down(); return GUI_YUM; } GUI_status SpellView::MouseDown(int x, int y, Events::MouseButton button) { y -= area.top; x -= area.left; Events *event = Game::get_game()->get_event(); bool selecting_spell_target, canceling_spell, doing_nothing; if (Game::get_game()->is_original_plus()) { if (Game::get_game()->is_original_plus_full_map()) selecting_spell_target = (x < -7 || y > 194); else selecting_spell_target = (x < -7); canceling_spell = (x > 1 && (y > 101 || x > 137)); doing_nothing = ((x > -8 && x < 16) || (x > -8 && (y < 8 || (y > 71 && y < 195)))); } else { selecting_spell_target = (x < 0 && y > 0 && y < 162); canceling_spell = (x > 1 && (y > 101 || x > 137)); doing_nothing = (y < 8 || y > 71 || x < 16 || x > 134); } if (button == Events::BUTTON_RIGHT) return cancel_spell(); if (selecting_spell_target && !event_mode) { // cast selected spell on the map if (event->is_looking_at_spellbook()) { close_look(); return GUI_YUM; } event->target_spell(); if (event->get_mode() == INPUT_MODE) { y += area.top; x += area.left; Game::get_game()->get_map_window()->select_target(x, y); } return GUI_YUM; } if (canceling_spell) // cancel spell return cancel_spell(); if (doing_nothing) // do nothing return GUI_YUM; // selecting spell index sint8 index = get_selected_index(); if (index >= num_spells_per_page) index = num_spells_per_page; else index = 0; y = (y / num_spells_per_page) - 1; //printf("x = %d, y = %d index=%d\n", x, y, index); if (cur_spells[index + y] != -1) { spell_container->quality = cur_spells[index + y]; update_display = true; if (event->is_looking_at_spellbook()) show_spell_description(); else if (event_mode) event_mode_select_spell(); else Game::get_game()->get_event()->target_spell(); } return GUI_YUM; } void SpellView::hide_buttons() { if (left_button) left_button->Hide(); if (right_button) right_button->Hide(); } void SpellView::show_buttons() { if (left_button) left_button->Show(); if (right_button) right_button->Show(); } void SpellView::update_buttons() { show_buttons(); sint8 index = get_selected_index(); if (level == 1 && index <= (num_spells_per_page - 1) && left_button) left_button->Hide(); uint8 old_level = level; uint8 num_spells = 0; for (; num_spells == 0;) { level++; if (level == 9) break; num_spells = fill_cur_spell_list(); } level = old_level; fill_cur_spell_list(); if (right_button && ((level < 8 && num_spells == 0) || level == 8) && cur_spells[num_spells_per_page * (1 + index / num_spells_per_page)] == -1) right_button->Hide(); } void SpellView::close_look() { Game::get_game()->get_event()->set_looking_at_spellbook(false); Game::get_game()->get_scroll()->display_prompt(); Game::get_game()->get_view_manager()->close_spell_mode(); Game::get_game()->get_event()->endAction(); } void SpellView::show_spell_description() { if (get_selected_index() != -1) { sint16 index = get_selected_spell(); if (index < 256 && index > -1) Game::get_game()->get_magic()->show_spell_description((uint8)index); } close_look(); } GUI_status SpellView::callback(uint16 msg, GUI_CallBack *caller, void *data) { if (caller == left_button) { move_left(); return GUI_YUM; } else if (caller == right_button) { move_right(); return GUI_YUM; } return GUI_PASS; } } // End of namespace Nuvie } // End of namespace Ultima