Roarl

Members
  • Content Count

    41
  • Joined

  • Last visited

Everything posted by Roarl

    Loooooooooveeeee those WMOs!! Great job and thanks a lot for sharing.
  1. Sharing this here as well (copy/paste from Modcraft) Hi guys! Took me some time to post this because I couldn't find any fan for my laptop.... But I did some days ago so... Here you go! As I said before, I acknowledge this is not much. But I just wanna play my part in Modcraft as best as I can even if it doesn't mean a lot. First you will need the tables and rows which the script uses : SQL File Then the Creature Script : StockExchange.cpp /* 1.0.0 StockExchange NPC Script Description ----------- The script uses data from DB to define 5 different stocks (Name, Worth (in gold coins), Scale (scale of the stock's variations), OwnedRatio(so that players can't acquire more than its worth)). Players can buy and sell shares of those stocks, provided they have bought a licence first. Thanks to Rochet2 for the nice menu look and for helping me clean this script up! */ #include "ScriptPCH.h" #include "Config.h" #include "ScriptedCreature.h" #include "Language.h" #include "time.h" #define STOCKNUMBER 5 #define GOLDTOCOPPER 10000 namespace { class CS_StockExchange : public CreatureScript { public: CS_StockExchange() : CreatureScript("Creature_StockExchange") { } uint32 stock = 0; void OnPlayerTextEmote(Player* player, uint32 textEmote, uint32 emoteNum, ObjectGuid guid) override { } bool OnGossipHello(Player* plr, Creature* creature) override //MAIN MENU { //StockUpdate every 2 minutes QueryResult result = WorldDatabase.PQuery("SELECT `UpdateTime` FROM `stocks` WHERE iD = 0"); if (!result) return false; Field* field = result->Fetch(); uint32 stockPrevTime = field[0].GetUInt32(); uint32 realCurrTime = getMSTime(); uint32 stockDiff = getMSTimeDiff(stockPrevTime, realCurrTime); if (stockDiff >= 120000) { uint32 timeMult; timeMult = stockDiff / 120000; if (StockUpdate(timeMult)) TC_LOG_INFO("server.worldserver", "Stocks have been updated (%u)", realCurrTime); else { TC_LOG_INFO("server.worldserver", "Error while updating stocks..."); } stockPrevTime = realCurrTime; WorldDatabase.PExecute("UPDATE `stocks` SET `UpdateTime` = %u WHERE `iD` = 0", getMSTime()); } WorldSession* session = plr->GetSession(); QueryResult licencetest = CharacterDatabase.PQuery("SELECT `1`, `2`, `3`, `4`, `5` FROM `character_stockdata` WHERE iD = %u", plr->GetGUIDLow()); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Scroll_08:30:30:-18:0|tWhat are the current exchange rates?", GOSSIP_SENDER_MAIN, 2); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Book_11:30:30:-18:0|tHow many unsold shares are there left?", GOSSIP_SENDER_MAIN, 1); if (!licencetest) { plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_MISC_NOTE_02:30:30:-18:0|tI'd like to acquire a trade licence.", GOSSIP_SENDER_MAIN, 12, "This will cost you", 400000, false); } else //Player can invest if he has a licence { plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_02:30:30:-18:0|tI'd like to manage my portfolio.", GOSSIP_SENDER_MAIN, 3); } plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tIt was a pleasure to make business with you.", GOSSIP_SENDER_MAIN, 4); plr->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); return true; } bool OnGossipSelect(Player* plr, Creature* creature, uint32 sender, uint32 uiAction) override { int32 amount = 0; plr->PlayerTalkClass->ClearMenus(); WorldSession* session = plr->GetSession(); std::ostringstream message; float ownedratio[5]; int worth[5]; for (int iter = 1; iter < 6; iter++) { QueryResult result = WorldDatabase.PQuery("SELECT `OwnedRatio`,`Worth` FROM `stocks` WHERE iD = %u", iter); if (!result) return false; Field* field = result->Fetch(); ownedratio[iter-1]= field[0].GetFloat(); worth[iter-1] = field[1].GetInt32(); } switch (uiAction) { case 1: //REMAINING SHARES message << "REMAINING SHARES (in gold coins)$BRavenholdt Manor : " << worth[0] * (1 - ownedratio[0]) << "$BBloodsail : " << worth[1] * (1 - ownedratio[1]) << "$BSteamwheedle Cartel : " << worth[2] * (1 - ownedratio[2]) << "$BTheramore Silk Traders : " << worth[3] * (1 - ownedratio[3]) << "$BEldoar'norore : " << worth[4] * (1 - ownedratio[4]); creature->Whisper(message.str(), LANG_UNIVERSAL, plr); plr->PlayerTalkClass->SendCloseGossip(); OnGossipHello(plr, creature); break; case 2: //CURRENT EXCHANGE RATES message << "CURRENT EXCHANGE RATES (in gold coins)$BRavenholdt Manor : " << worth[0] << "$BBloodsail : " << worth[1] << "$BSteamwheedle Cartel : " << worth[2] << "$BTheramore Silk Traders : " << worth[3] << "$BEldoar'norore : " << worth[4]; creature->Whisper(message.str(), LANG_UNIVERSAL, plr); plr->PlayerTalkClass->SendCloseGossip(); OnGossipHello(plr, creature); break; case 3: //MANAGE PORTFOLIO MENU (5-10) plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Helmet_30:30:30:-18:0|tRavenholdt Manor", GOSSIP_SENDER_MAIN, 5); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Helmet_66:30:30:-18:0|tBloodsail", GOSSIP_SENDER_MAIN, 6); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Bomb_03:30:30:-18:0|tSteamwheedle Cartel", GOSSIP_SENDER_MAIN, 7); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Chest_Cloth_56:30:30:-18:0|tTheramore Silk Traders", GOSSIP_SENDER_MAIN, 8); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_FireDancer_01:30:30:-18:0|tEldoar'norore", GOSSIP_SENDER_MAIN, 9); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack", GOSSIP_SENDER_MAIN, 10); plr->SEND_GOSSIP_MENU(50002, creature->GetGUID()); break; case 4: //CLOSE GOSSIP plr->PlayerTalkClass->SendCloseGossip(); break; case 5: //MANAGE RAVENHOLDT STOCK stock = 1; plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tBuy shares", stock, 0, "How much gold would you like to invest?", 0, true); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tSell all shares", stock, 11); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", GOSSIP_SENDER_MAIN, 3); plr->SEND_GOSSIP_MENU(50002, creature->GetGUID()); return true; break; case 6: //MANAGE BLOODSAIL STOCK stock = 2; plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tBuy shares", stock, 0, "How much gold would you like to invest?", 0, true); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tSell all shares", stock, 11); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", GOSSIP_SENDER_MAIN, 3); plr->SEND_GOSSIP_MENU(50002, creature->GetGUID()); break; case 7: //MANAGE GOBLIN STOCK stock = 3; plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tBuy shares", stock, 0, "How much gold would you like to invest?", 0, true); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tSell all shares", stock, 11); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", GOSSIP_SENDER_MAIN, 3); plr->SEND_GOSSIP_MENU(50002, creature->GetGUID()); break; case 8: //MANAGE SILKTRADERS STOCK stock = 4; plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tBuy shares", stock, 0, "How much gold would you like to invest?", 0, true); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tSell all shares", stock, 11); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", GOSSIP_SENDER_MAIN, 3); plr->SEND_GOSSIP_MENU(50002, creature->GetGUID()); break; case 9: //MANAGE ELDOAR'NORORE STOCK stock = 5; plr->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Letter_03:30:30:-18:0|tBuy shares", stock, 0, "How much gold would you like to invest?", 0, true); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Coin_16:30:30:-18:0|tSell all shares", stock, 11); plr->ADD_GOSSIP_ITEM(GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", GOSSIP_SENDER_MAIN, 3); plr->SEND_GOSSIP_MENU(50002, creature->GetGUID()); return true; break; case 11: //TRANSACTION = SELL if (!SellStock(plr, stock, creature)) plr->GetSession()->SendNotification("Error while selling shares... Please contact the administrator."); else { } plr->PlayerTalkClass->SendCloseGossip(); OnGossipHello(plr, creature); break; case 12: //CREATE PLAYER LICENCE ( = ROW IN CHARACTER_STOCKDATA ) if (plr->GetMoney() >= 40*GOLDTOCOPPER) { if (CreateLicence(plr)) { amount = 40*GOLDTOCOPPER; plr->ModifyMoney(-amount); message << "Your licence has been acquired successfully, " << plr->GetName() << "!"; creature->Say(message.str(), LANG_UNIVERSAL, plr); } else plr->GetSession()->SendNotification("Error while registering your licence. Please contact the administrator."); } else { message << "I am afraid you do not have these 40 gold coins, " << plr->GetName() << "..."; creature->Say(message.str().c_str(), LANG_UNIVERSAL, plr); } plr->PlayerTalkClass->SendCloseGossip(); OnGossipHello(plr, creature); break; default: OnGossipHello(plr, creature); break; } return true; } bool OnGossipSelectCode(Player* plr, Creature* creature, uint32 stock, uint32 uiAction, const char* code) override { plr->PlayerTalkClass->ClearMenus(); std::ostringstream message; int32 amount = 0; std::string investment = code; static const char* allowedcharacters = "1234567890"; if (!investment.length() || investment.find_first_not_of(allowedcharacters) != std::string::npos) //If investment was specified incorrectly { plr->GetSession()->SendNotification("Your investment bust be declared using figures.(0123456789)"); } else //If investment was specified properly { //Statements uint32 investmentI = uint32(atol(code)); if (investmentI*GOLDTOCOPPER <= plr->GetMoney()) { QueryResult result1 = WorldDatabase.PQuery("SELECT `OwnedRatio`,`Worth` FROM `stocks` WHERE iD = %u", stock); if (!result1) return false; Field* field = result1->Fetch(); float ownedratio = field[0].GetFloat(); int worth = field[1].GetInt32(); QueryResult result2 = CharacterDatabase.PQuery("SELECT `%u` FROM `character_stockdata` WHERE iD = %u", stock, plr->GetGUIDLow()); if (!result2) return false; field = result2->Fetch(); float initialratio = field[0].GetFloat(); float ratio = static_cast<float>(investmentI) / worth; if (ratio <= (1-ownedratio)) //If enough stocks left { int amount = investmentI*GOLDTOCOPPER; plr->ModifyMoney(-amount); // substract it from player money CharacterDatabase.PExecute("UPDATE `character_stockdata` SET `%u` = %f WHERE `iD` = %u", stock, initialratio + ratio, plr->GetGUIDLow()); WorldDatabase.PExecute("UPDATE `stocks` SET `OwnedRatio` = %f WHERE `iD` = %u", ownedratio + ratio, stock); } else { plr->GetSession()->SendNotification("There are not so many shares for you to acquire, check the remaining shares before trying to invest."); } } else { message << "You do not have so much money, " << plr->GetName() << "."; creature->Say(message.str().c_str(), LANG_UNIVERSAL, plr); } } plr->PlayerTalkClass->SendCloseGossip(); OnGossipHello(plr, creature); return true; } private : bool SendMoneyByMail(ObjectGuid player, uint64 money, int32 stock) { MailSender sender(MAIL_CREATURE, 50038, MAIL_STATIONERY_DEFAULT); std::string stockName; switch (stock) { case 1: stockName = "Ravenholdt Manor"; break; case 2: stockName = "Bloodsail"; break; case 3: stockName = "Steamwheedle Cartel"; break; case 4: stockName = "Theramore Silk Traders"; break; case 5: stockName = "Eldoar'norore"; break; default: return false; } std::ostringstream text; text << "You have requested the General Exchange Hall to sell your shares of the " << stockName << " stock. This has yielded some " << money/GOLDTOCOPPER << " gold coins from which I have deducted " << money/10/GOLDTOCOPPER << " in the sake of various sale and accountancy costs. Enclosed to this letter are " << 9* money/10/GOLDTOCOPPER << " gold coins which come down to you."; MailDraft draft("Share Sale", text.str().c_str()); draft.AddMoney(money * 9 / 10); SQLTransaction letter = CharacterDatabase.BeginTransaction(); draft.SendMailTo(letter, MailReceiver(NULL, player.GetCounter()), sender); CharacterDatabase.CommitTransaction(letter); return true; } bool SellStock(Player* plr, int32 stock, Creature* creature) /*SELL ALL THE SHARES OF THE SPECIFIED STOCK */ { QueryResult result2 = WorldDatabase.PQuery("SELECT `OwnedRatio`,`Worth` FROM `stocks` WHERE iD = %u", stock); if (!result2) return false; QueryResult result1 = CharacterDatabase.PQuery("SELECT `1`, `2`, `3`, `4`, `5` FROM `character_stockdata` WHERE iD = %u", plr->GetGUIDLow()); if (!result1) return false; Field* field = result1->Fetch(); float ratio = field[stock - 1].GetFloat(); if (ratio == 0) { plr->GetSession()->SendNotification("You don't own any share of this stock..."); } else //ONLY IF PLAYER OWNS SOMETHING { Field* field = result2->Fetch(); float newownedratio = field[0].GetFloat()-ratio; int worth = field[1].GetInt32(); if (!SendMoneyByMail(plr->GetGUID(), worth*GOLDTOCOPPER*ratio, stock)) return false; std::ostringstream text; text << "I shall make the arragement for the sale, " << plr->GetName() << ", you will receive your money by mail soon enough."; creature->Say(text.str().c_str(), LANG_UNIVERSAL); CharacterDatabase.PExecute("UPDATE `character_stockdata` SET `%u` = 0 WHERE `iD` = %u", stock, plr->GetGUIDLow()); WorldDatabase.PExecute("UPDATE `stocks` SET `OwnedRatio` = %f WHERE `iD` = %u", newownedratio, stock); } return true; } bool CreateLicence(Player* plr) { SQLTransaction trans = CharacterDatabase.BeginTransaction(); trans->PAppend("REPLACE INTO `character_stockdata` (`iD`, `1`, `2`, `3`, `4`, `5`) VALUES (%u, 0, 0, 0, 0, 0)", plr->GetGUIDLow()); CharacterDatabase.CommitTransaction(trans); return true; } bool StockUpdate(int timeMult) { int32 stockWorth[STOCKNUMBER], newStockWorth[STOCKNUMBER]; int32 stockScale[STOCKNUMBER]; float stockOwnedRatio[STOCKNUMBER]; srand(time(NULL)); // Load stock values from DB for (int itr = 0; itr < STOCKNUMBER; itr++) { QueryResult result = WorldDatabase.PQuery("SELECT `Scale`,`OwnedRatio`,`Worth` FROM `stocks` WHERE iD = %u", itr + 1); if (!result) return false; Field* field = result->Fetch(); stockScale[itr] = field[0].GetInt32(); stockOwnedRatio[itr] = field[1].GetFloat(); stockWorth[itr] = field[2].GetInt32(); } // Dicethrows and Upload values to DB for (int itr = 0; itr < STOCKNUMBER; itr++) { char sign; if (stockWorth[itr] + stockScale[itr]*timeMult > 200000) //Don't go over 200.000 gold coins sign = -1; else if (stockWorth[itr] - stockScale[itr]*timeMult < 0) //Nor below 0 sign = 1; else { int dice = (rand() % 101) + floor(10 * stockOwnedRatio[itr]) - 55; sign = dice / abs(dice); } newStockWorth[itr] = stockWorth[itr] + sign*stockScale[itr]*timeMult; WorldDatabase.PExecute("UPDATE `stocks` SET `worth` = %u WHERE `iD` = %u", newStockWorth[itr], itr + 1); } return true; } }; } void AddSC_Stock() { new CS_StockExchange(); } I know this code ain't perfect but I'm still open for suggestions and critics. Have a nice day.
  2. If the lua interface file appeared only in BFA I really think it makes sense it was hardcoded before. Looking into it as well.
  3. Did you try what he said ? Like both forcing the check to true and outputting something in the error or debug log if the HasSpell (easiest one to check) condition is true ?
  4. Maybe give the player the spell but don't script anything linked with it. Like if you are giving that buff to race = RACE_TAUREN, just check for race == RACE_TAUREN instead of HasAura... Maybe that's kind of hackish though I don't know... And if you want this to be some spell you can learn, then, yeah, it also defeats the purpose.
  5. Could you also post your modification in spell.dbc ?
  6. Try HasAura instead of HasSpell EDIT: It is a passive and I never toyed with passives so I am not sure how they work... Well try it just in case. The only think I can promise you is that there is no interface limitation for xp rate : it's all server-side.
  7. Very first result I got on Google... Everything you need to know and download, including the LoginScreen tool that Mordred made, is provided here. Then, about Interface\Glues, have a look inside MPQ files.
  8. Yeah Nagrand is my favorite zone too ! Hope you can emulate or even improve its feel. Keep us updated and keep it up
  9. Roarl

    Noggit custom map

    Hey, I cannot recognize your Noggit version, never used that one. It shouldn't be the issue but here is a more recent release : Also, could you enclose your .adt and .wdt files so I can have a look at them ? On your screenshot, there seem to be a clickable area which I highlighted in yellow below. Did you try to click on it ? Regards.
  10. Hey, You could start by checking this out : Regards, Roarl // Update : So sorry, I misunderstood your request. I am afraid I cannot help. (If a moderator sees this, could you please delete my answer? It is kind of useless! Thanks <3)
  11. Version 1.0

    172 downloads

    I always disliked how the AuctionHouseBot handled selling and buying items randomly in the recent TrinityCore releases. It used the in-game npc seller price to determine both the sell and buy prices on the AH for the bot resulting in prices being to damn low (generally) or really too high (less often) and sold strange random items sometimes never sold by players. With this version of the AuctionHouseBot, the sell and buy prices are worked out from an external table in the world database called auction_prices which simply contains item ids and their price in copper. ONLY items belonging to this table can be sold or bought by the bot and their price in the table is used to determine both the sell price and the buy price. The contents of the auction_prices table has been generated using sampled AH data from several private servers and will be updated from time to time. General rules : The chance of an item being bought when the price is higher than the table price is a decaying exponential. The exponential decays later if no other instance of an item is sold. E.G. If you have a monopoly on [Copper Ore] then the bot knows that the price of [Copper Ore] is a bit higher than usual. The bot will NEVER buy an item if another instance of that item is sold for less. This is simply more realistic. An item sold by a player that can be bought from a npc using base money will never be bought by the bot even if it appears in auction_prices, to avoid exploit. They still can be sold by the bot, though. Of course, a code is never perfect. Should you have any suggestion for the bot behavior, problems to report or improvements to make in the code, feel free to leave a comment. Installation : Move the 6 .cpp files to your \src\server\game\AuctionHouseBot TrinityCore source directory and replace the existing ones Don't forget to commit them if you use git Recompile your core Apply the auction_prices.sql statement to your world database Apply the update.sql statement to your world database, if there is one. [Repeat step 5 only for each future update]
  12. Hello, I am currently working on a small housing system and am almost done with the bare bones but something is still refusing to work properly. I want to add a very small description of how the housing system works in a gossip menu, but as soon as SendGossipMenuFor it triggers the OnGossipHello instantly. I compared this part to the Transmogrifier script of Rochet2, which works fine, and it really looks the same so I do not get what I am doing wrong. More concretly, when I click "Why acquire a property ?" in the first menu, the relevant action (case 1 in OnGossipSelect in the code below) is executed (as expected) but then instantly the action made available in the next menu is also executed instantly (default case, with action = 3) : in the end the second menu never appears, instead OnGossipHello is executed again. Does anyone have any idea how to avoid this ? Thanks in advance for your time ! class npc_house_seller : public CreatureScript { public: npc_house_seller() : CreatureScript("npc_house_seller") { } class npc_house_sellerAI : public ScriptedAI { public: npc_house_sellerAI(Creature* creature) : ScriptedAI(creature) {} bool GossipHello(Player* player) override { return OnGossipHello(player, me); } static bool OnGossipHello(Player* player, Creature* creature) { WorldSession* session = player->GetSession(); AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Book_11:30:30:-18:0|tWhy acquire a property ?", 1, 0); std::stringstream warningMessage; warningMessage.str(std::string()); warningMessage << "Are you sure you want to proceed ? It will cost you " << HousingSystem::instance()->HouseFromSeller(creature->GetSpawnId())->_price / 10000 << " gold and strip you of any previous estate property."; AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Enchant_Disenchant:30:30:-18:0|tPurchase this estate", 2, 0, warningMessage.str().c_str(), 0, false); SendGossipMenuFor(player, 800001, creature->GetGUID()); return true; } bool GossipSelect(Player* player, uint32 /*menu_id*/, uint32 gossipListId) override { uint32 sender = player->PlayerTalkClass->GetGossipOptionSender(gossipListId); uint32 action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); return OnGossipSelect(player, me, sender, action); } static bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) { ClearGossipMenuFor(player); WorldSession* session = player->GetSession(); switch (sender) { case 1: { AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack..", 3, 0); SendGossipMenuFor(player, 800002, creature->GetGUID()); } break; case 2: if (player->HasEnoughMoney(HousingSystem::instance()->HouseFromSeller(creature->GetSpawnId())->_price)) { House* house = HousingSystem::instance()->HouseFromPlayer(player->GetGUID().GetCounter()); if (house != nullptr) { house->_owner = 0; // Respawn NPC Map* map = sMapMgr->FindBaseNonInstanceMap(house->_map_id); Position pos(house->_coord_x, house->_coord_y, house->_coord_z, house->_orientation); Creature* cbuffer = new Creature(); if (!cbuffer->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_ANYWHERE, 50006, pos)) delete cbuffer; cbuffer->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), PHASEMASK_ANYWHERE); ObjectGuid::LowType db_guid = cbuffer->GetSpawnId(); // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells() // current "creature" variable is deleted and created fresh new, otherwise old values might trigger asserts or cause undefined behavior cbuffer->CleanupsBeforeDelete(); delete cbuffer; cbuffer = new Creature(); if (!cbuffer->LoadFromDB(db_guid, map, true, true)) delete cbuffer; sObjectMgr->AddCreatureToGrid(db_guid, sObjectMgr->GetCreatureData(db_guid)); house->_seller = db_guid; HousingSystem::instance()->AdaptSellerMapOnHouseChange(db_guid, house); house->SaveToDB(); } house = HousingSystem::instance()->HouseFromSeller(creature->GetSpawnId()); house->_owner = player->GetGUID().GetCounter(); house->_seller = 0; house->SaveToDB(); HousingSystem::instance()->AdaptPlayerMapOnHouseChange(house->_owner, house); // Remove Creature ObjectGuid::LowType guid = creature->GetGUID().GetCounter(); creature->CombatStop(); creature->DeleteFromDB(); creature->AddObjectToRemoveList(); player->ModifyMoney(0-HousingSystem::instance()->HouseFromSeller(creature->GetSpawnId())->_price, false); std::stringstream yay; yay.str(std::string()); yay << "Congratulations ! You are now the proud owner of this estate ! "; WorldPacket data(SMSG_MOTD); Tokenizer motdTokens(yay.str(), '@'); data << uint32(motdTokens.size()); for (Tokenizer::const_reference token : motdTokens) data << token; player->GetSession()->SendPacket(&data); player->AddItem(60073,1); player->PlayerTalkClass->SendCloseGossip(); } else { std::stringstream nope; nope.str(std::string()); nope << "You cannot afford that property ! You need at least " << HousingSystem::instance()->HouseFromSeller(creature->GetSpawnId())->_price/10000 << " gold."; session->SendNotification(nope.str().c_str()); player->PlayerTalkClass->SendCloseGossip(); } break; default: { std::stringstream nope; nope.str(std::string()); nope << "Triggered " << sender << " !"; session->SendNotification(nope.str().c_str()); } return OnGossipHello(player, creature); } return true; } }; CreatureAI* GetAI(Creature *creature) const override { return new npc_house_sellerAI(creature); } };
  13. Hi, thanks for your answer. I get your point and indeed, adding a second GossipItem in the menu before sending it works. But do you know why it is that way ? And do you have any idea why it works for Rochet2's script and not for mine ? For instance, in the code below at case case EQUIPMENT_SLOT_END + 9: the menu is working as it should. static bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) { ClearGossipMenuFor(player); WorldSession* session = player->GetSession(); switch (sender) { case EQUIPMENT_SLOT_END: // Show items you can use ShowTransmogItems(player, creature, action); break; case EQUIPMENT_SLOT_END + 1: // Main menu OnGossipHello(player, creature); break; case EQUIPMENT_SLOT_END + 2: // Remove Transmogrifications { bool removed = false; for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) { if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) { if (!newItem->transmog) continue; newItem->transmog = 0; newItem->SetState(ITEM_CHANGED, player); sTransmogrification->UpdateItem(player, newItem); removed = true; } } if (removed) session->SendAreaTriggerMessage("%s", GTS(LANG_ERR_UNTRANSMOG_OK)); else session->SendNotification(LANG_ERR_UNTRANSMOG_NO_TRANSMOGS); OnGossipHello(player, creature); } break; case EQUIPMENT_SLOT_END + 3: // Remove Transmogrification from single item { if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action)) { if (newItem->transmog) { newItem->transmog = 0; newItem->SetState(ITEM_CHANGED, player); sTransmogrification->UpdateItem(player, newItem); session->SendAreaTriggerMessage("%s", GTS(LANG_ERR_UNTRANSMOG_OK)); } else session->SendNotification(LANG_ERR_UNTRANSMOG_NO_TRANSMOGS); } OnGossipSelect(player, creature, EQUIPMENT_SLOT_END, action); } break; #ifdef PRESETS case EQUIPMENT_SLOT_END + 4: // Presets menu { if (!sTransmogrification->EnableSets) { OnGossipHello(player, creature); return true; } if (sTransmogrification->EnableSetInfo) AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Book_11:30:30:-18:0|tHow sets work", EQUIPMENT_SLOT_END + 10, 0); if (!player->presetMap.empty()) { for (PresetMapType::const_iterator it = player->presetMap.begin(); it != player->presetMap.end(); ++it) AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Statue_02:30:30:-18:0|t" + it->second.name, EQUIPMENT_SLOT_END + 6, it->first); if (player->presetMap.size() < sTransmogrification->MaxSets) AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/GuildBankFrame/UI-GuildBankFrame-NewTab:30:30:-18:0|tSave set", EQUIPMENT_SLOT_END + 8, 0); } else AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/GuildBankFrame/UI-GuildBankFrame-NewTab:30:30:-18:0|tSave set", EQUIPMENT_SLOT_END + 8, 0); AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack..", EQUIPMENT_SLOT_END + 1, 0); SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); } break; case EQUIPMENT_SLOT_END + 5: // Use preset { if (!sTransmogrification->EnableSets) { OnGossipHello(player, creature); return true; } // action = presetID PresetMapType::const_iterator it = player->presetMap.find(action); if (it != player->presetMap.end()) { for (PresetslotMapType::const_iterator it2 = it->second.slotMap.begin(); it2 != it->second.slotMap.end(); ++it2) if (Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, it2->first)) sTransmogrification->PresetTransmog(player, item, it2->second, it2->first); } OnGossipSelect(player, creature, EQUIPMENT_SLOT_END + 6, action); } break; case EQUIPMENT_SLOT_END + 6: // view preset { if (!sTransmogrification->EnableSets) { OnGossipHello(player, creature); return true; } // action = presetID PresetMapType::const_iterator it = player->presetMap.find(action); if (it == player->presetMap.end()) { OnGossipSelect(player, creature, EQUIPMENT_SLOT_END + 4, 0); return true; } for (PresetslotMapType::const_iterator it2 = it->second.slotMap.begin(); it2 != it->second.slotMap.end(); ++it2) AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sTransmogrification->GetItemIcon(it2->second, 30, 30, -18, 0) + sTransmogrification->GetItemLink(it2->second, session), sender, action); AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/INV_Misc_Statue_02:30:30:-18:0|tUse set", EQUIPMENT_SLOT_END + 5, action, "Using this set for transmogrify will bind transmogrified items to you and make them non-refundable and non-tradeable.\nDo you wish to continue?\n\n" + it->second.name, 0, false); AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/PaperDollInfoFrame/UI-GearManager-LeaveItem-Opaque:30:30:-18:0|tDelete set", EQUIPMENT_SLOT_END + 7, action, "Are you sure you want to delete " + it->second.name + "?", 0, false); AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack..", EQUIPMENT_SLOT_END + 4, 0); SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); } break; case EQUIPMENT_SLOT_END + 7: // Delete preset { if (!sTransmogrification->EnableSets) { OnGossipHello(player, creature); return true; } // action = presetID auto it = player->presetMap.find(action); if (it != player->presetMap.end()) { CharacterDatabase.PExecute("DELETE FROM `custom_transmogrification_sets` WHERE `Owner` = %u AND `PresetID` = %u", player->GetGUID().GetCounter(), uint32(action)); player->presetMap.erase(it); } OnGossipSelect(player, creature, EQUIPMENT_SLOT_END + 4, 0); } break; case EQUIPMENT_SLOT_END + 8: // Save preset { if (!sTransmogrification->EnableSets) { OnGossipHello(player, creature); return true; } if (player->presetMap.size() >= sTransmogrification->MaxSets) { OnGossipHello(player, creature); return true; } uint32 cost = 0; bool canSave = false; for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) { if (!sTransmogrification->GetSlotName(slot, session)) continue; if (Item* newItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) { uint32 entry = newItem->transmog; if (!entry) continue; const ItemTemplate* temp = sObjectMgr->GetItemTemplate(entry); if (!temp) continue; if (!sTransmogrification->SuitableForTransmogrification(player, temp)) // no need to check? continue; cost += sTransmogrification->GetSpecialPrice(temp); canSave = true; AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, sTransmogrification->GetItemIcon(entry, 30, 30, -18, 0) + sTransmogrification->GetItemLink(entry, session), EQUIPMENT_SLOT_END + 8, 0); } } if (canSave) AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/GuildBankFrame/UI-GuildBankFrame-NewTab:30:30:-18:0|tSave set", 0, 0, "Insert set name", cost*sTransmogrification->SetCostModifier + sTransmogrification->SetCopperCost, true); AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/PaperDollInfoFrame/UI-GearManager-Undo:30:30:-18:0|tUpdate menu", sender, action); AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack..", EQUIPMENT_SLOT_END + 4, 0); SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); } break; case EQUIPMENT_SLOT_END + 10: // Set info { AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack..", EQUIPMENT_SLOT_END + 4, 0); SendGossipMenuFor(player, sTransmogrification->SetNpcText, creature->GetGUID()); } break; #endif case EQUIPMENT_SLOT_END + 9: // Transmog info { AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack..", EQUIPMENT_SLOT_END + 1, 0); SendGossipMenuFor(player, sTransmogrification->TransmogNpcText, creature->GetGUID()); } break; default: // Transmogrify { if (!sender && !action) { OnGossipHello(player, creature); return true; } // sender = slot, action = display TransmogTrinityStrings res = sTransmogrification->Transmogrify(player, ObjectGuid(HighGuid::Item, 0, action), sender); if (res == LANG_ERR_TRANSMOG_OK) session->SendAreaTriggerMessage("%s", GTS(LANG_ERR_TRANSMOG_OK)); else session->SendNotification(res); // OnGossipSelect(player, EQUIPMENT_SLOT_END, sender); new Timed(player, creature, EQUIPMENT_SLOT_END, sender); } break; } return true; }
  14. What version of the game are you using ? What emulator are you using ? ( I am going to guess 3.3.5 and TrinityCore from now on ) In any case, you should start by getting used to TrinityCore code and understanding how they implemented battlegrounds in the first place. When you are done coding the battleground itself, you can either replace one of the existing battlegrounds with it, or create a custom NPC to queue for it, or add it in BattlemasterList.dbc I guess. There might be additionnal .dbc work involved, I am not sure.
  15. Not sure I am gonna use this anytime soon but this is amazing... No idea why I didn't stumble on it earlier ! Good work both, and very nice and exhaustive tutorial !
  16. Amazing work, keep it up !
    No comments no rating for this? Always loved your work, Alastor, these models are awesome. I've stopped modding as well but I think this is a really nice release. They look even better ingame. Cheers
  17. Oh, nothing serious. I think I spotted some typos in the F.A.Q. and in the description. Just corrected them.... Also added a quote of Amaroth in the F.A.Q so that the information given is relevant. I can PM you when I release the new models if you wanna.
  18. Hi dude, You need to provide a password in the Session Manager window of HeidiSQL (seen @ 0:44). You should be able to find it in the config files of your server (which I guess are located in the configs directory of Arcemu in your case). Once you get there look for something like LoginDatabaseInfo = "127.0.0.1;3306;root;ROOT;auth" WorldDatabaseInfo = "127.0.0.1;3306;root;ROOT;world" CharacterDatabaseInfo = "127.0.0.1;3306;root;ROOT;characters"In this example, root is the user and ROOT is the password. These lines are from TrinityCore but it should not be so different from Arcemu. Cheers
  19. Basic platform animationsSo, you are building a platform game and you want to spice things up a trifle? Or maybe you just want an elevator for your custom map? You just hit the right tutorial then! A. RequirementsDBCUtil ( or any DBC<->CSV tool) (I think MyDBCEditor sucks here because it uses hexa for some columns which tends to complicate things a lot)Ladik's MPQ EditorA TrinityCore serverA kebab skewerB. Table of ContentsCreating the GameObjectGetting TransportAnimation.dbcEditing TransportAnimation.dbc.csvBPAG releaseC. Tutorial1.Creating the GameObjectOpen your favorite SQL Editor and head to the gameobject_template table. Once you get there create an entry for your custom gameobject, set the type as 11 (GAMEOBJECT_TYPE_TRANSPORT)and use whatever displayID you want to use. SET THE FLAGS TO 40 (without that, your character will not move with the platform) Note down its Guid. N.B.I Modifying the size will NOT have any effect as animated gameobjects always have their standard size in-game ( that is to say 1 ). 2.Getting TransportAnimation.dbcIf you built TrinityCore in the "C:\Build" directory for instance, you should find the dbc files in "C:\Build\bin\Release\dbc". Copy TransportAnimation.dbc and paste it where you are going to use DBCUtil. 3.Editing TransportAnimation.dbc.csvAfter you have converted TransportAnimation.dbc to csv (simple drag and drop on DBCUtil will do), let us open it with your favorite text / csv editor. The columns of the TransportAnimation are 1 ID Integer 2 TransportID Integer 3 TimeIndex Integer 4 PosX Float 5 PosY Float 6 PosZ Float 7 iRefID_SequenceID Integer (from http://pxr.dk/wowdev/wiki/). Foremost, once the server notified your client that it is encountering a transport gameobject(GAMEOBJECT_TYPE_TRANSPORT), your client plays the transport animation which he reads from TransportAnimation.dbc. He will find the animation in the dbc using the Guid of the gameobject (TransportID). The animation is then sequenced through time using the TimeIndex which is expressed in milliseconds. The 4th, 5th and 6th columns express the relative displacement of the gameobject along the Y, X and Z axis respectively (careful here, the wiki states that it rather is X, Y, Z but it's actually not the case). Eventually, the last column is the animation played by the gameobject. It is NOT compulsory but when the model you are using as moving platform has animations, why not take advantage of it? Here an example : 179710, 301034, 0, 0, 0, 0, 148, 179711, 301034, 0, 0, 0, 0, 148, 179712, 301034, 3000, -29.1, -1.6, 0, 148, 179713, 301034, 3000, -29.1, -1.6, 0, 149, 179714, 301034, 3030, -29.1, -1.6, 0, 146, 179715, 301034, 6030, 0, 0, 0, 146,Once you have done all this, convert the csv file back to dbc and put it in the dbc folder of TrinityCore AS WELL AS in your custom patch using the MPQ Editor. 4.BPAG releaseAs you create animations, you will soon find out writing these rows is a tedious work. I have therefore written a tiny console tool which will write them for you (christened BPAG, an acronym for Basic Platform Animation Generator (the name is pretty cool but this piece of software is total bullshit )). You can download it here. You will need to feed the BPAG with several details, let me explain them to you. First ID : the ID (first column) of the last row of the TransportAnimation.dbc file PLUS one. gobGUID : the Guid of the gameobject you are using Start position : Coordinates of the starting point of your platform, X, Y, Z in order (E.G. 123 451 154) End Position : Coordinates of the return point of your platform, X, Y, Z in order (E.G. 123 451 154) Travel Time (ms) : The time that the platform will take to travel from the the starting point to the return point (in milliseconds). Pause Time (ms) : If you want your platform to stop at the starting position and at the return position (useful for elevators) (in milliseconds) Is it a two-speed platform ? (y/n) : Answer by y/Y (yes) or n/N (no). Two-speed platforms will speed up after some time interval. Speed ratio (if it is a two-speed platform) : If you want the platform to go 5 times faster after the previously mentioned time interval, input 5. Speed change time (ms) (if it is a two-speed platform) : Is the previously mentioned time interval in milliseconds Once you have provided the BPAG with all this, you will find the generated animation in output.txt (same directory as the .exe). You only have to paste that at the end of TransportAnimation.dbc.csv then convert it back to dbc.
  20. Roarl

    Moving Platforms

    Thank you! Yes I surely will update both the tool and the tutorial at some point (E.G. it would be great if the tool could load the last ID from the dbc and append the animation directly to it )
  21. Hi there! How do you access the (player) user of a gameobject? For instance, let us consider this simple gameobject script : class go_chairofjudgmentgood : public GameObjectScript { public: go_chairofjudgmentgood() : GameObjectScript("go_chairofjudgmentgood") { } struct go_chairofjudgmentgoodAI : public GameObjectAI { uint32 goTimer; go_chairofjudgmentgoodAI(GameObject* go) : GameObjectAI(go) {} void Reset() { goTimer = 7000; } void UpdateAI(uint32 diff) override { GameObjectAI::UpdateAI(diff); if (goTimer <= diff) { //HERE } else goTimer -= diff; } }; GameObjectAI * GetAI(GameObject * go) const { return new go_chairofjudgmentgoodAI(go); } };Let us assume the object type of the chairofjudgmentgood is indeed a chair. How could I get the Player (class) of the player sitting on the chair at line 23 (//HERE)? Thanks in advance for your help
  22. Haha I guess I made that mistake as well, and more than once. x) It's funny how it's always the small mistakes we fail to see!