introduce offline training scripts

This commit is contained in:
ErikasKontenis 2020-11-07 14:32:53 +02:00
parent 093ac51d31
commit 33d9dd7dcd
12 changed files with 378 additions and 5 deletions

View File

@ -1,6 +1,6 @@
-- Custom
knightCloseAttackDamageIncreasePercent = 50
paladinRangeAttackDamageIncreasePercent = 40
knightCloseAttackDamageIncreasePercent = 20
paladinRangeAttackDamageIncreasePercent = 15
-- Combat settings
-- NOTE: valid values for worldType are: "pvp", "no-pvp" and "pvp-enforced"

View File

@ -239,6 +239,7 @@
<action itemid="3031" script="misc/changegold.lua" />
<action itemid="3035" script="misc/changegold.lua" />
<action itemid="3043" script="misc/changegold.lua" />
<action fromid="2032" toid="2032" script="misc/skill_trainer.lua" />
<!-- Chests -->
<action itemid="2479" script="misc/chests.lua" />

View File

@ -0,0 +1,23 @@
local statues = {
[2032] = SKILL_SWORD,
[18489] = SKILL_AXE,
[18490] = SKILL_CLUB,
[18491] = SKILL_DISTANCE,
[18492] = SKILL_MAGLEVEL
}
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
local skill = statues[item:getId()]
if not player:isPremium() then
player:sendCancelMessage(RETURNVALUE_YOUNEEDPREMIUMACCOUNT)
return true
end
if player:isPzLocked() then
return false
end
player:setOfflineTrainingSkill(skill)
player:remove()
return true
end

View File

@ -6,7 +6,8 @@
<event type="login" name="FirstItems" script="firstitems.lua"/>
<event type="login" name="RegenerateStamina" script="regeneratestamina.lua" />
<event type="death" name="PlayerDeath" script="playerdeath.lua"/>
<event type="login" name="OfflineTraining" script="offlinetraining.lua" />
<!-- Killing In The Name Of Quest -->
<event type="kill" name="KillingInTheNameOfKills" script="killing_in_the_name_of.lua" />
</creaturescripts>

View File

@ -0,0 +1,75 @@
function onLogin(player)
local lastLogout = player:getLastLogout()
local offlineTime = lastLogout ~= 0 and math.min(os.time() - lastLogout, 86400 * 21) or 0
local offlineTrainingSkill = player:getOfflineTrainingSkill()
if offlineTrainingSkill == -1 then
player:addOfflineTrainingTime(offlineTime * 1000)
return true
end
player:setOfflineTrainingSkill(-1)
if offlineTime < 600 then
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You must be logged out for more than 10 minutes to start offline training.")
return true
end
local trainingTime = math.max(0, math.min(offlineTime, math.min(43200, player:getOfflineTrainingTime() / 1000)))
player:removeOfflineTrainingTime(trainingTime * 1000)
local remainder = offlineTime - trainingTime
if remainder > 0 then
player:addOfflineTrainingTime(remainder * 1000)
end
if trainingTime < 60 then
return true
end
local text = "During your absence you trained for"
local hours = math.floor(trainingTime / 3600)
if hours > 1 then
text = string.format("%s %d hours", text, hours)
elseif hours == 1 then
text = string.format("%s 1 hour", text)
end
local minutes = math.floor((trainingTime % 3600) / 60)
if minutes ~= 0 then
if hours ~= 0 then
text = string.format("%s and", text)
end
if minutes > 1 then
text = string.format("%s %d minutes", text, minutes)
else
text = string.format("%s 1 minute", text)
end
end
text = string.format("%s.", text)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, text)
local vocation = player:getVocation()
local promotion = vocation:getPromotion()
local topVocation = not promotion and vocation or promotion
local updateSkills = false
if table.contains({SKILL_CLUB, SKILL_SWORD, SKILL_AXE, SKILL_DISTANCE}, offlineTrainingSkill) then
local modifier = topVocation:getAttackSpeed() / 1000
updateSkills = player:addOfflineTrainingTries(offlineTrainingSkill, (trainingTime / modifier) / (offlineTrainingSkill == SKILL_DISTANCE and 4 or 2))
elseif offlineTrainingSkill == SKILL_MAGLEVEL then
local gainTicks = topVocation:getManaGainTicks() * 2
if gainTicks == 0 then
gainTicks = 1
end
updateSkills = player:addOfflineTrainingTries(SKILL_MAGLEVEL, trainingTime * (vocation:getManaGainAmount() / gainTicks))
end
if updateSkills then
player:addOfflineTrainingTries(SKILL_SHIELD, trainingTime / 4)
end
return true
end

View File

@ -372,6 +372,7 @@ function setPlayerGroupId(cid, groupId) local p = Player(cid) return p ~= nil an
function doPlayerSetSex(cid, sex) local p = Player(cid) return p ~= nil and p:setSex(sex) or false end
function doPlayerSetGuildLevel(cid, level) local p = Player(cid) return p ~= nil and p:setGuildLevel(level) or false end
function doPlayerSetGuildNick(cid, nick) local p = Player(cid) return p ~= nil and p:setGuildNick(nick) or false end
function doPlayerSetOfflineTrainingSkill(cid, skillId) local p = Player(cid) return p and p:setOfflineTrainingSkill(skillId) or false end
function doShowTextDialog(cid, itemId, text) local p = Player(cid) return p ~= nil and p:showTextDialog(itemId, text) or false end
function doPlayerAddItemEx(cid, uid, ...) local p = Player(cid) return p ~= nil and p:addItemEx(Item(uid), ...) or false end
function doPlayerRemoveItem(cid, itemid, count, ...) local p = Player(cid) return p ~= nil and p:removeItem(itemid, count, ...) or false end

View File

@ -1138,6 +1138,8 @@ CREATE TABLE `players` (
`onlinetime` int(11) NOT NULL DEFAULT '0',
`deletion` bigint(15) NOT NULL DEFAULT '0',
`balance` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
`offlinetraining_time` smallint(5) unsigned NOT NULL DEFAULT '43200',
`offlinetraining_skill` int(11) NOT NULL DEFAULT '-1',
`stamina` smallint(5) NOT NULL DEFAULT '3360',
`skill_fist` int(10) UNSIGNED NOT NULL DEFAULT '10',
`skill_fist_tries` bigint(20) UNSIGNED NOT NULL DEFAULT '0',

View File

@ -190,7 +190,7 @@ bool IOLoginData::preloadPlayer(Player* player, const std::string& name)
bool IOLoginData::loadPlayerById(Player* player, uint32_t id)
{
std::ostringstream query;
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `id` = " << id;
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `id` = " << id;
return loadPlayer(player, Database::getInstance()->storeQuery(query.str()));
}
@ -198,7 +198,7 @@ bool IOLoginData::loadPlayerByName(Player* player, const std::string& name)
{
Database* db = Database::getInstance();
std::ostringstream query;
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `name` = " << db->escapeString(name);
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `name` = " << db->escapeString(name);
return loadPlayer(player, db->storeQuery(query.str()));
}
@ -321,6 +321,9 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
player->lastLoginSaved = result->getNumber<time_t>("lastlogin");
player->lastLogout = result->getNumber<time_t>("lastlogout");
player->offlineTrainingTime = result->getNumber<int32_t>("offlinetraining_time") * 1000;
player->offlineTrainingSkill = result->getNumber<int32_t>("offlinetraining_skill");
Town* town = g_game.map.towns.getTown(result->getNumber<uint32_t>("town_id"));
if (!town) {
std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Town ID " << result->getNumber<uint32_t>("town_id") << " which doesn't exist" << std::endl;
@ -660,6 +663,8 @@ bool IOLoginData::savePlayer(Player* player)
query << "`lastlogout` = " << player->getLastLogout() << ',';
query << "`balance` = " << player->bankBalance << ',';
query << "`offlinetraining_time` = " << player->getOfflineTrainingTime() / 1000 << ',';
query << "`offlinetraining_skill` = " << player->getOfflineTrainingSkill() << ',';
query << "`stamina` = " << player->getStaminaMinutes() << ',';
query << "`skill_fist` = " << player->skills[SKILL_FIST].level << ',';

View File

@ -2008,6 +2008,15 @@ void LuaScriptInterface::registerFunctions()
registerMethod("Player", "getSkillTries", LuaScriptInterface::luaPlayerGetSkillTries);
registerMethod("Player", "addSkillTries", LuaScriptInterface::luaPlayerAddSkillTries);
registerMethod("Player", "addOfflineTrainingTime", LuaScriptInterface::luaPlayerAddOfflineTrainingTime);
registerMethod("Player", "getOfflineTrainingTime", LuaScriptInterface::luaPlayerGetOfflineTrainingTime);
registerMethod("Player", "removeOfflineTrainingTime", LuaScriptInterface::luaPlayerRemoveOfflineTrainingTime);
registerMethod("Player", "addOfflineTrainingTries", LuaScriptInterface::luaPlayerAddOfflineTrainingTries);
registerMethod("Player", "getOfflineTrainingSkill", LuaScriptInterface::luaPlayerGetOfflineTrainingSkill);
registerMethod("Player", "setOfflineTrainingSkill", LuaScriptInterface::luaPlayerSetOfflineTrainingSkill);
registerMethod("Player", "getItemCount", LuaScriptInterface::luaPlayerGetItemCount);
registerMethod("Player", "getItemById", LuaScriptInterface::luaPlayerGetItemById);
@ -7530,6 +7539,95 @@ int LuaScriptInterface::luaPlayerAddSkillTries(lua_State* L)
return 1;
}
int LuaScriptInterface::luaPlayerAddOfflineTrainingTime(lua_State* L)
{
// player:addOfflineTrainingTime(time)
Player* player = getUserdata<Player>(L, 1);
if (player) {
int32_t time = getNumber<int32_t>(L, 2);
player->addOfflineTrainingTime(time);
player->sendStats();
pushBoolean(L, true);
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerGetOfflineTrainingTime(lua_State* L)
{
// player:getOfflineTrainingTime()
Player* player = getUserdata<Player>(L, 1);
if (player) {
lua_pushnumber(L, player->getOfflineTrainingTime());
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerRemoveOfflineTrainingTime(lua_State* L)
{
// player:removeOfflineTrainingTime(time)
Player* player = getUserdata<Player>(L, 1);
if (player) {
int32_t time = getNumber<int32_t>(L, 2);
player->removeOfflineTrainingTime(time);
player->sendStats();
pushBoolean(L, true);
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerAddOfflineTrainingTries(lua_State* L)
{
// player:addOfflineTrainingTries(skillType, tries)
Player* player = getUserdata<Player>(L, 1);
if (player) {
skills_t skillType = getNumber<skills_t>(L, 2);
uint64_t tries = getNumber<uint64_t>(L, 3);
pushBoolean(L, player->addOfflineTrainingTries(skillType, tries));
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerGetOfflineTrainingSkill(lua_State* L)
{
// player:getOfflineTrainingSkill()
Player* player = getUserdata<Player>(L, 1);
if (player) {
lua_pushnumber(L, player->getOfflineTrainingSkill());
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerSetOfflineTrainingSkill(lua_State* L)
{
// player:setOfflineTrainingSkill(skillId)
Player* player = getUserdata<Player>(L, 1);
if (player) {
uint32_t skillId = getNumber<uint32_t>(L, 2);
player->setOfflineTrainingSkill(skillId);
pushBoolean(L, true);
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaPlayerGetItemCount(lua_State* L)
{
// player:getItemCount(itemId[, subType = -1])

View File

@ -833,6 +833,15 @@ class LuaScriptInterface
static int luaPlayerGetSkillTries(lua_State* L);
static int luaPlayerAddSkillTries(lua_State* L);
static int luaPlayerAddOfflineTrainingTime(lua_State* L);
static int luaPlayerGetOfflineTrainingTime(lua_State* L);
static int luaPlayerRemoveOfflineTrainingTime(lua_State* L);
static int luaPlayerAddOfflineTrainingTries(lua_State* L);
static int luaPlayerGetOfflineTrainingSkill(lua_State* L);
static int luaPlayerSetOfflineTrainingSkill(lua_State* L);
static int luaPlayerGetItemCount(lua_State* L);
static int luaPlayerGetItemById(lua_State* L);

View File

@ -1194,6 +1194,8 @@ void Player::onThink(uint32_t interval)
if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
checkSkullTicks();
}
addOfflineTrainingTime(interval);
}
uint32_t Player::isMuted() const
@ -3737,6 +3739,141 @@ void Player::sendClosePrivate(uint16_t channelId)
}
}
bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries)
{
if (tries == 0 || skill == SKILL_LEVEL) {
return false;
}
bool sendUpdate = false;
uint32_t oldSkillValue, newSkillValue;
long double oldPercentToNextLevel, newPercentToNextLevel;
if (skill == SKILL_MAGLEVEL) {
uint64_t currReqMana = vocation->getReqMana(magLevel);
uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
if (currReqMana >= nextReqMana) {
return false;
}
oldSkillValue = magLevel;
oldPercentToNextLevel = static_cast<long double>(manaSpent * 100) / nextReqMana;
g_events->eventPlayerOnGainSkillTries(this, SKILL_MAGLEVEL, tries);
uint32_t currMagLevel = magLevel;
while ((manaSpent + tries) >= nextReqMana) {
tries -= nextReqMana - manaSpent;
magLevel++;
manaSpent = 0;
g_creatureEvents->playerAdvance(this, SKILL_MAGLEVEL, magLevel - 1, magLevel);
sendUpdate = true;
currReqMana = nextReqMana;
nextReqMana = vocation->getReqMana(magLevel + 1);
if (currReqMana >= nextReqMana) {
tries = 0;
break;
}
}
manaSpent += tries;
if (magLevel != currMagLevel) {
std::ostringstream ss;
ss << "You advanced to magic level " << magLevel << '.';
sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
}
uint8_t newPercent;
if (nextReqMana > currReqMana) {
newPercent = Player::getPercentLevel(manaSpent, nextReqMana);
newPercentToNextLevel = static_cast<long double>(manaSpent * 100) / nextReqMana;
}
else {
newPercent = 0;
newPercentToNextLevel = 0;
}
if (newPercent != magLevelPercent) {
magLevelPercent = newPercent;
sendUpdate = true;
}
newSkillValue = magLevel;
}
else {
uint64_t currReqTries = vocation->getReqSkillTries(skill, skills[skill].level);
uint64_t nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
if (currReqTries >= nextReqTries) {
return false;
}
oldSkillValue = skills[skill].level;
oldPercentToNextLevel = static_cast<long double>(skills[skill].tries * 100) / nextReqTries;
g_events->eventPlayerOnGainSkillTries(this, skill, tries);
uint32_t currSkillLevel = skills[skill].level;
while ((skills[skill].tries + tries) >= nextReqTries) {
tries -= nextReqTries - skills[skill].tries;
skills[skill].level++;
skills[skill].tries = 0;
skills[skill].percent = 0;
g_creatureEvents->playerAdvance(this, skill, (skills[skill].level - 1), skills[skill].level);
sendUpdate = true;
currReqTries = nextReqTries;
nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
if (currReqTries >= nextReqTries) {
tries = 0;
break;
}
}
skills[skill].tries += tries;
if (currSkillLevel != skills[skill].level) {
std::ostringstream ss;
ss << "You advanced to " << getSkillName(skill) << " level " << skills[skill].level << '.';
sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
}
uint8_t newPercent;
if (nextReqTries > currReqTries) {
newPercent = Player::getPercentLevel(skills[skill].tries, nextReqTries);
newPercentToNextLevel = static_cast<long double>(skills[skill].tries * 100) / nextReqTries;
}
else {
newPercent = 0;
newPercentToNextLevel = 0;
}
if (skills[skill].percent != newPercent) {
skills[skill].percent = newPercent;
sendUpdate = true;
}
newSkillValue = skills[skill].level;
}
if (sendUpdate) {
sendSkills();
}
std::ostringstream ss;
ss << std::fixed << std::setprecision(2) << "Your " << ucwords(getSkillName(skill)) << " skill changed from level " << oldSkillValue << " (with " << oldPercentToNextLevel << "% progress towards level " << (oldSkillValue + 1) << ") to level " << newSkillValue << " (with " << newPercentToNextLevel << "% progress towards level " << (newSkillValue + 1) << ')';
sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
return sendUpdate;
}
uint64_t Player::getMoney() const
{
std::vector<const Container*> containers;

View File

@ -154,6 +154,25 @@ class Player final : public Creature, public Cylinder
return staminaMinutes;
}
bool addOfflineTrainingTries(skills_t skill, uint64_t tries);
void addOfflineTrainingTime(int32_t addTime) {
offlineTrainingTime = std::min<int32_t>(12 * 3600 * 1000, offlineTrainingTime + addTime);
}
void removeOfflineTrainingTime(int32_t removeTime) {
offlineTrainingTime = std::max<int32_t>(0, offlineTrainingTime - removeTime);
}
int32_t getOfflineTrainingTime() const {
return offlineTrainingTime;
}
int32_t getOfflineTrainingSkill() const {
return offlineTrainingSkill;
}
void setOfflineTrainingSkill(int32_t skill) {
offlineTrainingSkill = skill;
}
uint64_t getBankBalance() const {
return bankBalance;
}
@ -1069,6 +1088,8 @@ class Player final : public Creature, public Cylinder
int32_t premiumDays = 0;
int32_t bloodHitCount = 0;
int32_t shieldBlockCount = 0;
int32_t offlineTrainingSkill = -1;
int32_t offlineTrainingTime = 0;
int32_t idleTime = 0;
int32_t lastWalkingTime = 0;