Skip to content

Instantly share code, notes, and snippets.

@darkalvaro

darkalvaro/.lua Secret

Created December 9, 2020 07:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darkalvaro/d04e48d6a3b70eb43224e57b3adf8eaa to your computer and use it in GitHub Desktop.
Save darkalvaro/d04e48d6a3b70eb43224e57b3adf8eaa to your computer and use it in GitHub Desktop.
001-npcBattle.lua
-- Last Storages: 9312, 9712
if (NpcBattle == nil) then
local NPC_BATTLE_TIMEOUT = 60 -- one minute
local NPC_TIME_BETWEEN_BATTLES = 23 * 60 * 60 -- 24 hours
local NPC_TIME_BETWEEN_BATTLES_FOREVER = -2
local NPC_DIFFICULTY_DYNAMIC = 0 -- Depends on player level
local NPC_POKEMON_EXPERIENCE_RATE = 2.0
--local NPC_LEVEL_STORAGE_BASE = 9500 -- Just to remind
local NPC_LEVEL_MAX = 100
local NPC_GROW_PER_LEVEL = 3 -- How much the NPC Pokemon will grow with each NPC level
--local LAST_BATTLE_TIME_STORAGE_BASE = 9100 -- Just to remind
local STORAGE_BASE = 9000
local NPC_STORAGES = {
CURRENT_BATTLE_STATUS = STORAGE_BASE + 1,
OPPONENT = STORAGE_BASE + 2,
POKEMON1_STATUS = STORAGE_BASE + 3,
POKEMON2_STATUS = STORAGE_BASE + 4,
POKEMON3_STATUS = STORAGE_BASE + 5,
POKEMON4_STATUS = STORAGE_BASE + 6,
POKEMON5_STATUS = STORAGE_BASE + 7,
POKEMON6_STATUS = STORAGE_BASE + 8,
BATTLE_TIMEOUT = STORAGE_BASE + 9
}
local POKEMONNUMBER_TO_VAR = {
[1] = NPC_STORAGES.POKEMON1_STATUS,
[2] = NPC_STORAGES.POKEMON2_STATUS,
[3] = NPC_STORAGES.POKEMON3_STATUS,
[4] = NPC_STORAGES.POKEMON4_STATUS,
[5] = NPC_STORAGES.POKEMON5_STATUS,
[6] = NPC_STORAGES.POKEMON6_STATUS
}
local NPC_BATTLE_STATUS = {
ISNT_BATTLEING = -1,
IS_BATTLEING = 0
}
local NPC_POKEMON_STATUS = {
CANNOT_USE = -1,
CAN_USE = 0
}
local TALK_STATE = {
ERASE = 0,
ACCEPTING_BATTLE = 1
}
local BATTLE_END_MODES = {
PLAYER_LOGOUT = 0,
PLAYER_WIN = 1,
PLAYER_LOSS = 2,
PLAYER_AWAY = 3
}
local NPC_DIALOGS = {
POKEMON_DOWN = {
"Grr...",
"I guess I underestimated you...",
"So you aren't so weak.",
"Let's see if you can beat this"
},
BATTLE_START = {
"Alright, prepare yourself!",
"Yep! Let's battle!",
"Let's see if you can do."
},
PLAYER_WITHOUT_POKEMON = {
"Where is your Pokemon?",
"Where is your Pokemon? Afraid?",
"First call your Pokemon!"
},
NPC_LOSS = {
"Grr...",
"You're very lucky...",
"Congratulations!"
},
NPC_WIN = {
"Haha! Where is the Pokemon trainer?",
"Yes! I'm the best! No chance for you.",
"More luck next time."
}
}
--[[ // ]]
local NpcsByName = {}
NpcBattle = {
name = "",
uid = 0,
pokemons = {},
currentPokemons = {},
rewardBaseExp = 0,
rewardItems = {},
rewardUniqueItems = {},
rewardRespect = 0,
rewardBadge = nil,
lastBattleTimeStorage = 0,
requiredRespect = 0,
oneWin = false,
difficulty = 10,
required = nil,
onWin = nil,
cooldowns = {},
levelStorage = 0,
baseBet = 0,
currentBet = 0,
npcHandler = nil,
requiredLevel = 0,
changes = {}, -- new pokemon that the NPC will carry when the player win x times structure = changes{{level = 1, pokemon = "Pikachu"}, {level = 2, pokemon = Charmander}, ...}
payRespect = 0,
evolve = true,
lastDefeatTimeStorage = nil,
lastOrder = 0,
pokemonMaxLevel = NPC_LEVEL_MAX,
specialMove = nil, -- A move that all Pokemon from this NPC will use
pokemonDefeatExperienced = false,
onEnd = nil,
onDefeatNpcPokemon = nil,
requiredMessage = nil,
pokemonMovesets = nil,
currentPokemonId = -1,
pokemonTeamEvolvable = true,
extraStats = nil,
lossSpeech = nil,
winSpeech = nil,
battleInterval = nil,
defeatedPokemonCount = 0,
linealOrder = false
}
-- local REGISTERED_STORAGES = {}
function NpcBattle:new(name, lastBattleTimeStorage, levelStorage, npcHandler, lastDefeatTimeStorage)
if (not npcHandler) then
log(LOG_TYPES.ERROR, "NpcBattle:new missing npcHandler", name, lastBattleTimeStorage, levelStorage)
return
end
if (not levelStorage) then
log(LOG_TYPES.ERROR, "NpcBattle:new missing levelStorage", name, lastBattleTimeStorage)
return
end
if (not lastBattleTimeStorage) then
log(LOG_TYPES.ERROR, "NpcBattle:new missing lastBattleTimeStorage", name)
return
end
if (not name or type(name) ~= "string") then
log(LOG_TYPES.ERROR, "NpcBattle:new missing name")
return
end
--[[if (table.find(REGISTERED_STORAGES, lastBattleTimeStorage)) then This is buggy, cuz if we put more then one NPC at the map (same NPC) the warning will show up
log(LOG_TYPES.ERROR, "NpcBattle:new duplicated last battle time storage", name, lastBattleTimeStorage)
else
REGISTERED_STORAGES[#REGISTERED_STORAGES + 1] = lastBattleTimeStorage
end
if (table.find(REGISTERED_STORAGES, levelStorage)) then
log(LOG_TYPES.ERROR, "NpcBattle:new duplicated level storage", name, levelStorage)
else
REGISTERED_STORAGES[#REGISTERED_STORAGES + 1] = levelStorage
end]]
local obj = {}
obj.name = name
obj.lastBattleTimeStorage = lastBattleTimeStorage
obj.levelStorage = levelStorage
obj.npcHandler = npcHandler
obj.lastDefeatTimeStorage = lastDefeatTimeStorage
setmetatable(obj, self)
self.__index = self
NpcsByName[name] = obj
if (getCreatureIcon(getNpcId()) == CREATURE_ICONS.NONE) then
setCreatureIcon(getNpcId(), CREATURE_ICONS.BATTLE)
end
return obj
end
function NpcBattle:setBattleInterval(v)
self.battleInterval = v
end
function NpcBattle:setCustomPokemonLevel(v)
self.customPokemonLevel = v
end
function NpcBattle:setLossSpeech(v)
self.lossSpeech = v
end
function NpcBattle:setWinSpeech(v)
self.winSpeech = v
end
function NpcBattle:setPokemonMovesets(movesets)
self.pokemonMovesets = movesets
end
function NpcBattle:setPokemons(pokemons)
self.pokemons = pokemons
end
function NpcBattle:setRewardBaseExp(rewardBaseExp)
self.rewardBaseExp = rewardBaseExp
end
function NpcBattle:setRewardItems(rewardItems)
self.rewardItems = rewardItems
end
function NpcBattle:setRewardUniqueItems(rewardUniqueItems)
self.rewardUniqueItems = rewardUniqueItems
end
function NpcBattle:setRewardRespect(rewardRespect)
self.rewardRespect = rewardRespect
end
function NpcBattle:setRewardBadge(rewardBadge)
self.rewardBadge = rewardBadge
end
function NpcBattle:setRequiredRespect(requiredRespect)
self.requiredRespect = requiredRespect
end
function NpcBattle:setOneWin(oneWin)
self.oneWin = oneWin
end
function NpcBattle:setDifficulty(difficulty)
self.difficulty = difficulty
end
function NpcBattle:setRequired(required)
self.required = required
end
function NpcBattle:setRequiredMessage(msg)
self.requiredMessage = msg
end
function NpcBattle:setOnWin(onWin)
self.onWin = onWin
end
function NpcBattle:setOnEnd(v)
self.onEnd = v
end
function NpcBattle:setOnDefeatNpcPokemon(v)
self.onDefeatNpcPokemon = v
end
function NpcBattle:setBaseBet(baseBet)
self.baseBet = baseBet
end
function NpcBattle:setRequiredLevel(requiredLevel)
self.requiredLevel = requiredLevel
end
function NpcBattle:addChange(level, pokemon)
table.insert(self.changes, { level = level, pokemon = pokemon })
end
function NpcBattle:setPayRespect(payRespect)
self.payRespect = payRespect
end
function NpcBattle:setEvolve(evolve)
self.evolve = evolve
end
function NpcBattle:setPokemonMaxLevel(v)
self.pokemonMaxLevel = v
end
function NpcBattle:setSpecialMove(v)
self.specialMove = v
end
function NpcBattle:setPokemonDefeatExperienced(v)
self.pokemonDefeatExperienced = v
end
function NpcBattle:setRequiredStorage(v)
self.requiredStorage = v
end
function NpcBattle:setPokemonTeamEvolvable(v)
self.pokemonTeamEvolvable = v
end
function NpcBattle:setPokemonExtraStats(v)
self.extraStats = v
end
function NpcBattle:setLinealOrder(v)
self.linealOrder = v
end
--[[ // ]]
-- IS
function NpcBattle:isBattleing()
return getCreatureStorage(self.uid, NPC_STORAGES.CURRENT_BATTLE_STATUS) == NPC_BATTLE_STATUS.IS_BATTLEING
end
function NpcBattle:isPokemonOnline()
return #getCreatureSummons(self.uid) > 0
end
-- GET
function NpcBattle:getPokemons()
return self.pokemons
end
function NpcBattle:getPokemon()
return getCreatureSummons(self.uid)[1]
end
function NpcBattle:getBattleTimeoutRemaing()
return getCreatureStorage(self.uid, NPC_STORAGES.BATTLE_TIMEOUT)
end
function NpcBattle:getRequiredRespect()
return self.requiredRespect
end
function NpcBattle:getMoveCooldown(move)
return (self.cooldowns[move] and self.cooldowns[move] - getCurrentTimeInSeconds()) or 0
end
--[[ The NPC level is based on how many wins the player gets against him, many wins = npc stronger ]]
function NpcBattle:getLevel(pid)
if (self.evolve) then
local v = getCreatureStorage(pid, self.levelStorage)
return (v and v > 0 and v <= self.pokemonMaxLevel) and v or 1
end
return 1
end
function NpcBattle:getPokemonMove(pokemonUid, targetUid)
local moves = self.pokemonMovesets and self.pokemonMovesets[self.currentPokemonId] or getPokemonDefaultSkills(getPokemonReferenceName(pokemonUid))
if (self.specialMove) then
moves[#moves + 1] = self.specialMove
end
-- First try a random move, if cant use it, try from the strongest to down
local move = table.random(moves)
if (not self:canPokemonUseMove(move, pokemonUid, targetUid)) then
move = nil
for i = #moves, #moves, -1 do
if (self:canPokemonUseMove(moves[i], pokemonUid, targetUid)) then
move = moves[i]
break
end
end
end
return move
end
-- SET
function NpcBattle:setBattleStatus(status)
doCreatureSetStorage(self.uid, NPC_STORAGES.CURRENT_BATTLE_STATUS, status)
end
function NpcBattle:setOpponent(opponent)
doCreatureSetStorage(self.uid, NPC_STORAGES.OPPONENT, opponent)
doNpcSetOpponent(self.uid, opponent) -- Source function
end
function NpcBattle:setPokemonStatus(pokemonNumber, status)
doCreatureSetStorage(self.uid, POKEMONNUMBER_TO_VAR[pokemonNumber], status)
end
function NpcBattle:setBattleTimeout(battleTimeout)
doCreatureSetStorage(self.uid, NPC_STORAGES.BATTLE_TIMEOUT, battleTimeout)
end
function NpcBattle:setPlayerLastBattleTimeWithNpc(pid, forever)
if (isNumber(forever)) then
doCreatureSetStorage(pid, self.lastBattleTimeStorage, forever)
else
doCreatureSetStorage(pid, self.lastBattleTimeStorage, (forever and NPC_TIME_BETWEEN_BATTLES_FOREVER or os.time()))
end
end
function NpcBattle:setMoveCooldown(move, cooldown)
if (not cooldown) then
cooldown = getPokemonSkillCooldownTime(move)
end
self.cooldowns[move] = cooldown + getCurrentTimeInSeconds()
end
function NpcBattle:setLevel(pid, level)
doCreatureSetStorage(pid, self.levelStorage, (level <= self.pokemonMaxLevel and level or
self.pokemonMaxLevel))
end
-- CAN
function NpcBattle:canPokemonUseMove(move, pokemonUid, targetUid)
if (self:getMoveCooldown(move) > 0 or getPokemonSkillWildBlock(move) or
getMonsterLevel(pokemonUid) < getPokemonSkillRequiredLevel(getCreatureName(pokemonUid), move) or
(getPokemonSkillMakeHeal(move) and getCreatureHealth(pokemonUid) / getCreatureMaxHealth(pokemonUid) > 0.5)) then
return false
end
if (getInstantSpellInfo(SKILL_FUNCTION_PREFIX .. move).needdirection == 1) then
local pokemonPos, targetPos = getCreaturePosition(pokemonUid), getCreaturePosition(targetUid)
if (pokemonPos.x ~= targetPos.x and pokemonPos.y ~= targetPos.y) then
return false
end
doCreatureSetLookDirection(pokemonUid, getDirectionTo(pokemonPos, targetPos))
end
return true
end
function NpcBattle:canPlayerBattleWithNpc(pid)
if (getPlayerDueling(pid)) then
return __L(pid, "You can't battle against me while you're dueling.")
end
if (self.payRespect > 0 and getPlayerRespect(pid) < self.payRespect) then
return string.format(__L(pid, "You need pay %s respect points to battle against me."), self.payRespect)
end
if (getPlayerLevel(pid) < self.requiredLevel) then
return string.format(__L(pid, "You need at least level %s to battle against me."), self.requiredLevel)
end
self:doUpdateCurrentBet(pid)
if (self.currentBet > 0 and getPlayerMoney(pid) < self.currentBet) then
return string.format(__L(pid, "You need at least %s dollars to bet."), self.currentBet)
end
if (getPlayerRespect(pid) < self.requiredRespect) then
return string.format(__L(pid, "Sorry kid, you need at least %s respect points to do a battle with me."), self.requiredRespect)
end
if (self.rewardBadge ~= nil) then
if (getPlayerItemCount(pid, self.rewardBadge.newItemId) > 0) then
return __L(pid, "You already have my badge, there is no reason to a new battle.")
end
end
local lastBattleTime = getCreatureStorage(pid, self.lastBattleTimeStorage)
if (lastBattleTime == NPC_TIME_BETWEEN_BATTLES_FOREVER) then
return __L(pid, "You beat me before, there is no reason to a new battle.")
elseif (lastBattleTime > -1) then
local interval = self.battleInterval ~= nil and self.battleInterval or NPC_TIME_BETWEEN_BATTLES
-- If default 24h, we can disable the remaing time if the player dueled before the serversave
if (interval == NPC_TIME_BETWEEN_BATTLES and lastBattleTime <= getGlobalStorageValue(GLOBAL_STORAGES.SERVER_START_TIME)) then
interval = 0
end
local remaingTime = (lastBattleTime + interval) - os.time()
if (remaingTime > 0) then
return string.format(__L(pid, "Sorry, you must wait %s seconds to battle with me again."), table.concat(string.timediff(remaingTime, pid)))
end
end
if ((self.requiredStorage and getCreatureStorage(pid, self.requiredStorage) < 0) or
(self.required and not self.required(pid))) then
return self.requiredMessage and self.requiredMessage or __L(pid, "You can't duel against me yet.")
end
if (not isSightClear(getCreaturePosition(pid), getCreaturePosition(self.uid), false)) then
return __L(pid, "First get next to me.")
end
if (isPokemonOnline(pid)) then
local pokemon = getPlayerPokemon(pid)
if (isMonster(pokemon) and isPokemonUsingHealthPotion(pokemon) or isPokemonUsingEnergyPotion(pokemon)) then
return __L(pid, "You can't battle against me while your Pokemon is using potions.")
end
end
if (getTilePzInfo(getCreaturePosition(pid))) then
return __L(pid, "You can't duel against me while you are in a protection zone.")
end
return true
end
-- DO
function NpcBattle:doMovesCooldownReset()
self.cooldowns = {}
end
function NpcBattle:doPlayerGiveBadge(pid, badge)
doTransformItem(getPlayerItemById(pid, true, badge.oldItemId).uid, badge.newItemId)
end
function NpcBattle:doPokemonsStatusReset()
for i = 1, #self.currentPokemons do
self:setPokemonStatus(i, NPC_POKEMON_STATUS.CAN_USE)
end
end
function NpcBattle:doBattleTimeoutReset()
self:setBattleTimeout(NPC_BATTLE_TIMEOUT)
end
function NpcBattle:doCallPokemon(pokemonName, pid)
self:doMovesCooldownReset()
doAddExhaust(self.uid)
local spawnPosition = getCreaturePosition(self.uid)
local pokemon = doSummonCreature(pokemonName, spawnPosition, false)
local pos = getPositionAdjacent(pokemon, spawnPosition)
if (pos) then
doTeleportThing(pokemon, pos)
end
doConvinceCreature(self.uid, pokemon)
doSendMagicEffect(spawnPosition, balls["poke"].effects.use)
registerCreatureEvent(pokemon, "npcPokemonDeath")
doCreatureSay(self.uid, string.format(__L(pid, "Go, %s!"), pokemonName), TALKTYPE_MONSTER)
addEvent(function()
if (isCreature(pokemon)) then
doSendCreatureEffect(pokemon, CREATURE_EFFECTS.RED_FADE_IN)
end
end, 10)
local level = 0
if (self.customPokemonLevel ~= nil) then
level = self.customPokemonLevel(pid)
elseif (self.difficulty ~= NPC_DIFFICULTY_DYNAMIC) then
level = self.difficulty + ((self:getLevel(getNpcOpponent(self.uid)) - 1) * NPC_GROW_PER_LEVEL)
else
level = getPlayerLevel(getNpcOpponent(self.uid))
level = level >= 10 and level or 10
level = level <= 90 and level or 90
level = getRandom(level - 20, level)
end
level = (level <= self.pokemonMaxLevel and level or self.pokemonMaxLevel)
level = (level <= POKEMON_LEVEL_MAX and level or POKEMON_LEVEL_MAX)
level = (level > 0 and level or 1)
setMonsterExtraPoints(pokemon, level + 10)
setMonsterLevel(pokemon, level)
if (self.pokemonDefeatExperienced) then
setCreatureSkillLoss(pokemon, true) -- Enable exp
setMonsterExperienceRate(pokemon, NPC_POKEMON_EXPERIENCE_RATE)
end
if (self.extraStats) then
setMonsterVarPokeStat(pokemon, MONSTER_POKE_STATS.MAXHEALTH, self.extraStats)
setMonsterVarPokeStat(pokemon, MONSTER_POKE_STATS.ATTACK, self.extraStats)
setMonsterVarPokeStat(pokemon, MONSTER_POKE_STATS.DEFENSE, self.extraStats)
setMonsterVarPokeStat(pokemon, MONSTER_POKE_STATS.SPECIALATTACK, self.extraStats)
setMonsterVarPokeStat(pokemon, MONSTER_POKE_STATS.SPECIALDEFENSE, self.extraStats)
setMonsterVarPokeStat(pokemon, MONSTER_POKE_STATS.SPEED, self.extraStats)
end
end
local function getElementMultiplerBetweenPokemons(pokemon1, pokemon2)
if (not pokemon1 or not pokemon2) then
return 0
end
local result = 0
local pokemon1elements = getPokemonTypes(nil, pokemon1)
local pokemon2elements = getPokemonTypes(nil, pokemon2)
for i = 1, #pokemon1elements do
for j = 1, #pokemon2elements do
result = result + getElementMultipler(pokemon1elements[i], pokemon2elements[j])
end
end
return result
end
local function findMaxInTable(inTable)
if (#inTable < 1) then
return nil
end
local resultKey, resultValue, tableKey, tableValue = -1, -1, nil, nil
for tableKey = 1, #inTable do
tableValue = inTable[tableKey]
if (tableValue > -1 and tableValue > resultValue) then
resultKey = tableKey
resultValue = tableValue
end
end
if (resultValue > -1) then
return resultKey
end
return nil
end
function NpcBattle:doCallNextPokemon()
local pid = getNpcOpponent(self.uid)
self.defeatedPokemonCount = (self.currentPokemonId and (self.defeatedPokemonCount + 1) or 0)
if (self.defeatedPokemonCount > 0 and self.onDefeatNpcPokemon) then
self:onDefeatNpcPokemon(pid, self.defeatedPokemonCount)
end
if (self.linealOrder) then
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON1_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
self:doCallPokemon(self.currentPokemons[1], pid)
self:setPokemonStatus(1, NPC_POKEMON_STATUS.CANNOT_USE)
self.currentPokemonId = 1
return true
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON2_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
self:doCallPokemon(self.currentPokemons[2], pid)
self:setPokemonStatus(2, NPC_POKEMON_STATUS.CANNOT_USE)
self.currentPokemonId = 2
return true
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON3_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
self:doCallPokemon(self.currentPokemons[3], pid)
self:setPokemonStatus(3, NPC_POKEMON_STATUS.CANNOT_USE)
self.currentPokemonId = 3
return true
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON4_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
self:doCallPokemon(self.currentPokemons[4], pid)
self:setPokemonStatus(4, NPC_POKEMON_STATUS.CANNOT_USE)
self.currentPokemonId = 4
return true
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON5_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
self:doCallPokemon(self.currentPokemons[5], pid)
self:setPokemonStatus(5, NPC_POKEMON_STATUS.CANNOT_USE)
self.currentPokemonId = 5
return true
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON6_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
self:doCallPokemon(self.currentPokemons[6], pid)
self:setPokemonStatus(6, NPC_POKEMON_STATUS.CANNOT_USE)
self.currentPokemonId = 6
return true
end
return false
end
local npcPokemonsMultiplers = {}
local npcOpponentPokemonName = getPlayerPokemonName(pid)
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON1_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
table.insert(npcPokemonsMultiplers, 1, getElementMultiplerBetweenPokemons(self.currentPokemons[1], npcOpponentPokemonName))
else
table.insert(npcPokemonsMultiplers, 1, -1)
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON2_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
table.insert(npcPokemonsMultiplers, 2, getElementMultiplerBetweenPokemons(self.currentPokemons[2], npcOpponentPokemonName))
else
table.insert(npcPokemonsMultiplers, 2, -1)
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON3_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
table.insert(npcPokemonsMultiplers, 3, getElementMultiplerBetweenPokemons(self.currentPokemons[3], npcOpponentPokemonName))
else
table.insert(npcPokemonsMultiplers, 3, -1)
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON4_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
table.insert(npcPokemonsMultiplers, 4, getElementMultiplerBetweenPokemons(self.currentPokemons[4], npcOpponentPokemonName))
else
table.insert(npcPokemonsMultiplers, 4, -1)
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON5_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
table.insert(npcPokemonsMultiplers, 5, getElementMultiplerBetweenPokemons(self.currentPokemons[5], npcOpponentPokemonName))
else
table.insert(npcPokemonsMultiplers, 5, -1)
end
if (getCreatureStorage(self.uid, NPC_STORAGES.POKEMON6_STATUS) == NPC_POKEMON_STATUS.CAN_USE) then
table.insert(npcPokemonsMultiplers, 6, getElementMultiplerBetweenPokemons(self.currentPokemons[6], npcOpponentPokemonName))
else
table.insert(npcPokemonsMultiplers, 6, -1)
end
local bestPokemonToUseNow = findMaxInTable(npcPokemonsMultiplers)
if (bestPokemonToUseNow) then
self:doCallPokemon(self.currentPokemons[bestPokemonToUseNow], pid)
self:setPokemonStatus(bestPokemonToUseNow, NPC_POKEMON_STATUS.CANNOT_USE)
self.currentPokemonId = bestPokemonToUseNow
return true
end
return false
end
function NpcBattle:doBattleEnd(pid, battleEndMode)
if (battleEndMode == BATTLE_END_MODES.PLAYER_WIN) then
if (self.lossSpeech) then
doCreatureSay(self.uid, __L(pid, self.lossSpeech), TALKTYPE_SAY)
else
doCreatureSay(self.uid, __L(pid, table.random(NPC_DIALOGS.NPC_LOSS)), TALKTYPE_SAY)
end
if (self.rewardBaseExp > 0 or self.rewardBadge ~= nil or #self.rewardItems > 0 or
#self.rewardUniqueItems > 0 or self.currentBet > 0) then
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, string.format(__L(pid, "You won %s and received your reward."), self.name))
else
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, string.format(__L(pid, "You won %s."), self.name))
end
doPlayerAddBattleWin(pid)
doPlayerAddRespect(pid, (self.rewardRespect * 2))
doPlayerAddStatistic(pid, PLAYER_STATISTIC_IDS.DEFEAT_NPC, 1)
local previousLevel = self:getLevel(pid)
self:setLevel(pid, previousLevel + 1)
if (self.rewardBaseExp > 0) then
doPlayerAddExperience(pid, self.rewardBaseExp * previousLevel)
doSendAnimatedText(getCreaturePosition(pid), "+EXP!", COLOR_LIGHTGREEN)
end
if (self.rewardBadge ~= nil) then
self:doPlayerGiveBadge(pid, self.rewardBadge)
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, string.format(__L(pid, "Congratulations! You received the %s from %s."), getItemNameById(self.rewardBadge.newItemId), self.name))
end
doPlayerAchievementCheck(pid, ACHIEVEMENT_IDS.KANTO_BADGES)
for i = 1, #self.rewardItems, 2 do
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, string.format(__L(pid, "You received: %s [%sx]."), getItemNameById(self.rewardItems[i]), self.rewardItems[i + 1]))
doPlayerAddItem(pid, self.rewardItems[i], self.rewardItems[i + 1], true)
end
for i = 1, #self.rewardUniqueItems, 2 do
local item = doCreateUniqueItemEx(pid, self.rewardUniqueItems[i], self.rewardUniqueItems[i + 1])
if (item) then
if (doPlayerAddItemEx(pid, item, false) == RETURNVALUE_NOERROR) then
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, string.format(__L(pid, "You received: %s [%sx]."), getItemNameById(self.rewardUniqueItems[i]), self.rewardUniqueItems[i + 1]))
else
doPlayerAddDepotItem(pid, 0, item)
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, string.format(__L(pid, "You received: %s [%x] directly into your depot."), getItemNameById(self.rewardUniqueItems[i]), self.rewardUniqueItems[i + 1]))
end
else
log(LOG_TYPES.ERROR, "NpcBattle:doBattleEnd - Can't create reward unique item.",
getCreatureName(pid), self.rewardUniqueItems[i], self.rewardUniqueItems[i + 1])
end
end
if (self.currentBet > 0) then
doPlayerAddMoney(pid, math.floor(self.currentBet * 2))
end
if (self.oneWin) then
self:setPlayerLastBattleTimeWithNpc(pid, true)
end
if (self.lastDefeatTimeStorage) then
doCreatureSetStorage(pid, self.lastDefeatTimeStorage, os.time())
end
if (self.onWin) then
self.onWin(pid)
end
elseif (battleEndMode == BATTLE_END_MODES.PLAYER_LOSS) then
if (self.winSpeech) then
doCreatureSay(self.uid, __L(pid, self.winSpeech), TALKTYPE_SAY)
else
doCreatureSay(self.uid, __L(pid, table.random(NPC_DIALOGS.NPC_WIN)), TALKTYPE_SAY)
end
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, string.format(__L(pid, "You have been defeated by %s!"), self.name))
doPlayerAddBattleLoss(pid)
elseif (battleEndMode == BATTLE_END_MODES.PLAYER_AWAY) then
doCreatureSay(self.uid, table.random(NPC_DIALOGS.NPC_WIN), TALKTYPE_SAY)
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, "You and your opponent were too far from each other and the battle was canceled.")
doPlayerAddBattleLoss(pid)
end
if (self:isPokemonOnline()) then
local npcPokemon = self:getPokemon()
doCreatureSay(self.uid, string.format(__L(pid, "Back, %s!"), getCreatureName(npcPokemon)), TALKTYPE_SAY)
doSendMagicEffect(getCreaturePosition(npcPokemon), balls["poke"].effects.use)
doRemoveCreature(npcPokemon)
end
self:setBattleStatus(NPC_BATTLE_STATUS.ISNT_BATTLEING)
self.npcHandler:releaseFocus(pid)
if (isPlayer(pid)) then
setPlayerBattleing(pid, false)
setPlayerDisconnectAtExit(pid, true)
doCreatureSetNoMove(pid, false)
if (self.onEnd) then
self.onEnd(pid, battleEndMode == BATTLE_END_MODES.PLAYER_WIN, self.uid)
end
end
end
function NpcBattle:doBattleCheck(pid, battleTimeRemaing, decreaseTimeout, playerLastFreeCap, checkAlivePokemon)
if (not isPlayer(pid)) then
self:doBattleEnd(pid, BATTLE_END_MODES.PLAYER_LOGOUT)
return
end
local playerCap = getPlayerFreeCap(pid)
if (playerLastFreeCap and playerCap ~= playerLastFreeCap) then
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, "You modified your Pokemon team during battle! You're out!")
self:doBattleEnd(pid, BATTLE_END_MODES.PLAYER_LOSS)
return
end
if (battleTimeRemaing == 0) then
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, "Your time has ended.")
self:doBattleEnd(pid, BATTLE_END_MODES.PLAYER_LOSS)
return
end
local playerPosition = getCreaturePosition(pid)
local npcPosition = getCreaturePosition(self.uid)
if (not npcPosition) then
return
end
if (getDistanceBetween(npcPosition, playerPosition) > 6 or playerPosition.z ~= npcPosition.z) then
self:doBattleEnd(pid, BATTLE_END_MODES.PLAYER_AWAY)
return
end
if (self:isPokemonOnline()) then
local playerPokemon = getPlayerPokemon(pid)
if (isCreature(playerPokemon)) then
local npcPokemon = self:getPokemon()
if (not hasTarget(npcPokemon)) then
doCreatureSetTarget(npcPokemon, playerPokemon)
end
if (not isExhaust(self.uid)) then
local move = self:getPokemonMove(npcPokemon, playerPokemon)
if (move) then
self:doPokemonUseMove(npcPokemon, move)
end
end
if (getRandom(0, 100) <= 25 or getDistanceBetween(getCreaturePosition(npcPokemon), getCreaturePosition(playerPokemon)) > 3) then
self:doOrderPokemon(npcPokemon, playerPokemon, pid)
end
elseif (getPlayerDuelPokemonRemaing(pid) <= 0) then
self:doBattleEnd(pid, BATTLE_END_MODES.PLAYER_LOSS)
return
elseif (decreaseTimeout) then
if (checkAlivePokemon) then
local tmpBalls = getPlayerAllBallsWithPokemon(pid)
local alives = #tmpBalls
if (alives < PLAYER_BALL_MAX) then -- Maybe this player hasnt six Pokemon, so the timeout is useless
for _, ball in pairs(tmpBalls) do
local ballName = ballsNames[ball.itemid]
if (not ballName or ball.itemid == balls[ballName].discharged) then
alives = alives - 1
end
end
if (alives <= 0) then
self:doBattleEnd(pid, BATTLE_END_MODES.PLAYER_LOSS)
return
end
else
checkAlivePokemon = nil -- Isnt need check if the player got six Pokemon, the timeout will work fine
end
end
local timeout = self:getBattleTimeoutRemaing()
if (timeout <= 0) then
doPlayerSendTextMessage(pid, MESSAGE_STATUS_CONSOLE_BLUE, "Your time has ended.")
self:doBattleEnd(pid, BATTLE_END_MODES.PLAYER_LOSS)
return
else
doSendAnimatedText(playerPosition, timeout, TEXTCOLOR_RED)
self:setBattleTimeout(timeout - 1)
decreaseTimeout = false
end
else
decreaseTimeout = true
end
else
if (self:doCallNextPokemon()) then
doCreatureSay(self.uid, __L(pid, table.random(NPC_DIALOGS.POKEMON_DOWN)), TALKTYPE_SAY)
else
self:doBattleEnd(pid, BATTLE_END_MODES.PLAYER_WIN)
return
end
end
addEvent(self.doBattleCheck, 500, self, pid, battleTimeRemaing - 0.5, decreaseTimeout, playerCap, checkAlivePokemon)
end
function NpcBattle:doBattleStart(pid)
self:setBattleStatus(NPC_BATTLE_STATUS.IS_BATTLEING)
self:setOpponent(pid)
self:doUpdateCurrentPokemons(pid)
self:doPokemonsStatusReset()
self:doBattleTimeoutReset()
self:doCallNextPokemon()
self:setPlayerLastBattleTimeWithNpc(pid)
if (self.currentBet > 0) then doPlayerRemoveMoney(pid, self.currentBet) end
if (self.payRespect > 0) then doPlayerAddRespect(pid, -self.payRespect) end
setPlayerDuelPokemonRemaing(pid, 6)
setPlayerBattleing(pid, true)
doAddCondition(pid, pokemonCallDelayCondition)
doCreatureSay(self.uid, __L(pid, table.random(NPC_DIALOGS.BATTLE_START)), TALKTYPE_SAY)
setPlayerDisconnectAtExit(pid, false)
doCreatureSetNoMove(pid, true)
self:doBattleCheck(pid, 10 * 60, true, nil, true)
end
function NpcBattle:doPokemonUseMove(pokemonUid, move)
if (doPokemonUseSkill(pokemonUid, move)) then
self:setMoveCooldown(move)
doAddExhaust(self.uid)
end
end
function NpcBattle:doUpdateCurrentPokemons(pid)
local npcLevel = self:getLevel(pid)
self.currentPokemons = table.copy(self.pokemons)
-- Add all possibly pokemons to my team
for i, change in ipairs(self.changes) do
if (npcLevel >= change.level) then
table.insert(self.currentPokemons, change.pokemon)
end
end
-- If I get more then I can carry, remove random
while (#self.currentPokemons > 6) do
table.remove(self.currentPokemons, math.random(1, #self.currentPokemons))
end
-- What level my pokemons are now
local level
if (self.difficulty ~= NPC_DIFFICULTY_DYNAMIC) then
level = self.difficulty + ((npcLevel - 1) * NPC_GROW_PER_LEVEL)
level = (level <= POKEMON_LEVEL_MAX and level or POKEMON_LEVEL_MAX)
else
level = getPlayerLevel(pid)
level = level >= 10 and level or 10
level = level <= 90 and level or 90
level = math.random(level - 5, level)
end
-- Evolve them, if possible
if (self.pokemonTeamEvolvable) then
for i = 1, #self.currentPokemons do
local evolutions = getPokemonEvolutions(self.currentPokemons[i])
while (evolutions and #evolutions > 0) do
local tmpEvolutions = evolutions
evolutions = nil
for k, v in ipairs(tmpEvolutions) do
if (level >= v.requiredLevel) then
self.currentPokemons[i] = v.name
evolutions = getPokemonEvolutions(self.currentPokemons[i])
break
end
end
end
end
end
end
function NpcBattle:doUpdateCurrentBet(pid)
self.currentBet = self.baseBet * self:getLevel(pid)
end
function NpcBattle:doOrderPokemon(pokemonUid, targetUid, pid)
local pos = getPositionAdjacent(pokemonUid, getCreaturePosition(targetUid))
if (pos) then
if (not getPathToEx(pokemonUid, pos)) then
doTeleportThing(pokemonUid, getCreaturePosition(targetUid))
return
end
--doCreatureWalkToPosition(pokemonUid, pos)
if ((os.time() - self.lastOrder) > 4) then
doCreatureSay(self.uid, string.format(__L(pid, "%s, go there!"), getCreatureNickname(pokemonUid)), TALKTYPE_MONSTER)
self.lastOrder = os.time()
end
end
end
-- Global functions
function NpcBattle:doTalkStart(npcUid, pid)
self.uid = npcUid
if (self:isBattleing()) then
selfSay("I'm battling at the moment, please wait.", pid)
return TALK_STATE.ERASE
else
local result = self:canPlayerBattleWithNpc(pid)
if (result == true) then
selfSay(string.format(__L(pid, "Do you really want to battle with me?%s%s"),
(self.currentBet > 0 and (string.format(__L(pid, " (You will bet %s dollars)"), self.currentBet)) or ""),
(self.payRespect > 0 and (string.format(__L(pid, " (You will pay %s respect points)"), self.payRespect)) or "")), pid)
return TALK_STATE.ACCEPTING_BATTLE
else
selfSay(result, pid)
return TALK_STATE.ERASE
end
end
end
function NpcBattle:doTalkEnd(npcUid, pid, talkState)
self.uid = npcUid
if (talkState == TALK_STATE.ACCEPTING_BATTLE) then
if (self:isBattleing()) then
selfSay("I'm battling at the moment, please wait.", pid)
return TALK_STATE.ERASE
elseif (not isPokemonOnline(pid) and #getCreatureSummons(pid) == 0) then
selfSay(__L(pid, table.random(NPC_DIALOGS.PLAYER_WITHOUT_POKEMON)), pid)
return TALK_STATE.ERASE
else
local result = self:canPlayerBattleWithNpc(pid)
if (result == true) then
self:doBattleStart(pid)
else
selfSay(result, pid)
return TALK_STATE.ERASE
end
end
else
selfSay("Huh?", pid)
end
return TALK_STATE.ERASE
end
function NpcBattle:doForceBattleStart(npcUid, pid)
if (self:isBattleing()) then
return false
end
self.uid = npcUid
self:doBattleStart(pid)
return true
end
function getNpcOpponent(nid)
return getCreatureStorage(nid, NPC_STORAGES.OPPONENT)
end
function getNpcBattleByName(name)
return NpcsByName[name]
end
function getPlayerDefeatedNPC(cid, npcName)
local r = getCreatureStorage(cid,
(type(npcName) == 'string' and getNpcBattleByName(npcName).levelStorage or npcName)) -- npcName can be the storage too, because I can normaly call this function only inside NPCs
return r > 1
end
function getPlayerLastDefeatedNpc(cid, lastDefeatTimeStorage)
return getCreatureStorage(cid, lastDefeatTimeStorage)
end
function doPlayerEraseDefeatedNpc(cid, npcName)
if (type(npcName) ~= 'string') then
log(LOG_TYPES.ERROR, "npcBattle:doPlayerEraseDefeatedNPC - npcName is not a string.", getCreatureName(cid), npcName)
return
end
doCreatureSetStorage(cid, getNpcBattleByName(npcName).levelStorage, -1)
doCreatureSetStorage(cid, getNpcBattleByName(npcName).lastBattleTimeStorage, -1)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment