//////////////////////////////////////////////////////////////////////////////// // 액션을 실행한다. //////////////////////////////////////////////////////////////////////////////// void ActionRandomSay::execute (Creature * pCreature1 , Creature * pCreature2) throw(Error) { __BEGIN_TRY __BEGIN_DEBUG Assert(pCreature1 != NULL); Assert(pCreature2 == NULL); Assert(pCreature1->isNPC()); // RandomSay는 아래에서 보다시피, 임의의 범위(Start와 End) 안의 // 스크립트 중 랜덤을 돌려 하나를 클라이언트에게 보내는 식이다. // 그러므로 Start와 End 사이에 존재하지 않는 스크립트가 있으면 곤란하다. // 스크립트 테이블을 만들 때, RandomSay에 사용하는 것은 // 데이터가 반드시 연속적으로 존재하게 만들어야 한다. NPC* pNPC = dynamic_cast<NPC*>(pCreature1); ScriptID_t scriptID = m_StartScriptID + random() % (m_EndScriptID - m_StartScriptID + 1); const Script* pScript = g_pPublicScriptManager->getScript(scriptID); GCNPCSay gcNPCSay; gcNPCSay.setObjectID(pNPC->getObjectID()); gcNPCSay.setScriptID(pScript->getScriptID()); gcNPCSay.setSubjectID(0); Zone * pZone = pNPC->getZone(); Assert(pZone != NULL); pZone->broadcastPacket(pNPC->getX() , pNPC->getY() , &gcNPCSay); __END_DEBUG __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // // 일반 아이템을 처리한다. // ////////////////////////////////////////////////////////////////////////////// void CGShopRequestSellHandler::executeNormal (CGShopRequestSell* pPacket , Player* pPlayer) throw(ProtocolException , Error) { __BEGIN_TRY __BEGIN_DEBUG_EX #ifdef __GAME_SERVER__ ObjectID_t NPCID = pPacket->getObjectID(); ObjectID_t ITEMOID = pPacket->getItemObjectID(); GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPlayer); Creature* pCreature = pGamePlayer->getCreature(); PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pCreature); BYTE index = 0; bool bSpecialItem = false; Zone* pZone = pPC->getZone(); if (pZone == NULL) return sendFailPacket(pPacket, pPlayer); Creature* pNPCBase = NULL; /* try { pNPCBase = pZone->getCreature(NPCID); } catch (NoSuchElementException & nsee) { pNPCBase = NULL; } */ // NoSuch제거. by sigi. 2002.5.2 pNPCBase = pZone->getCreature(NPCID); if (pNPCBase == NULL || !pNPCBase->isNPC()) return sendFailPacket(pPacket, pPlayer); NPC* pNPC = dynamic_cast<NPC*>(pNPCBase); // 플레이어가 팔려고 하는 아이템을 가지고 있는지 검사 Inventory* pInventory = pPC->getInventory(); //Gold_t playerMoney = pPC->getGold(); Item* pItem = pInventory->getItemWithObjectID(ITEMOID); ItemNum_t itemNumber = pItem->getNum(); Price_t itemPrice = g_pPriceManager->getPrice(pItem, pNPC->getMarketCondBuy(), SHOP_RACK_NORMAL, pPC) * itemNumber; // 플레이어의 인벤토리에 아이템을 제거한다. pInventory->deleteItem(ITEMOID); pItem->whenPCLost(pPC); if (!pItem->destroy()) { filelog("shopDBBug.txt", "NoSuchItemInDB-destroy: %s", pItem->toString().c_str()); throw DisconnectException("아이템 지울려는데 DB에 없다."); } // 만약 벨트라면 안에 있는 포션을 삭제해준다. // DB에서 지우는 것은 Belt::destroy()를 부르는 것만으로 포션까지 삭제된다. if (pItem->getItemClass() == Item::ITEM_CLASS_BELT) { Inventory* pBeltInventory = dynamic_cast<Belt*>(pItem)->getInventory(); for (int y=0; y<pBeltInventory->getHeight(); y++) { for (int x=0; x<pBeltInventory->getWidth(); x++) { Item* pBeltItem = pBeltInventory->getItem(x, y); if (pBeltItem != NULL) { pBeltInventory->deleteItem(x, y); SAFE_DELETE(pBeltItem); } } } } // Skull 일 경우 Variable Manager 에서 머리값 배수 값으로 가격을 새로 계산한다 if (pItem->getItemClass() == Item::ITEM_CLASS_SKULL) itemPrice = itemPrice * (g_pVariableManager->getHeadPriceBonus() / 100); // ItemTrace Log 를 남겨야 한다면 남긴다 if (pItem != NULL && pItem->isTraceItem() ) remainTraceLog(pItem, pCreature->getName() , pNPC->getName(), ITEM_LOG_DELETE, DETAIL_SHOPSELL); // 플레이어에게 물건값을 지불한다. // pPC->setGoldEx(playerMoney+itemPrice); // by sigi. 2002.9.4 pPC->increaseGoldEx(itemPrice); // 플레이어가 물건 팔 때 처리할 것들을 처리한다. pPC->sellItem(pItem); if (pItem->getItemClass() == Item::ITEM_CLASS_MOON_CARD && pItem->getItemType() == 4) addOlympicStat(pPC, 4, (uint)(itemNumber)); bool bClearDefaultOptionTypes = false; if (pItem->getItemClass() == Item::ITEM_CLASS_EVENT_ITEM && pItem->getItemType() >= 32 && pItem->getItemType() <= 36) bClearDefaultOptionTypes = true; // NPC에게 자리가 충분하다면 플레이어가 판 아이템을 보관한다. // 운영자 명령어로 만든 아이템은 바로 없앤다. // 단 스페셜 아이템만을 보관한다. 노말 아이템은 그냥 버림. // 퀘스트 아이템은 보관하지 않고 버린다. if (pNPC->getShopType()==SHOPTYPE_NORMAL && pItem->getCreateType()!=Item::CREATE_TYPE_CREATE && !pItem->getOptionTypeList().empty() && !pItem->isTimeLimitItem()) { bSpecialItem = true; index = pNPC->getFirstEmptySlot(SHOP_RACK_SPECIAL); if (index < SHOP_RACK_INDEX_MAX) { // 아이템을 추가한다. pNPC->insertShopItem(SHOP_RACK_SPECIAL, index, pItem); // 스페셜 아이템을 NPC가 진열장에 추가했으므로, 상점 버전이 올라간다. pNPC->increaseShopVersion(SHOP_RACK_SPECIAL); //////////////////////////////////////////////////////////////////////////// // 근처의 플레이어들에게는 GCShopBought를... //////////////////////////////////////////////////////////////////////////// int CenterX = pNPC->getX(); int CenterY = pNPC->getY(); Creature* pNearCreature = NULL; Player* pNearPlayer = NULL; GCShopBought boughtpkt; boughtpkt.setObjectID(NPCID); if (!pItem->getOptionTypeList().empty()) { boughtpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_SPECIAL)); boughtpkt.setShopType(SHOP_RACK_SPECIAL); } else { boughtpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_NORMAL)); boughtpkt.setShopType(SHOP_RACK_NORMAL); } boughtpkt.setShopIndex(index); boughtpkt.setItemObjectID(ITEMOID); boughtpkt.setItemClass(pItem->getItemClass()); boughtpkt.setItemType(pItem->getItemType()); boughtpkt.setOptionType(pItem->getOptionTypeList()); boughtpkt.setDurability(pItem->getDurability()); boughtpkt.setSilver(pItem->getSilver()); boughtpkt.setGrade(pItem->getGrade()); boughtpkt.setEnchantLevel(pItem->getEnchantLevel()); //pZone->broadcastPacket(pNPC->getX(), pNPC->getY(), &boughtpkt, pPC); try { for (int zx=CenterX-5; zx<=CenterX+5; zx++) { for (int zy=CenterY-5; zy<=CenterY+5; zy++) { // 바운드를 넘어가지 않는가를 체크 if (!isValidZoneCoord(pZone, zx, zy)) continue; Tile & tile = pZone->getTile(zx, zy); // 걸어다니는 크리쳐를 검색 if (tile.hasCreature(Creature::MOVE_MODE_WALKING)) { pNearCreature = tile.getCreature(Creature::MOVE_MODE_WALKING); if (pNearCreature == NULL) continue; // 방금 물건을 판 플레이어라면 생략 if (pNearCreature->getObjectID() == pPC->getObjectID()) continue; // 만약 플레이어라면 패킷을 보내준다. if (pNearCreature->isPC()) { pNearPlayer = pNearCreature->getPlayer(); if (pNearPlayer == NULL) continue; pNearPlayer->sendPacket(&boughtpkt); } } // 날아다니는 크리쳐를 검색 if (tile.hasCreature(Creature::MOVE_MODE_FLYING)) { pNearCreature = tile.getCreature(Creature::MOVE_MODE_FLYING); if (pNearCreature == NULL) continue; // 방금 물건을 판 플레이어라면 생략 if (pNearCreature->getObjectID() == pPC->getObjectID()) continue; // 만약 플레이어라면 패킷을 보내준다. if (pNearCreature->isPC()) { pNearPlayer = pNearCreature->getPlayer(); if (pNearPlayer == NULL) continue; pNearPlayer->sendPacket(&boughtpkt); } } } // end of for (ZoneCoord_t zy=CenterY-5; zy<=CenterY+5; zy++) } // end of for (ZoneCoord_t zx=CenterX-5; zx<=CenterX+5; zx++) } catch (Throwable & t) { filelog("shopbug_packet.log", "%s", t.toString().c_str()); } } // if (index < SHOP_RACK_INDEX_MAX) else { SAFE_DELETE(pItem); } } // if (pItem->getOptionType() != 0) else { bSpecialItem = false; SAFE_DELETE(pItem); } // 물건을 산 플레이어에게 GCShopSellOK를...보낸다. GCShopSellOK okpkt; okpkt.setObjectID(NPCID); if (bSpecialItem) okpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_SPECIAL)); else okpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_NORMAL)); okpkt.setItemObjectID(ITEMOID); //okpkt.setPrice(playerMoney+itemPrice); // playerMoney + itemPrice 가 MAX_MONEY를 넘어갈 수 있다. // 2003.1.8 by bezz okpkt.setPrice(pPC->getGold()); pPlayer->sendPacket(&okpkt); if (bClearDefaultOptionTypes ) { pPC->clearDefaultOptionTypes(); pPC->initAllStatAndSend(); if (pPC->isSlayer() ) { Slayer* pSlayer = dynamic_cast<Slayer*>(pPC); Assert(pSlayer != NULL); pSlayer->sendRealWearingInfo(); } else if (pPC->isVampire() ) { Vampire* pVampire = dynamic_cast<Vampire*>(pPC); Assert(pVampire != NULL); pVampire->sendRealWearingInfo(); } else if (pPC->isOusters() ) { Ousters* pOusters = dynamic_cast<Ousters*>(pPC); Assert(pOusters != NULL); pOusters->sendRealWearingInfo(); } } #endif __END_DEBUG_EX __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // // 모터 사이클을 처리한다. // ////////////////////////////////////////////////////////////////////////////// void CGShopRequestSellHandler::executeMotorcycle (CGShopRequestSell* pPacket , Player* pPlayer) throw(ProtocolException , Error) { __BEGIN_TRY __BEGIN_DEBUG_EX #ifdef __GAME_SERVER__ // 패킷 정보를 뽑아낸다. ObjectID_t NPCID = pPacket->getObjectID(); ObjectID_t ITEMOID = pPacket->getItemObjectID(); GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPlayer); Creature* pCreature = pGamePlayer->getCreature(); PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pCreature); Zone* pZone = pPC->getZone(); if (pZone == NULL) return sendFailPacket(pPacket, pPlayer); Creature* pNPCBase = NULL; /* try { pNPCBase = pZone->getCreature(NPCID); } catch (NoSuchElementException & nsee) { pNPCBase = NULL; } */ // NoSuch제거. by sigi. 2002.5.2 pNPCBase = pZone->getCreature(NPCID); if (pNPCBase == NULL || !pNPCBase->isNPC()) return sendFailPacket(pPacket, pPlayer); NPC* pNPC = dynamic_cast<NPC*>(pNPCBase); int CenterX = pNPC->getX(); int CenterY = pNPC->getY(); // 플레이어가 팔려고 하는 아이템을 가지고 있는지 검사 Inventory* pInventory = pPC->getInventory(); Gold_t playerMoney = pPC->getGold(); Item* pItem = pInventory->getItemWithObjectID(ITEMOID); if (pItem == NULL) return sendFailPacket(pPacket, pPlayer); // 주위 일정 범위를 검색해서, 모터 사이클이 있는지 확인한다. try { for (int zx=CenterX-5; zx<=CenterX+5; zx++) { for (int zy=CenterY-5; zy<=CenterY+5; zy++) { // 바운드를 넘어가지 않는가를 체크 if (!isValidZoneCoord(pZone, zx, zy)) continue; Tile & tile = pZone->getTile(zx, zy); if (tile.hasItem()) { Item* pItemOnTile = tile.getItem(); if (pItemOnTile == NULL) continue; // 만일 아이템이 타일 위에 있을 경우, 모터 사이클인지 확인한다. if (pItemOnTile->getItemClass() == Item::ITEM_CLASS_MOTORCYCLE) { DWORD targetID = dynamic_cast<Key*>(pItem)->getTarget(); ItemID_t motorcycleID = pItemOnTile->getItemID(); if (targetID == motorcycleID) { // 모터사이클을 DB에서 삭제한다. pItemOnTile->destroy(); // 플레이어의 인벤토리에서 열쇠를 제거한다. pInventory->deleteItem(ITEMOID); pItem->destroy(); SAFE_DELETE(pItem); // 열쇠 값이 아니라, 오토바이 값을 줘야 한다. Price_t itemPrice = g_pPriceManager->getPrice(pItemOnTile, pNPC->getMarketCondBuy(), SHOP_RACK_NORMAL, pPC); // 플레이어의 돈을 늘린다. //pPC->setGoldEx(playerMoney+itemPrice); // by sigi. 2002.9.4 pPC->increaseGoldEx(itemPrice); // 물건을 판 플레이어에게 GCShopSellOK를...보낸다. GCShopSellOK okpkt; okpkt.setObjectID(NPCID); if (!pItemOnTile->getOptionTypeList().empty()) okpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_SPECIAL)); else okpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_NORMAL)); okpkt.setItemObjectID(ITEMOID); okpkt.setPrice(playerMoney+itemPrice); pPlayer->sendPacket(&okpkt); // 뻑킹 센터에서 박스를 삭제해 준다. if (g_pParkingCenter->hasMotorcycleBox(motorcycleID)) g_pParkingCenter->deleteMotorcycleBox(motorcycleID); // NPC에게 자리가 충분하다면 플레이어가 판 아이템을 보관한다. // 단 스페셜 아이템만을 보관한다. 노말 아이템은 그냥 버림. //if (pItemOnTile->getOptionType() != 0) //{ // index = pNPC->getFirstEmptySlot(SHOP_RACK_SPECIAL); // if (index < SHOP_RACK_INDEX_MAX) // { // pNPC->insertShopItem(SHOP_RACK_SPECIAL, index, pItemOnTile); // // 스페셜 아이템을 NPC가 진열장에 추가했으므로, 상점 버전이 올라간다. // pNPC->increaseShopVersion(SHOP_RACK_SPECIAL); // } //} //else //{ // SAFE_DELETE(pItemOnTile); //} // 모터 사이클을 찾았으므로, 할 일이 끝났다. return; } } } } // end of for (ZoneCoord_t zy=CenterY-5; zy<=CenterY+5; zy++) } // end of for (ZoneCoord_t zx=CenterX-5; zx<=CenterX+5; zx++) } catch (Throwable & t) { filelog("shopbug_packet.log", "%s", t.toString().c_str()); } // FOR 루프를 다 돌고, 이까지 왔다는 것은 근처에 오토바이가 없다는 말이당... // 그러므로 모터 사이클 팔기가 실패했다는 것을 알린다. GCShopSellFail failpkt; failpkt.setObjectID(NPCID); pPlayer->sendPacket(&failpkt); #endif __END_DEBUG_EX __END_CATCH }
void Player::activation() { float lowestDist = 90; bool partFound = false; Part* closestPart = NULL; int closestPartID; float partDist = lowestDist + 1; //Looping thru all of the parts for(int i = 0; i < world->getPartAmount(); i++) { if(world->getPartUsable(i) == 1) //Making sure we are only looking at parts that can actually be used { float partX = world->getPartX(i); float partY = world->getPartY(i); float distX = partX - x; float distY = partY - y; float dist = agk::Sqrt(distX * distX + distY * distY); int useRange = 90; if(dist < useRange) //Checking if the part is within reach { partFound = true; if(dist < lowestDist) //CHecking if this is the closest part found { lowestDist = dist; //Changing the lowest distance partDist = dist; closestPart = world->getPartFromID(i); //Changing the lowest part we have found closestPartID = i; } } } } bool npcFound = false; NPC* closestNPC = NULL; float npcDist = lowestDist + 1; //Looping thru all the NPCs (Using the) for(unsigned int i = 0; i < defaultNPCGroup->getNPCAmount(); i++) { NPC* npc = defaultNPCGroup->getNPC(i); if(npc != NULL) { float npcX = npc->getX(); float npcY = npc->getY(); float distX = npcX - x; float distY = npcY - y; float dist = agk::Sqrt(distX * distX + distY * distY); if(dist < lowestDist) { npcFound = true; lowestDist = dist; npcDist = dist; closestNPC = npc; } } } if((partFound == true && closestPart != NULL) || (npcFound == true && closestNPC != NULL)) { if(partDist < npcDist) { //Positioning the activation text agk::SetTextVisible(activateText, 1); agk::SetTextPosition(activateText, agk::WorldToScreenX( closestPart->getX() ), agk::WorldToScreenY( closestPart->getY() )); //Changing the text to the use text of the part std::string fText; fText = i_activateName; fText.append(") "); fText.append(closestPart->getUseMsg()); agk::SetTextString(activateText, fText.data()); //Adding the background agk::SetSpritePosition(activateSprite, agk::WorldToScreenX(closestPart->getX()) -5.0f, agk::WorldToScreenY(closestPart->getY()) - 2.5f); agk::SetSpriteVisible(activateSprite, 1); agk::SetSpriteScale(activateSprite, agk::GetTextTotalWidth(activateText) + 10, agk::GetTextTotalHeight(activateText) + 5); //Checking if the part is activated if(Input::activate() == true) { //Getting the script of the item std::string actScript; actScript = ("scripts/"); actScript.append(closestPart->getActScript()); //Saving the part that was activated last for use with labels in lua world->setLastActive(closestPartID); //Checking if this is a lua script or an old script if(actScript.find(".lua") != -1) { LuaHandler::runScript(actScript); } else { //Starting the script Script::run(actScript.data(), closestPart, world, this); } } } else { //Positioning the activation text agk::SetTextVisible(activateText, 1); agk::SetTextPosition(activateText, agk::WorldToScreenX( closestNPC->getX() ), agk::WorldToScreenY( closestNPC->getY() )); //Changing the text to the use text of the part std::string fText; fText = i_activateName; fText.append(") "); fText.append("talk"); agk::SetTextString(activateText, fText.data()); //Adding the background agk::SetSpritePosition(activateSprite, agk::WorldToScreenX(closestNPC->getX()) -5.0f, agk::WorldToScreenY(closestNPC->getY()) - 2.5f); agk::SetSpriteVisible(activateSprite, 1); agk::SetSpriteScale(activateSprite, agk::GetTextTotalWidth(activateText) + 10, agk::GetTextTotalHeight(activateText) + 5); //Checking if the part is activated if(Input::activate() == true) { closestNPC->startConversation(); } } } else //No parts where found, hide the text { agk::SetTextVisible(activateText, 0); agk::SetSpriteVisible(activateSprite, 0); } }