Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added outfit and mount preview feature to the store #773

Merged
merged 10 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data-otservbr-global/migrations/16.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function onUpdateDatabase()
print("> Updating database to version 17 (Tutorial support)")
print("Updating database to version 17 (Tutorial support)")
db.query("ALTER TABLE `players` ADD `istutorial` SMALLINT(1) NOT NULL DEFAULT '0'")
return true -- true = There are others migrations file | false = this is the last migration file
end
4 changes: 3 additions & 1 deletion data-otservbr-global/migrations/24.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
function onUpdateDatabase()
return false -- true = There are others migrations file | false = this is the last migration file
Spdlog.info("Updating database to version 25 (random mount outfit window)")
db.query("ALTER TABLE `players` ADD `randomize_mount` SMALLINT(1) NOT NULL DEFAULT '0'")
return true
end
3 changes: 3 additions & 0 deletions data-otservbr-global/migrations/25.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function onUpdateDatabase()
return false -- true = There are others migrations file | false = this is the last migration file
end
3 changes: 2 additions & 1 deletion schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS `server_config` (
CONSTRAINT `server_config_pk` PRIMARY KEY (`config`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '24'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');
INSERT INTO `server_config` (`config`, `value`) VALUES ('db_version', '25'), ('motd_hash', ''), ('motd_num', '0'), ('players_record', '0');

-- Table structure `accounts`
CREATE TABLE IF NOT EXISTS `accounts` (
Expand Down Expand Up @@ -142,6 +142,7 @@ CREATE TABLE IF NOT EXISTS `players` (
`istutorial` tinyint(1) NOT NULL DEFAULT '0',
`forge_dusts` bigint(21) NOT NULL DEFAULT '0',
`forge_dust_level` bigint(21) NOT NULL DEFAULT '100',
`randomize_mount` tinyint(1) NOT NULL DEFAULT '0',
INDEX `account_id` (`account_id`),
INDEX `vocation` (`vocation`),
CONSTRAINT `players_pk` PRIMARY KEY (`id`),
Expand Down
32 changes: 32 additions & 0 deletions src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5177,6 +5177,33 @@ void Player::setCurrentMount(uint8_t mount)
addStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, mount);
}

bool Player::hasAnyMount() const
{
for (const auto& mounts = g_game().mounts.getMounts();
const Mount& mount : mounts) {
if (hasMount(&mount)) {
return true;
}
}
return false;
}

uint8_t Player::getRandomMountId() const
{
std::vector<uint8_t> playerMounts;

for (const auto& mounts = g_game().mounts.getMounts();
const Mount& mount : mounts) {
if (hasMount(&mount)) {
playerMounts.push_back(mount.id);
}
}

auto playerMountsSize = static_cast<int32_t>(playerMounts.size() - 1);
auto randomIndex = uniform_random(0, std::max<int32_t>(0, playerMountsSize));
return playerMounts.at(randomIndex);
}

bool Player::toggleMount(bool mount)
{
if ((OTSYS_TIME() - lastToggleMount) < 3000 && !wasMounted) {
Expand Down Expand Up @@ -5205,6 +5232,10 @@ bool Player::toggleMount(bool mount)
return false;
}

if (isRandomMounted()) {
currentMountId = getRandomMountId();
}

const Mount* currentMount = g_game().mounts.getMountByID(currentMountId);
if (!currentMount) {
return false;
Expand All @@ -5227,6 +5258,7 @@ bool Player::toggleMount(bool mount)
}

defaultOutfit.lookMount = currentMount->clientId;
setCurrentMount(currentMount->id);

if (currentMount->speed != 0) {
g_game().changeSpeed(this, currentMount->speed);
Expand Down
10 changes: 10 additions & 0 deletions src/creatures/players/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,17 @@ class Player final : public Creature, public Cylinder
bool tameMount(uint8_t mountId);
bool untameMount(uint8_t mountId);
bool hasMount(const Mount* mount) const;
bool hasAnyMount() const;
uint8_t getRandomMountId() const;
void dismount();

uint8_t isRandomMounted() const {
return randomMount;
}
void setRandomMount(uint8_t isMountRandomized) {
randomMount = isMountRandomized;
}

void sendFYIBox(const std::string& message) {
if (client) {
client->sendFYIBox(message);
Expand Down Expand Up @@ -2383,6 +2392,7 @@ class Player final : public Creature, public Cylinder
int32_t idleTime = 0;
uint32_t coinBalance = 0;
uint16_t expBoostStamina = 0;
uint8_t randomMount = 0;

uint16_t lastStatsTrainingTime = 0;
uint16_t staminaMinutes = 2520;
Expand Down
25 changes: 19 additions & 6 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4908,7 +4908,7 @@ void Game::playerToggleMount(uint32_t playerId, bool mount)
player->toggleMount(mount);
}

void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMountRandomized/* = 0*/)
{
if (!g_configManager().getBoolean(ALLOW_CHANGEOUTFIT)) {
return;
Expand All @@ -4919,6 +4919,13 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
return;
}

player->setRandomMount(isMountRandomized);

if (isMountRandomized && outfit.lookMount != 0 && player->hasAnyMount()) {
auto randomMount = mounts.getMountByID(player->getRandomMountId());
outfit.lookMount = randomMount->clientId;
}

const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType);
if (!playerOutfit) {
outfit.lookMount = 0;
Expand All @@ -4934,17 +4941,23 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
return;
}

const Tile* playerTile = player->getTile();
if (!playerTile) {
return;
}

if (playerTile->hasFlag(TILESTATE_PROTECTIONZONE)) {
outfit.lookMount = 0;
}

if (player->isMounted()) {
Mount* prevMount = mounts.getMountByID(player->getCurrentMount());
if (prevMount) {
changeSpeed(player, mount->speed - prevMount->speed);
}

player->setCurrentMount(mount->id);
} else {
player->setCurrentMount(mount->id);
outfit.lookMount = 0;
}

player->setCurrentMount(mount->id);
} else if (player->isMounted()) {
player->dismount();
}
Expand Down
2 changes: 1 addition & 1 deletion src/game/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ class Game
void playerShowQuestLine(uint32_t playerId, uint16_t questId);
void playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type,
const std::string& receiver, const std::string& text);
void playerChangeOutfit(uint32_t playerId, Outfit_t outfit);
void playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMountRandomized = 0);
void playerInviteToParty(uint32_t playerId, uint32_t invitedId);
void playerJoinParty(uint32_t playerId, uint32_t leaderId);
void playerRevokePartyInvitation(uint32_t playerId, uint32_t invitedId);
Expand Down
10 changes: 6 additions & 4 deletions src/io/iologindata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ bool IOLoginData::preloadPlayer(Player* player, const std::string& name)
player->setGUID(result->getNumber<uint32_t>("id"));
Group* group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id"));
if (!group) {
SPDLOG_ERROR("Player {} has group id {} whitch doesn't exist", player->name,
SPDLOG_ERROR("Player {} has group id {} which doesn't exist", player->name,
result->getNumber<uint16_t>("group_id"));
return false;
}
Expand Down Expand Up @@ -164,7 +164,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)

Group* group = g_game().groups.getGroup(result->getNumber<uint16_t>("group_id"));
if (!group) {
SPDLOG_ERROR("Player {} has group id {} whitch doesn't exist", player->name, result->getNumber<uint16_t>("group_id"));
SPDLOG_ERROR("Player {} has group id {} which doesn't exist", player->name, result->getNumber<uint16_t>("group_id"));
return false;
}
player->setGroup(group);
Expand Down Expand Up @@ -216,7 +216,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
}

if (!player->setVocation(result->getNumber<uint16_t>("vocation"))) {
SPDLOG_ERROR("Player {} has vocation id {} whitch doesn't exist",
SPDLOG_ERROR("Player {} has vocation id {} which doesn't exist",
player->name, result->getNumber<uint16_t>("vocation"));
return false;
}
Expand Down Expand Up @@ -282,6 +282,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
player->addTaskHuntingPoints(result->getNumber<uint64_t>("task_points"));
player->addForgeDusts(result->getNumber<uint64_t>("forge_dusts"));
player->addForgeDustLevel(result->getNumber<uint64_t>("forge_dust_level"));
player->setRandomMount(result->getNumber<uint16_t>("randomize_mount"));

player->lastLoginSaved = result->getNumber<time_t>("lastlogin");
player->lastLogout = result->getNumber<time_t>("lastlogout");
Expand All @@ -292,7 +293,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)

Town* town = g_game().map.towns.getTown(result->getNumber<uint32_t>("town_id"));
if (!town) {
SPDLOG_ERROR("Player {} has town id {} whitch doesn't exist", player->name,
SPDLOG_ERROR("Player {} has town id {} which doesn't exist", player->name,
result->getNumber<uint16_t>("town_id"));
return false;
}
Expand Down Expand Up @@ -890,6 +891,7 @@ bool IOLoginData::savePlayer(Player* player)
query << "`task_points` = " << player->getTaskHuntingPoints() << ',';
query << "`forge_dusts` = " << player->getForgeDusts() << ',';
query << "`forge_dust_level` = " << player->getForgeDustLevel() << ',';
query << "`randomize_mount` = " << static_cast<uint16_t>(player->isRandomMounted()) << ',';

query << "`cap` = " << (player->capacity / 100) << ',';
query << "`sex` = " << static_cast<uint16_t>(player->sex) << ',';
Expand Down
54 changes: 41 additions & 13 deletions src/server/network/protocol/protocolgame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,8 @@ void ProtocolGame::parseSetOutfit(NetworkMessage &msg)
newOutfit.lookMountLegs = std::min<uint8_t>(132, msg.getByte());
newOutfit.lookMountFeet = std::min<uint8_t>(132, msg.getByte());
newOutfit.lookFamiliarsType = msg.get<uint16_t>();
g_game().playerChangeOutfit(player->getID(), newOutfit);
uint8_t isMountRandomized = msg.getByte();
g_game().playerChangeOutfit(player->getID(), newOutfit, isMountRandomized);
}
else if (outfitType == 1)
{
Expand Down Expand Up @@ -5987,15 +5988,34 @@ void ProtocolGame::sendOutfitWindow()

for (const Outfit& outfit : outfits) {
uint8_t addons;
if (!player->getOutfitAddons(outfit, addons)) {
continue;
if (player->getOutfitAddons(outfit, addons)) {
msg.add<uint16_t>(outfit.lookType);
msg.addString(outfit.name);
msg.addByte(addons);
msg.addByte(0x00);
++outfitSize;
} else if (outfit.lookType == 1210 || outfit.lookType == 1211) {
msg.add<uint16_t>(outfit.lookType);
msg.addString(outfit.name);
msg.addByte(3);
msg.addByte(0x02);
++outfitSize;
} else if (outfit.lookType == 1456 || outfit.lookType == 1457) {
msg.add<uint16_t>(outfit.lookType);
msg.addString(outfit.name);
msg.addByte(3);
msg.addByte(0x03);
++outfitSize;
} else if (outfit.from == "store") {
msg.add<uint16_t>(outfit.lookType);
msg.addString(outfit.name);
msg.addByte(outfit.lookType >= 962 && outfit.lookType <= 975 ? 0 : 3);
msg.addByte(0x01);
msg.add<uint32_t>(0x00);
++outfitSize;
}

msg.add<uint16_t>(outfit.lookType);
msg.addString(outfit.name);
msg.addByte(addons);
msg.addByte(0x00);
if (++outfitSize == limitOutfits) {
if (outfitSize == limitOutfits) {
break;
}
}
Expand All @@ -6016,9 +6036,17 @@ void ProtocolGame::sendOutfitWindow()
msg.add<uint16_t>(mount.clientId);
msg.addString(mount.name);
msg.addByte(0x00);
if (++mountSize == limitMounts) {
break;
}
++mountSize;
} else if (mount.type == "store") {
msg.add<uint16_t>(mount.clientId);
msg.addString(mount.name);
msg.addByte(0x01);
msg.add<uint32_t>(0x00);
++mountSize;
}

if (mountSize == limitMounts) {
break;
}
}

Expand Down Expand Up @@ -6055,8 +6083,8 @@ void ProtocolGame::sendOutfitWindow()
msg.addByte(0x00); //Try outfit
msg.addByte(mounted ? 0x01 : 0x00);

// Version 12.81 - Random outfit 'bool'
msg.addByte(0);
// Version 12.81 - Random mount 'bool'
msg.addByte(player->isRandomMounted() ? 0x01 : 0x00);

writeToOutputBuffer(msg);
}
Expand Down