/* * The Mana World * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * * The Mana World 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 2 of the License, or * any later version. * * The Mana World 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 The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id$ */ #include "char_select.h" #include #include #include #include #include "button.h" #include "ok_dialog.h" #include "playerbox.h" #include "textfield.h" #include "windowcontainer.h" #include "../being.h" #include "../game.h" #include "../log.h" #include "../main.h" #include "../playerinfo.h" #include "../net/messagein.h" #include "../net/messageout.h" #include "../net/network.h" #include "../net/protocol.h" CharSelectDialog::CharDeleteConfirm::CharDeleteConfirm(CharSelectDialog *m): ConfirmDialog(m, "Confirm", "Are you sure you want to delete this character?"), master(m), mStatus(0) { } void CharSelectDialog::CharDeleteConfirm::action(const std::string &eventId) { //ConfirmDialog::action(eventId); if (eventId == "yes") { master->attemptCharDelete(); ConfirmDialog::yesButton->setEnabled(false); ConfirmDialog::noButton->setEnabled(false); mStatus = 1; } else { ConfirmDialog::action(eventId); } } void CharSelectDialog::CharDeleteConfirm::logic() { if (mStatus == 1) { if (packetReady()) { master->checkCharDelete(); ConfirmDialog::action("yes"); } else { flush(); } } } CharSelectDialog::CharSelectDialog(): Window("Select Character"), mStatus(0), mCurrentSlot(0) { selectButton = new Button("Ok"); cancelButton = new Button("Cancel"); newCharButton = new Button("New"); delCharButton = new Button("Delete"); previousButton = new Button("Previous"); nextButton = new Button("Next"); nameLabel = new gcn::Label("Name"); levelLabel = new gcn::Label("Level"); jobLevelLabel = new gcn::Label("Job Level"); moneyLabel = new gcn::Label("Money"); playerBox = new PlayerBox(); selectButton->setEventId("ok"); newCharButton->setEventId("new"); cancelButton->setEventId("cancel"); delCharButton->setEventId("delete"); previousButton->setEventId("previous"); nextButton->setEventId("next"); int w = 195; int h = 220; setContentSize(w, h); playerBox->setDimension(gcn::Rectangle(5, 5, w - 10, 90)); nameLabel->setDimension(gcn::Rectangle(10, 100, 128, 16)); levelLabel->setDimension(gcn::Rectangle(10, 116, 128, 16)); jobLevelLabel->setDimension(gcn::Rectangle(10, 132, 128, 16)); moneyLabel->setDimension(gcn::Rectangle(10, 148, 128, 16)); previousButton->setPosition(5, 170); nextButton->setPosition(previousButton->getWidth() + 10, 170); newCharButton->setPosition(5, h - 5 - newCharButton->getHeight()); delCharButton->setPosition( 5 + newCharButton->getWidth() + 5, newCharButton->getY()); cancelButton->setPosition( w - 5 - cancelButton->getWidth(), newCharButton->getY()); selectButton->setPosition( cancelButton->getX() - 5 - selectButton->getWidth(), newCharButton->getY()); add(playerBox); add(selectButton); add(cancelButton); add(newCharButton); add(delCharButton); add(previousButton); add(nextButton); add(nameLabel); add(levelLabel); add(jobLevelLabel); add(moneyLabel); // Set up event listener selectButton->addActionListener(this); cancelButton->addActionListener(this); newCharButton->addActionListener(this); delCharButton->addActionListener(this); previousButton->addActionListener(this); nextButton->addActionListener(this); selectButton->requestFocus(); setLocationRelativeTo(getParent()); setPlayerInfo(NULL); } void CharSelectDialog::changeSlot(int slot) { mCurrentSlot = slot; if (mCurrentSlot < 0) { mCurrentSlot = MAX_SLOT; } else if (mCurrentSlot > MAX_SLOT) { mCurrentSlot = 0; } if (char_info[mCurrentSlot] == NULL) { newCharButton->setEnabled(true); } } void CharSelectDialog::action(const std::string& eventId) { if (eventId == "ok" && n_character > 0) { // Start game newCharButton->setEnabled(false); delCharButton->setEnabled(false); selectButton->setEnabled(false); previousButton->setEnabled(false); nextButton->setEnabled(false); attemptCharSelect(); mStatus = 1; } else if (eventId == "cancel") { state = EXIT_STATE; } else if (eventId == "new") { if (n_character < MAX_SLOT + 1) { // Start new character dialog new CharCreateDialog(this, mCurrentSlot); } } else if (eventId == "delete") { // Delete character if (n_character > 0) { new CharDeleteConfirm(this); } } else if (eventId == "previous") { changeSlot(mCurrentSlot - 1); } else if (eventId == "next") { changeSlot(mCurrentSlot + 1); } } void CharSelectDialog::setPlayerInfo(PLAYER_INFO *pi) { if (pi) { std::stringstream nameCaption, levelCaption, jobCaption, moneyCaption; nameCaption << pi->name; levelCaption << "Lvl: " << pi->lvl; jobCaption << "Job Lvl: " << pi->jobLvl; moneyCaption << "Gold: " << pi->gp; nameLabel->setCaption(nameCaption.str()); levelLabel->setCaption(levelCaption.str()); jobLevelLabel->setCaption(jobCaption.str()); moneyLabel->setCaption(moneyCaption.str()); if (mStatus != 1) { newCharButton->setEnabled(false); delCharButton->setEnabled(true); selectButton->setEnabled(true); } playerBox->hairStyle = pi->hairStyle - 1; playerBox->hairColor = pi->hairColor - 1; playerBox->showPlayer = true; } else { nameLabel->setCaption("Name"); levelLabel->setCaption("Level"); jobLevelLabel->setCaption("Job Level"); moneyLabel->setCaption("Money"); newCharButton->setEnabled(true); delCharButton->setEnabled(false); selectButton->setEnabled(false); playerBox->hairStyle = 0; playerBox->hairColor = 0; playerBox->showPlayer = false; } } void CharSelectDialog::attemptCharDelete() { // Request character deletion MessageOut outMsg; outMsg.writeInt16(0x0068); outMsg.writeInt32(char_info[mCurrentSlot]->id); outMsg.writeString("a@a.com", 40); } void CharSelectDialog::checkCharDelete() { MessageIn msg = get_next_message(); if (msg.getId() == 0x006f) { skip(msg.getLength()); delete char_info[mCurrentSlot]; n_character--; char_info[mCurrentSlot] = NULL; setPlayerInfo(NULL); new OkDialog(this, "Info", "Player deleted"); } else if (msg.getId() == 0x0070) { new OkDialog(this, "Error", "Failed to delete character."); skip(msg.getLength()); } else { new OkDialog(this, "Error", "Unknown"); skip(msg.getLength()); } } void CharSelectDialog::attemptCharSelect() { // Request character selection MessageOut outMsg; outMsg.writeInt16(0x0066); outMsg.writeInt8(mCurrentSlot); } void CharSelectDialog::checkCharSelect() { // Receive reply MessageIn msg = get_next_message(); if (state == ERROR_STATE) { return; } logger->log("CharSelect: Packet ID: %x, Length: %d, in_size: %d", msg.getId(), msg.getLength(), in_size); if (msg.getId() == 0x0071) { char_ID = msg.readInt32(); map_path = "maps/" + msg.readString(16); map_path = map_path.substr(0, map_path.rfind(".")) + ".tmx.gz"; map_address = msg.readInt32(); map_port = msg.readInt16(); player_info = char_info[mCurrentSlot]; // Clear unselected players infos for (int i = 0; i < MAX_SLOT + 1; i++) { if (i != mCurrentSlot) { delete char_info[i]; } } free(char_info); state = CONNECTING_STATE; logger->log("CharSelect: Map: %s", map_path.c_str()); logger->log("CharSelect: Server: %s:%i", iptostring(map_address), map_port); closeConnection(); } else if (msg.getId() == 0x006c) { switch (msg.readInt8()) { case 0: errorMessage = "Access denied"; break; case 1: errorMessage = "Cannot use this ID"; break; default: errorMessage = "Unknown failure to select character"; break; } skip(msg.getLength()); } else if (msg.getId() == 0x0081) { switch (msg.readInt8()) { case 1: errorMessage = "Map server offline"; break; case 3: errorMessage = "Speed hack detected"; break; case 8: errorMessage = "Duplicated login"; break; default: errorMessage = "Unkown error with 0x0081"; break; } closeConnection(); state = ERROR_STATE; } // Todo: add other packets } void CharSelectDialog::logic() { if (n_character > 0) { setPlayerInfo(char_info[mCurrentSlot]); } if (mStatus == 1) { if (packetReady()) { checkCharSelect(); } else { flush(); } } } CharCreateDialog::CharCreateDialog(Window *parent, int slot): Window("Create Character", true, parent), mStatus(0), mSlot(slot) { nameField = new TextField(""); nameLabel = new gcn::Label("Name:"); nextHairColorButton = new Button(">"); prevHairColorButton = new Button("<"); hairColorLabel = new gcn::Label("Hair Color:"); nextHairStyleButton = new Button(">"); prevHairStyleButton = new Button("<"); hairStyleLabel = new gcn::Label("Hair Style:"); createButton = new Button("Create"); cancelButton = new Button("Cancel"); playerBox = new PlayerBox(); playerBox->showPlayer = true; nameField->setEventId("create"); nextHairColorButton->setEventId("nextcolor"); prevHairColorButton->setEventId("prevcolor"); nextHairStyleButton->setEventId("nextstyle"); prevHairStyleButton->setEventId("prevstyle"); createButton->setEventId("create"); cancelButton->setEventId("cancel"); int w = 200; int h = 150; setContentSize(w, h); playerBox->setDimension(gcn::Rectangle(80, 30, 110, 85)); nameLabel->setPosition(5, 5); nameField->setDimension( gcn::Rectangle(45, 5, w - 45 - 7, nameField->getHeight())); prevHairColorButton->setPosition(90, 35); nextHairColorButton->setPosition(165, 35); hairColorLabel->setPosition(5, 40); prevHairStyleButton->setPosition(90, 64); nextHairStyleButton->setPosition(165, 64); hairStyleLabel->setPosition(5, 70); cancelButton->setPosition( w - 5 - cancelButton->getWidth(), h - 5 - cancelButton->getHeight()); createButton->setPosition( cancelButton->getX() - 5 - createButton->getWidth(), h - 5 - cancelButton->getHeight()); nameField->addActionListener(this); nextHairColorButton->addActionListener(this); prevHairColorButton->addActionListener(this); nextHairStyleButton->addActionListener(this); prevHairStyleButton->addActionListener(this); createButton->addActionListener(this); cancelButton->addActionListener(this); add(playerBox); add(nameField); add(nameLabel); add(nextHairColorButton); add(prevHairColorButton); add(hairColorLabel); add(nextHairStyleButton); add(prevHairStyleButton); add(hairStyleLabel); add(createButton); add(cancelButton); setLocationRelativeTo(getParent()); } void CharCreateDialog::logic() { if (mStatus == 1) { if (packetReady()) { checkCharCreate(); } else { flush(); } } } void CharCreateDialog::action(const std::string& eventId) { if (eventId == "create") { if (getName().length() >= 4) { // Attempt to create the character createButton->setEnabled(false); attemptCharCreate(); mStatus = 1; } else { new OkDialog(this, "Error", "Your name needs to be at least 4 characters."); } } else if (eventId == "cancel") { windowContainer->scheduleDelete(this); } else if (eventId == "nextcolor") { playerBox->hairColor++; } else if (eventId == "prevcolor") { playerBox->hairColor += NR_HAIR_COLORS - 1; } else if (eventId == "nextstyle") { playerBox->hairStyle++; } else if (eventId == "prevstyle") { playerBox->hairStyle += NR_HAIR_STYLES - 1; } playerBox->hairColor %= NR_HAIR_COLORS; playerBox->hairStyle %= NR_HAIR_STYLES; } std::string CharCreateDialog::getName() { return nameField->getText(); } void CharCreateDialog::attemptCharCreate() { // Send character infos MessageOut outMsg; outMsg.writeInt16(0x0067); outMsg.writeString(getName(), 24); outMsg.writeInt8(5); outMsg.writeInt8(5); outMsg.writeInt8(5); outMsg.writeInt8(5); outMsg.writeInt8(5); outMsg.writeInt8(5); outMsg.writeInt8(mSlot); outMsg.writeInt16(playerBox->hairColor + 1); outMsg.writeInt16(playerBox->hairStyle + 1); } void CharCreateDialog::checkCharCreate() { MessageIn msg = get_next_message(); if (msg.getId() == 0x006d) { PLAYER_INFO *tempPlayer = new PLAYER_INFO; tempPlayer->id = msg.readInt32(); tempPlayer->xp = msg.readInt32(); tempPlayer->gp = msg.readInt32(); tempPlayer->jobXp = msg.readInt32(); tempPlayer->jobLvl = msg.readInt32(); msg.skip(8); // unknown msg.readInt32(); // option msg.readInt32(); // karma msg.readInt32(); // manner msg.skip(2); // unknown tempPlayer->hp = msg.readInt16(); tempPlayer->maxHp = msg.readInt16(); tempPlayer->mp = msg.readInt16(); tempPlayer->maxMp = msg.readInt16(); msg.readInt16(); // speed msg.readInt16(); // class tempPlayer->hairStyle = msg.readInt16(); tempPlayer->weapon = msg.readInt16(); tempPlayer->lvl = msg.readInt16(); msg.readInt16(); // skill point msg.readInt16(); // head bottom msg.readInt16(); // shield msg.readInt16(); // head option top msg.readInt16(); // head option mid tempPlayer->hairColor = msg.readInt16(); msg.readInt16(); // unknown tempPlayer->name = msg.readString(24); tempPlayer->STR = msg.readInt8(); tempPlayer->AGI = msg.readInt8(); tempPlayer->VIT = msg.readInt8(); tempPlayer->INT = msg.readInt8(); tempPlayer->DEX = msg.readInt8(); tempPlayer->LUK = msg.readInt8(); int slot = msg.readInt8(); // character slot msg.readInt8(); // unknown n_character++; char_info[slot] = tempPlayer; windowContainer->scheduleDelete(this); } else if (msg.getId() == 0x006e) { new OkDialog(this, "Error", "Failed to create character"); createButton->setEnabled(true); } else { new OkDialog(this, "Error", "Unknown error"); createButton->setEnabled(true); } skip(msg.getLength()); } void charSelectInputHandler(SDL_KeyboardEvent *keyEvent) { if (keyEvent->keysym.sym == SDLK_ESCAPE) { state = EXIT_STATE; } }