illarion-content/npc/base/guard.lua

226 lines
7.6 KiB
Lua

--[[
Illarion Server
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details.
You should have received a copy of the GNU Affero General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
]]
--- Base script for guard NPCs
--
-- These NPCs guard a area and warp any monster coming too close and pushing
-- away players they do not like.
local class = require("base.class")
local baseNPC = require("npc.base.basic")
local common = require("base.common")
local factions = require("base.factions")
local messages = require("base.messages")
local character = require("base.character")
local _processMonsters
local _processPlayers
local _getCheckRadius
local _warpHostile
local _isPointInGuardArea
local _getTextAndTalktype
local guardNPC = class(function(self, rootNPC)
if rootNPC == nil or not rootNPC:is_a(baseNPC) then
return
end
self["_parent"] = rootNPC
self["_guardRange"] = {}
self["_guardRange"]["north"] = 0
self["_guardRange"]["south"] = 0
self["_guardRange"]["west"] = 0
self["_guardRange"]["east"] = 0
self["_warpLocation"] = nil
self["_warpedMonsterText"] = nil
self["_warpedPlayerText"] = nil
self["_hitPlayerText"] = nil
self["_nextCycleText"] = -1
self._parent:addCycle(self)
end)
function guardNPC:setGuardRange(n_range, s_range, w_range, e_range)
self._guardRange.north = n_range
self._guardRange.south = s_range
self._guardRange.west = w_range
self._guardRange.east = e_range
end
function guardNPC:setWarpLocation(x, y, z)
self._warpLocation = position(x, y, z)
end
function guardNPC:addWarpedMonsterText(germanText, englishText)
if (self._warpedMonsterText == nil) then
self._warpedMonsterText = messages.Messages()
end
self._warpedMonsterText:addMessage(germanText, englishText)
end
function guardNPC:addWarpedPlayerText(germanText, englishText)
if (self._warpedPlayerText == nil) then
self._warpedPlayerText = messages.Messages()
end
self._warpedPlayerText:addMessage(germanText, englishText)
end
function guardNPC:addHitPlayerText(germanText, englishText)
if (self._hitPlayerText == nil) then
self._hitPlayerText = messages.Messages()
end
self._hitPlayerText:addMessage(germanText, englishText)
end
function guardNPC:nextCycle(npcChar, counter)
if (counter >= self._nextCycleText) then
self._nextCycleText = 5
local radius = _getCheckRadius(self)
_processMonsters(self, npcChar, radius)
_processPlayers(self, npcChar, radius)
else
self._nextCycleText = self._nextCycleText - counter
end
return self._nextCycleText
end
function _processMonsters(self, npcChar, radius)
local monsterList = world:getMonstersInRangeOf(npcChar.pos, radius)
local warpedMonster = false
for _, monster in pairs(monsterList) do
if _isPointInGuardArea(self, npcChar, monster.pos) then
if not common.IsMonsterDocile(monster:getMonsterType()) then
warpedMonster = true
_warpHostile(self, npcChar, monster)
end
end
end
if warpedMonster and (self._warpedMonsterText ~= nil) then
if common.spamProtect(npcChar, 5) then
return
end
local german, english = self._warpedMonsterText:getRandomMessage()
local textTypeDe, textDe = _getTextAndTalktype(german)
local textTypeEn, textEn = _getTextAndTalktype(english)
if (textTypeDe ~= textTypeEn) then
debug("NPC "..npcChar.name.." has a warped monster text that yields different text types for German and English.\nGerman: "..textDe.."\nEnglish:"..textEn)
end
npcChar:talk(textTypeDe, textDe, textEn)
end
end
function _processPlayers(self, npcChar, radius)
if (self._parent._affiliation <= 0) then
return
end
local playerList = world:getPlayersInRangeOf(npcChar.pos, radius)
local warpedPlayers = false
local hitPlayers = false
for _, player in pairs(playerList) do
if _isPointInGuardArea(self, npcChar, player.pos) then
local relation = factions.getPlayerRelation(player, self._parent._affiliation)
if (relation == factions.RELATION_AGGRESSIVE) then
character.ChangeHP(player, -1000)
hitPlayers = true
warpedPlayers = true
_warpHostile(self, npcChar, player)
elseif (relation == factions.RELATION_HOSTILE) --[[or ((relation == factions.RELATION_NEUTRAL) and player.attackmode)]] then
warpedPlayers = true
_warpHostile(self, npcChar, player)
end
end
end
if hitPlayers then
world:makeSound(3, npcChar.pos)
if (self._hitPlayerText ~= nil) then
local german, english = self._hitPlayerText:getRandomMessage()
local textTypeDe, textDe = _getTextAndTalktype(german)
local textTypeEn, textEn = _getTextAndTalktype(english)
if (textTypeDe ~= textTypeEn) then
debug("NPC "..npcChar.name.." has a hit player text that yields different text types for German and English.\nGerman: "..textDe.."\nEnglish:"..textEn)
end
npcChar:talk(textTypeDe, textDe, textEn)
warpedPlayers = false
end
end
if warpedPlayers and (self._warpedPlayerText ~= nil) then
local german, english = self._warpedPlayerText:getRandomMessage()
local textTypeDe, textDe = _getTextAndTalktype(german)
local textTypeEn, textEn = _getTextAndTalktype(english)
if (textTypeDe ~= textTypeEn) then
debug("NPC "..npcChar.name.." has a warped player text that yields different text types for German and English.\nGerman: "..textDe.."\nEnglish:"..textEn)
end
npcChar:talk(textTypeDe, textDe, textEn)
end
end
function _warpHostile(self, npcChar, hostile)
if (self._warpLocation == nil) then
self._warpLocation = hostile.pos
debug("NPC "..npcChar.name.." is a guard with missing warp target location.")
end
hostile:warp(self._warpLocation)
end
function _getCheckRadius(self)
return math.max(self._guardRange.south, self._guardRange.north) + math.max(self._guardRange.west, self._guardRange.east)
end
function _isPointInGuardArea(self, npcChar, checkPoint)
if (checkPoint.z ~= npcChar.pos.z) then
return false
end
local npcX = npcChar.pos.x
local npcY = npcChar.pos.y
local farNorth = npcY - self._guardRange.north
local farSouth = npcY + self._guardRange.south
local farWest = npcX - self._guardRange.west
local farEast = npcX + self._guardRange.east
if (checkPoint.x < farWest) or (checkPoint.x > farEast) then
return false
end
if (checkPoint.y < farNorth) or (checkPoint.y > farSouth) then
return false
end
return true
end
function _getTextAndTalktype(text)
if (string.find(text, "[#/]w") == 1) then
return Character.whisper, string.gsub(text, "[#/]w%s*", "", 1)
elseif (string.find(text, "[#/]s") == 1) then
return Character.yell, string.gsub(text, "[#/]s%s*", "", 1)
elseif (string.find(text, "[#/]o") == 1) then
return Character.whisper, text
else
return Character.say, text
end
end
return guardNPC