eluna-scripts-ornfelt/lua/WowEmulationScriptPack/C++/npc_gambler.cpp
2024-09-23 07:59:23 +02:00

561 lines
23 KiB
C++

/*
# Gambler NPC
_This module was created for [StygianCore](https://rebrand.ly/stygiancoreproject). A World of Warcraft 3.3.5a Solo/LAN repack by StygianTheBest | [GitHub](https://rebrand.ly/stygiangithub) | [Website](https://rebrand.ly/stygianthebest))_
### Description
------------------------------------------------------------------------------------------------------------------
Hey, the name's Skinny. You feelin' lucky? This NPC will allow players to gamble copper, silver, or gold as
defined in the configuration. It's a nice way for players to make a little extra coin.
- Config:
- Set bet and jackpot amounts
- Enable/Disable coin type (gold, silver, copper)
- Set coin type to gamble
- A roll of 50 or higher wins double the bet!
- A roll of less than 50 loses double the bet!
- A roll of 100 within the first 10 rolls hits the jackpot!
- The jackpot can only be hit in the first 10 rolls of each session to discourage spam.
- A little help is given to players losing 5 rolls in a row.
### To-Do
------------------------------------------------------------------------------------------------------------------
- Track and display jackpot winners/dates.
- Create lottery from player losses.
### Data
------------------------------------------------------------------------------------------------------------------
- Type: NPC
- Script: gamble_npc
- Config: Yes
- SQL: Yes
- NPC ID: 601020
### Version
------------------------------------------------------------------------------------------------------------------
- v2019.04.17 - Fix display of win/lose amount for values 100 or above, applicable to copper/silver/gold
- v2019.04.15 - Ported to AzerothCore by gtao725 (https://github.com/gtao725/)
- v2019.02.13 - Redesign, Add Coin Type Options, Update AI
- v2018.12.09 - Update config
- v2017.08.10 - Release
### CREDITS
------------------------------------------------------------------------------------------------------------------
![Styx](https://stygianthebest.github.io/assets/img/avatar/avatar-128.jpg "Styx")
![StygianCore](https://stygianthebest.github.io/assets/img/projects/stygiancore/StygianCore.png "StygianCore")
##### This module was created for [StygianCore](https://rebrand.ly/stygiancoreproject). A World of Warcraft 3.3.5a Solo/LAN repack by StygianTheBest | [GitHub](https://rebrand.ly/stygiangithub) | [Website](https://rebrand.ly/stygianthebest))
#### Additional Credits
- [Blizzard Entertainment](http://blizzard.com)
- [TrinityCore](https://github.com/TrinityCore/TrinityCore/blob/3.3.5/THANKS)
- [SunwellCore](http://www.azerothcore.org/pages/sunwell.pl/)
- [AzerothCore](https://github.com/AzerothCore/azerothcore-wotlk/graphs/contributors)
- [OregonCore](https://wiki.oregon-core.net/)
- [Wowhead.com](http://wowhead.com)
- [OwnedCore](http://ownedcore.com/)
- [ModCraft.io](http://modcraft.io/)
- [MMO Society](https://www.mmo-society.com/)
- [AoWoW](https://wotlk.evowow.com/)
- [More credits are cited in the sources](https://github.com/StygianTheBest)
### LICENSE
------------------------------------------------------------------------------------------------------------------
This code and content is released under the [GNU AGPL v3](https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3).
*/
#include "Define.h"
#include "ScriptMgr.h"
#include "SharedDefines.h"
#include "Unit.h"
#include "Pet.h"
#include "Player.h"
#include "ScriptPCH.h"
#include "ScriptedGossip.h"
#include "GossipDef.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Chat.h"
#include "Config.h"
bool GamblerNPCAnnounce = true;
// Module Configuration Values
uint32 Bet1 = 1;
uint32 Bet2 = 2;
uint32 Bet3 = 3;
uint32 Bet4 = 4;
uint32 Bet5 = 5;
uint32 Jackpot = 50;
uint32 MoneyType = 3;
uint32 EnableGold = 1;
uint32 EnableSilver = 1;
uint32 EnableCopper = 1;
uint32 GamblerEmoteSpell;
uint32 GamblerMessageTimer;
class GamblerConfig : public WorldScript
{
public:
GamblerConfig() : WorldScript("GamblerConfig") { }
void OnBeforeConfigLoad(bool reload)
{
if (!reload) {
// Load Configuration Settings
SetInitialWorldSettings();
}
}
// Load Configuration Settings
void SetInitialWorldSettings()
{
// Get the bet amounts from config
GamblerNPCAnnounce = sConfigMgr->GetBoolDefault("Gambler.Announce", true);
Bet1 = sConfigMgr->GetIntDefault("Gambler.Amount1", 1);
Bet2 = sConfigMgr->GetIntDefault("Gambler.Amount2", 2);
Bet3 = sConfigMgr->GetIntDefault("Gambler.Amount3", 3);
Bet4 = sConfigMgr->GetIntDefault("Gambler.Amount4", 4);
Bet5 = sConfigMgr->GetIntDefault("Gambler.Amount5", 5);
Jackpot = sConfigMgr->GetIntDefault("Gambler.Jackpot", 50);
MoneyType = sConfigMgr->GetIntDefault("Gambler.MoneyType", 3);
EnableGold = sConfigMgr->GetIntDefault("Gambler.EnableGold", 1);
EnableSilver = sConfigMgr->GetIntDefault("Gambler.EnableSilver", 1);
EnableCopper = sConfigMgr->GetIntDefault("Gambler.EnableCopper", 1);
GamblerEmoteSpell = sConfigMgr->GetIntDefault("Gambler.MessageTimer", 44940);
GamblerMessageTimer = sConfigMgr->GetIntDefault("Gambler.MessageTimer", 60000);
// Enforce Min/Max Time
if (GamblerMessageTimer != 0)
{
if (GamblerMessageTimer < 60000 || GamblerMessageTimer > 300000)
{
GamblerMessageTimer = 60000;
}
}
}
};
class GamblerAnnounce : public PlayerScript
{
public:
GamblerAnnounce() : PlayerScript("GamblerAnnounce") {}
void OnLogin(Player* player, bool firstLogin)
{
// Announce Module
if (firstLogin)
{
/*if (GamblerNPCAnnounce)
{
ChatHandler(player->GetSession()).SendSysMessage("This server is running the |cff4CFF00GamblerNPC |rmodule.");
}*/
}
}
};
class gamble_npc : public CreatureScript
{
public:
gamble_npc() : CreatureScript("gamble_npc") { }
// Passive Emotes
struct NPC_PassiveAI : public ScriptedAI
{
NPC_PassiveAI(Creature* creature) : ScriptedAI(creature) { }
// Money
uint32 Pocket = 0;
uint32 BetAmount = 0;
uint32 WinAmount = 0;
uint32 PlayerMoney = 0;
uint32 JackpotAmount = 0;
std::string MoneyTypeText = "Electrum";
// Bets
uint32 Bets = 0; // # of bets placed
uint32 Wins = 0; // # of wins
uint32 Losses = 0; // # of losses
// Calculate Gamble Amounts
int CalcMoney(int Copper, int bet)
{
if (MoneyType == 3)
{
MoneyTypeText = "Gold"; // Gamble Gold
PlayerMoney = Copper / 10000; // Player Gold
BetAmount = ((bet * 10000) * 2); // Bet value in Gold
WinAmount = BetAmount / 10000; // Player Wins Gold
JackpotAmount = Jackpot * 10000; // Jackpot value in Copper
}
else if (MoneyType == 2)
{
MoneyTypeText = "Silver"; // Gamble Silver
PlayerMoney = Copper / 100; // Player Silver
BetAmount = ((bet * 100) * 2); // Bet value in Silver
WinAmount = BetAmount / 100; // Player Wins Silver
JackpotAmount = Jackpot * 100; // Jackpot value in Copper
}
else
{
MoneyTypeText = "Copper"; // Gamble Copper
PlayerMoney = Copper; // Player Copper
BetAmount = bet * 2; // Bet value in Copper
WinAmount = BetAmount; // Player Wins Copper
JackpotAmount = Jackpot * 1; // Jackpot value in Copper
}
return Copper;
}
// Gossip Hello
bool OnGossipHello(Player* player)
{
std::ostringstream messageCoinType;
std::ostringstream messagePocket;
std::ostringstream messageJackpot;
ClearGossipMenuFor(player);
// Calculate player money and bet values
Pocket = CalcMoney(player->GetMoney(), 0);
// For the high-rollers
if (Pocket >= 50000000 && Bets == 0) // If they have 5000+ Gold
{
std::ostringstream messageTaunt;
messageTaunt << "Whadda we have here? A high-roller eh? Step right up " << player->GetName() << "!";
player->GetSession()->SendNotification("%s", messageTaunt.str().c_str());
}
// Reset # of bets placed
Bets = 0;
// Clean up the display if using Copper or Silver
if (Pocket >= 100000 && (MoneyType == 1 || MoneyType == 2)) {
messagePocket << "Hi " << player->GetName() << ". I see you have PLENTY of " << MoneyTypeText << " to gamble.";
}
else if (Pocket >= 10000000 && MoneyType == 3) {
messagePocket << "Hi " << player->GetName() << ". I see you have PLENTY of " << MoneyTypeText << " to gamble.";
}
else {
messagePocket << "Hi " << player->GetName() << ". I see you've got " << PlayerMoney << " " << MoneyTypeText << " to gamble.";
}
// Main Menu
messageJackpot << "Place your bet. Today's Jackpot is " << Jackpot << " " << MoneyTypeText << ".";
messageCoinType << "Coin Type: " << MoneyTypeText;
AddGossipItemFor(player, GOSSIP_ICON_CHAT, messagePocket.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14);
AddGossipItemFor(player, GOSSIP_ICON_TALK, "So, how does this game work?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, messageCoinType.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, messageJackpot.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, 601020, me);
return true;
}
// Gossip Select
bool OnGossipSelect(Player* player, uint32 menuId, uint32 gossipListId)
{
uint32 const sender = player->PlayerTalkClass->GetGossipOptionSender(gossipListId);
uint32 const uiAction = player->PlayerTalkClass->GetGossipOptionAction(gossipListId);
// Strings
std::ostringstream Option1;
std::ostringstream Option2;
std::ostringstream Option3;
std::ostringstream Option4;
std::ostringstream Option5;
std::ostringstream CoinCopper;
std::ostringstream CoinSilver;
std::ostringstream CoinGold;
std::ostringstream messageCoins;
std::ostringstream messageInstruct;
// Initialize
ClearGossipMenuFor(player);
if (sender != GOSSIP_SENDER_MAIN)
{
return false;
}
// Main Menu
switch (uiAction)
{
// Gamble Menu
case GOSSIP_ACTION_INFO_DEF + 1:
Option1 << Bet1 << " " << MoneyTypeText;
Option2 << Bet2 << " " << MoneyTypeText;
Option3 << Bet3 << " " << MoneyTypeText;
Option4 << Bet4 << " " << MoneyTypeText;
Option5 << Bet5 << " " << MoneyTypeText;
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, Option1.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, Option2.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, Option3.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, Option4.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, Option5.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7);
AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, " Back", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14);
SendGossipMenuFor(player, 1, me);
break;
// Rules Menu
case GOSSIP_ACTION_INFO_DEF + 2:
messageInstruct << "The rules are simple " << player->GetName() << ".. If you roll higher than 50, you win double the bet amount. Otherwise, you lose twice the bet amount. A roll of 100 wins the jackpot. Good Luck!";
AddGossipItemFor(player, GOSSIP_ICON_CHAT, messageInstruct.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14);
AddGossipItemFor(player, GOSSIP_ICON_TALK, "Alright Skinny, I'm up for some gambling.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, 1, me);
break;
// Bet 1
case GOSSIP_ACTION_INFO_DEF + 3:
OnGossipSelectMoney(player, me, 1, 1, Bet1);
break;
// Bet 2
case GOSSIP_ACTION_INFO_DEF + 4:
OnGossipSelectMoney(player, me, 1, 1, Bet2);
break;
// Bet 3
case GOSSIP_ACTION_INFO_DEF + 5:
OnGossipSelectMoney(player, me, 1, 1, Bet3);
break;
// Bet 4
case GOSSIP_ACTION_INFO_DEF + 6:
OnGossipSelectMoney(player, me, 1, 1, Bet4);
break;
// Bet 5
case GOSSIP_ACTION_INFO_DEF + 7:
OnGossipSelectMoney(player, me, 1, 1, Bet5);
break;
// MoneyType Menu
case GOSSIP_ACTION_INFO_DEF + 8:
CoinCopper << "Copper";
CoinSilver << "Silver";
CoinGold << "Gold";
messageCoins << "Whatta ya gamblin' with " << player->GetName() << "?";
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, messageCoins.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8);
if (EnableCopper)
{
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, CoinCopper.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10);
}
if (EnableSilver)
{
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, CoinSilver.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11);
}
if (EnableGold)
{
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, CoinGold.str(), GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12);
}
SendGossipMenuFor(player, 601020, me);
break;
// MoneyType: Copper
case GOSSIP_ACTION_INFO_DEF + 10:
MoneyType = 1;
ClearGossipMenuFor(player);
OnGossipHello(player);
break;
// MoneyType: Silver
case GOSSIP_ACTION_INFO_DEF + 11:
MoneyType = 2;
ClearGossipMenuFor(player);
OnGossipHello(player);
break;
// MoneyTaype: Gold
case GOSSIP_ACTION_INFO_DEF + 12:
MoneyType = 3;
ClearGossipMenuFor(player);
OnGossipHello(player);
break;
// Main Menu
case GOSSIP_ACTION_INFO_DEF + 14:
ClearGossipMenuFor(player);
OnGossipHello(player);
break;
}
return true;
}
// Gossip Select Gold
bool OnGossipSelectMoney(Player* player, Creature* creature, uint32 /* sender */, uint32 /* uiAction */, uint32 bet)
{
ClearGossipMenuFor(player);
// Dice Roll
uint32 Roll = 0;
// Generate a "random" number
Roll = urand(1, 100);
Bets = Bets + 1;
// The house always wins (discourage spamming for the jackpot)
if (Bets >= 10 && Roll == 100)
{
// If they have bet 10 times this session, decrement their roll
// by 1 to prevent a roll of 100 and hitting the jackpot.
Roll = Roll - 1;
}
// Calculate player money and bet values
Pocket = CalcMoney(player->GetMoney(), bet);
// Losing Streak? Aww.. how about some help.
// After 5 losses in a row, add +25 to their next roll.
if (Losses >= 5 && Roll < 50)
{
std::ostringstream messageHelp;
messageHelp << "Lady luck isn't on your side tonight " << player->GetName() << ".";
creature->Whisper(messageHelp.str().c_str(), LANG_UNIVERSAL, player);
Roll = Roll + 25;
Losses = 0;
}
// For the cheapskates
if (Pocket < (BetAmount / 2))
{
std::ostringstream messageTaunt;
messageTaunt << "Hey, I got no time for cheapskates " << player->GetName() << ". Come back when you have " << bet << " " << MoneyTypeText << "!";
player->AddAura(228, player); // Polymorph Chicken
player->AddAura(5782, player); // Fear
creature->Whisper(messageTaunt.str().c_str(), LANG_UNIVERSAL, player);
CloseGossipMenuFor(player);
player->PlayDirectSound(5960); // Goblin Pissed
creature->HandleEmoteCommand(EMOTE_ONESHOT_RUDE);
return false;
}
// Hittin' the jackpot!
if (Roll == 100)
{
std::ostringstream messageAction;
std::ostringstream messageNotice;
player->ModifyMoney(Jackpot * 10000);
player->PlayDirectSound(3337);
player->CastSpell(player, 47292);
player->CastSpell(player, 44940);
messageAction << "The bones come to rest with a total roll of " << Roll << ".";
messageNotice << "Wowzers " << player->GetName() << "! You hit the jackpot and win " << Jackpot << " gold!";
creature->Whisper(messageAction.str().c_str(), LANG_UNIVERSAL, player);
player->GetSession()->SendNotification(messageNotice.str().c_str());
CloseGossipMenuFor(player);
creature->HandleEmoteCommand(EMOTE_ONESHOT_APPLAUD);
return true;
}
// Why does it happen? Because it happens.. Roll the bones.. Roll the bones!
if (Roll >= 50)
{
std::ostringstream messageAction;
std::ostringstream messageNotice;
player->ModifyMoney(BetAmount);
Wins = Wins + 1;
Losses = 0;
player->CastSpell(player, 47292);
messageAction << "The bones come to rest with a total roll of " << Roll << ".";
messageNotice << "Congratulations " << player->GetName() << ", You've won " << WinAmount << " " << MoneyTypeText << "!";
creature->Whisper(messageAction.str().c_str(), LANG_UNIVERSAL, player);
ChatHandler(player->GetSession()).SendSysMessage(messageNotice.str().c_str());
creature->HandleEmoteCommand(EMOTE_ONESHOT_APPLAUD);
}
else
{
std::ostringstream messageAction;
std::ostringstream messageNotice;
player->ModifyMoney(-BetAmount);
Losses = Losses + 1;
messageAction << "The bones come to rest with a total roll of " << Roll << ".";
messageNotice << "Tough luck " << player->GetName() << ", you've lost " << WinAmount << " " << MoneyTypeText << "!";
creature->Whisper(messageAction.str().c_str(), LANG_UNIVERSAL, player);
ChatHandler(player->GetSession()).SendSysMessage(messageNotice.str().c_str());
creature->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION);
}
OnGossipSelect(player, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
return true;
}
uint32 Choice;
uint32 MessageTimer;
// Called once when client is loaded
void Reset()
{
// GamblerMessageTimer Hardcoded - Does Not Use Config Value
MessageTimer = urand(60000, 180000); // 1-3 minutes
}
// Called at World update tick
void UpdateAI(const uint32 diff)
{
if (MessageTimer <= diff)
{
// If Enabled
if (GamblerMessageTimer != 0)
{
// Make a random message choice
Choice = urand(1, 3);
switch (Choice)
{
case 1:
{
me->Say("Come one, come all! Step right up to Skinny's! Place your bets, Place your bets!", LANG_UNIVERSAL, NULL);
me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION);
if (GamblerEmoteSpell != 0)
{
me->CastSpell(me, 44940);
}
MessageTimer = urand(60000, 180000);
break;
}
case 2:
{
me->Say("Come on! Place your bets, Don't be a chicken!", LANG_UNIVERSAL, NULL);
me->HandleEmoteCommand(EMOTE_ONESHOT_CHICKEN);
MessageTimer = urand(60000, 180000);
break;
}
case 3:
{
me->Say("Don't make me sad, Come and gamble! Step right up and win today!", LANG_UNIVERSAL, NULL);
me->HandleEmoteCommand(EMOTE_ONESHOT_CRY);
MessageTimer = urand(60000, 180000);
break;
}
default:
{
me->Say("Come one, come all!Step right up to Skinny's! Place your bets, Place your bets!", LANG_UNIVERSAL, NULL);
me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION);
me->CastSpell(me, 44940);
MessageTimer = urand(60000, 180000);
break;
}
}
}
}
else { MessageTimer -= diff; }
};
};
// CREATURE AI
CreatureAI* GetAI(Creature* creature) const override
{
return new NPC_PassiveAI(creature);
}
};
void AddSC_AddNPCGamblerScripts()
{
new GamblerConfig();
new GamblerAnnounce();
new gamble_npc();
}