2014-01-04 15:39:52 +01:00
--[[
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
2014-08-09 14:47:23 +02:00
with this program . If not , see < http : // www.gnu . org / licenses /> .
2014-01-04 15:39:52 +01:00
] ]
2010-05-25 15:12:51 +00:00
-- Generic Routine Collection
2014-09-19 21:15:18 +02:00
local M = { }
2010-05-25 15:12:51 +00:00
2017-06-11 14:16:34 +02:00
2010-07-22 22:23:51 +00:00
--- Select the proper text upon the language flag of the character
-- @param User The character who's language flag matters
-- @param textInDe german text
-- @param textInEn english text
-- @return Text german or english text version
2012-07-24 18:33:40 +02:00
2014-09-19 21:15:18 +02:00
function M . GetNLS ( User , textInDe , textInEn )
2016-03-29 21:06:15 +02:00
return ( User : getPlayerLanguage ( ) == Player.german and textInDe or textInEn )
end
2010-05-25 15:12:51 +00:00
2010-07-22 22:23:51 +00:00
--- Send a inform to the player in the language the player speaks
-- @param User The character who receives the message
-- @param textInDe german text
-- @param textInEn english text
2012-07-22 21:22:14 +02:00
-- @param informPriority Player.[low|medium|high]Priority
2012-07-23 22:23:57 +02:00
-- Default: Medium priority
2014-09-19 21:15:18 +02:00
function M . InformNLS ( User , textInDe , textInEn )
2012-09-13 00:49:19 +02:00
User : inform ( textInDe , textInEn )
end
2012-07-23 22:23:57 +02:00
-- Temp: Low priority
2014-09-19 21:15:18 +02:00
function M . TempInformNLS ( User , textInDe , textInEn )
2012-09-13 00:49:19 +02:00
User : inform ( textInDe , textInEn , Player.lowPriority )
end
2010-05-25 15:12:51 +00:00
2012-07-23 22:23:57 +02:00
-- High: High priority
2014-09-19 21:15:18 +02:00
function M . HighInformNLS ( User , textInDe , textInEn )
2012-09-13 00:49:19 +02:00
User : inform ( textInDe , textInEn , Player.highPriority )
end
2012-07-23 22:00:03 +02:00
2014-09-19 21:15:18 +02:00
function M . TalkNLS ( User , method , textInDe , textInEN )
2013-02-26 03:52:01 +01:00
User : talk ( method , textInDe , textInEN )
2015-01-17 11:06:36 +01:00
end
2010-09-26 17:07:45 +00:00
2018-04-03 19:58:17 +03:00
---
2018-05-07 19:02:20 +03:00
-- Notify user about an error, write it to log and raise exception (stop the execution)
2018-04-03 19:58:17 +03:00
-- @param User The character who receives the message
-- @param message The error message as string. In English. Should end with "."
function M . informError ( User , message )
User : inform ( " [ERROR] " .. message .. " Please inform a developer. " )
error ( message )
end
---
-- Show selection dialog with defined properties
-- @param User - the character to receive the dialog
-- @param title - string - the dialog title
-- @param description - string - the text below the title
-- @param buttons - a list of tables that define dialog options, with following fields:
-- icon - itemId of the icon to be displayed
-- text - text to be displayed
-- func - function to be called if this button is pressed, if nil than nothing is done
-- args - arguments to be passed to func
-- @param onclose - a table that defines what happens if dialog is closed, if nil than nothing is done
-- func - function to be called
-- args - arguments to be passed to func
-- @param closeOnMove - whether dialog closes when char moves, default is `true`
--
function M . selectionDialogWrapper ( User , title , description , buttons , onclose , closeOnMove )
onclose = onclose or { func = function ( ) end , args = { } }
2018-08-22 08:18:51 +03:00
if closeOnMove == nil then
closeOnMove = true
end
2018-04-03 19:58:17 +03:00
-- User:inform("#buttons = " .. #buttons)
buttons = buttons or { }
local callback = function ( dialog )
if ( not dialog : getSuccess ( ) ) then
onclose.func ( table.unpack ( onclose.args ) )
return
end
local index = dialog : getSelectedIndex ( ) + 1
if index >= 1 and index <= # buttons then
if buttons [ index ] . func ~= nil then
buttons [ index ] . func ( table.unpack ( buttons [ index ] . args ) )
end
else
User : inform ( " [ERROR] No valid function. Please inform a developer. " )
end
end
local sd = SelectionDialog ( title , description , callback )
for _ , btn in ipairs ( buttons ) do
local icon = btn.icon or 0
local text = btn.text or " "
sd : addOption ( icon , text )
end
if closeOnMove then
sd : setCloseOnMove ( )
end
User : requestSelectionDialog ( sd )
end
2026-01-08 14:54:09 +01:00
function M . tableToString ( user , theTable )
2025-12-27 21:11:54 +01:00
local theString = " "
for _ , tableEntry in ipairs ( theTable ) do
if theString ~= " " then
theString = theString .. " , "
end
2026-01-08 14:54:09 +01:00
theString = theString .. tableEntry.key .. " , " .. tableEntry.value
2025-12-27 21:11:54 +01:00
end
2026-01-08 14:54:09 +01:00
return theString
end
function M . convertTableToItemData ( user , theItem , theTable , dataKey , maxData )
local theString = M.tableToString ( user , theTable )
2025-12-27 21:11:54 +01:00
for i = 1 , maxData do
theItem : setData ( dataKey .. i , string.sub ( theString , 1 + ( 250 * ( i - 1 ) ) , 250 * i ) )
end
world : changeItem ( theItem )
end
2026-01-08 18:18:27 +01:00
function M . convertItemDataToTable ( user , theItem , dataKey , maxData , data )
2025-12-27 21:11:54 +01:00
local theString = " "
2026-01-08 18:18:27 +01:00
if theItem then
for i = 1 , maxData do
theString = theString .. theItem : getData ( dataKey .. i )
end
elseif data then
for i = 1 , maxData do
theString = theString .. data [ dataKey .. i ]
end
2025-12-27 21:11:54 +01:00
end
local retTable = { }
local parts = { }
for part in string.gmatch ( theString , " ([^,]+) " ) do
table.insert ( parts , part )
end
for i = 1 , # parts , 2 do
local foundKey = parts [ i ]
local foundValue = parts [ i + 1 ]
if foundKey and foundValue then
2026-01-08 14:54:09 +01:00
if tonumber ( foundValue ) then
foundValue = tonumber ( foundValue )
end
2025-12-27 21:11:54 +01:00
table.insert ( retTable , { key = foundKey , value = foundValue } )
end
end
return retTable
end
2018-04-03 19:58:17 +03:00
2010-09-26 17:07:45 +00:00
--- Get a text based on the gender of the character.
-- @param User The character used to choose the text
-- @param textMale The text returned in case the character is male
-- @param textFemale The text returned in case the character is female
-- @return textMale or textFemale, based on the gender of User
2014-09-19 21:15:18 +02:00
function M . GetGenderText ( User , textMale , textFemale )
2016-03-29 21:06:15 +02:00
return ( User : increaseAttrib ( " sex " , 0 ) == 1 and textFemale or textMale )
end
2010-09-26 17:07:45 +00:00
2017-11-17 11:51:04 +01:00
--[[ Get the frst letter in a text uppercase.
@ param text : " the text "
@ return text : " The text " ] ] --
function M . firstToUpper ( text )
if M.IsNilOrEmpty ( text ) then
return text
end
text = tostring ( text )
return ( text : gsub ( " ^%l " , string.upper ) )
end
2026-01-08 18:18:27 +01:00
function M . checkIfBookInHand ( user , alchemy , cooking )
local bookList = { 1061 , 105 , 106 , 129 , 2609 , 2610 , 114 , 115 , 2608 , 2615 , 107 , 108 , 111 , 112 , 2605 , 2617 , 109 , 110 , 117 , 128 , 130 , 2604 , 131 , 2602 , 2620 , 116 , 2621 , 2607 , 127 , 2598 , 2606 , 2616 , 2619 }
local leftTool = user : getItemAt ( Character.left_tool )
local rightTool = user : getItemAt ( Character.right_tool )
local potentialBooks = { leftTool , rightTool }
for _ , book in pairs ( potentialBooks ) do
local isBook = M.isInList ( book.id , bookList )
local isCookBook = not M.IsNilOrEmpty ( book : getData ( " cookBook " ) )
local isAlchemyBook = not M.IsNilOrEmpty ( book : getData ( " alchemyBook " ) )
local availableSlots = M.IsNilOrEmpty ( book : getData ( " recipe100 " ) )
local isEmpty = M.IsNilOrEmpty ( book : getData ( " sheet1 " ) ) and M.IsNilOrEmpty ( book : getData ( " page1 " ) ) and M.IsNilOrEmpty ( book : getData ( " magicBook " ) ) and M.IsNilOrEmpty ( book : getData ( " book " ) ) and M.IsNilOrEmpty ( book : getData ( " alchemyBook " ) )
if isBook and ( ( isAlchemyBook and alchemy ) or ( isCookBook and cooking ) or isEmpty ) and availableSlots then -- it is a book
if book.number > 1 then
user : inform ( " Du kannst nur in einem Buch gleichzeitig schreiben. " , " You can only write in one book at a time. " )
return false
end
return book
end
end
user : inform ( " Du brauchst ein Buch in der Hand, wenn du es beschriften oder darin schreiben m<> chtest. " , " You need a book in your hand if you want to label or write in one. " )
return false
end
2018-07-18 17:54:14 +02:00
--[[ Replace Umlaute by possible character.
@ param text : " the text with an <20> . "
@ return text : " the text with an ae. " ] ] --
function M . replaceUmlaut ( text )
if M.IsNilOrEmpty ( text ) then
return text
end
text = tostring ( text )
text = text : gsub ( " <EFBFBD> " , " ae " )
text = text : gsub ( " <EFBFBD> " , " oe " )
text = text : gsub ( " <EFBFBD> " , " ue " )
text = text : gsub ( " <EFBFBD> " , " Ae " )
text = text : gsub ( " <EFBFBD> " , " Oe " )
text = text : gsub ( " <EFBFBD> " , " Ue " )
text = text : gsub ( " <EFBFBD> " , " ss " )
return text
end
2010-09-26 17:07:45 +00:00
--- This function uses the random value generator to produce a random chance.
-- This function returns true in a specified amount of cases. The optional
-- Base parameter is used to set the value of 100%. By default this value is 1.
-- For example in case the value is set to 80 and the base is set to 100 the
-- function will return true in 80% of the calls. The same result would be
-- produced in case the value is set to 8 and the base is set to 10 or in case
-- the Value is set to 0.8 and the base is not set, or set to 1.
-- @param Value The chance value
-- @param Base The value of 100% (optional)
-- @return true in (Value/Base*100)% of the calls
2014-09-19 21:15:18 +02:00
function M . Chance ( Value , Base )
2010-09-29 18:22:03 +00:00
if ( Base ~= nil and Value >= Base ) or ( Base == nil and Value >= 1 ) then
2016-03-29 21:06:15 +02:00
return true
end
2010-09-29 09:15:02 +00:00
if ( Value <= 0 ) then
2016-03-29 21:06:15 +02:00
return false
end
2014-08-09 14:47:23 +02:00
2010-09-26 17:07:45 +00:00
if ( Base ~= nil ) and ( Base ~= 1 ) then
2016-03-29 21:06:15 +02:00
Value = Value / Base
end
2014-08-09 14:47:23 +02:00
2016-03-29 21:06:15 +02:00
return ( math.random ( ) <= Value )
end
2010-05-25 15:12:51 +00:00
2022-02-15 15:09:58 +01:00
---This function returns a value based on a binomial distribution.
-- Consider this function rolling dices against a minimum value
-- Mean value = probability * rolls
function M . BinomialByProbability ( probability , rolls )
local result = 0
for i = 1 , rolls do
if math.random ( ) < probability then
result = result + 1
end
end
return result
end
function M . BinomialByMean ( mean , rolls )
local probability = mean / rolls
return M.BinomialByProbability ( probability , rolls )
end
2010-08-13 09:44:08 +00:00
--- Determine if a character is looking at a position or not
-- @param User The character whos looking direction matters
-- @param Location The position the character should look at
-- @return true in case the character looks at the position, else false
2014-09-19 21:15:18 +02:00
function M . IsLookingAt ( User , Location )
2010-05-25 15:12:51 +00:00
if not User or not Location then
2016-03-29 21:06:15 +02:00
return false
end
2010-05-25 15:12:51 +00:00
2012-10-27 18:09:13 +02:00
if User.pos == Location then
2016-03-29 21:06:15 +02:00
return true
2010-05-25 15:12:51 +00:00
end
2016-03-29 21:06:15 +02:00
local richtung = User : getFaceTo ( )
2014-08-09 14:47:23 +02:00
2011-07-03 22:41:57 +00:00
return ( ( ( richtung == Character.north ) and ( Location.y < User.pos . y ) ) or
( ( richtung == Character.northeast ) and
2013-01-27 15:30:17 +01:00
( ( Location.y < User.pos . y ) and ( Location.x > User.pos . x ) ) ) or
2011-07-03 22:41:57 +00:00
( ( richtung == Character.east ) and ( Location.x > User.pos . x ) ) or
( ( richtung == Character.southeast ) and
2013-01-27 15:28:47 +01:00
( ( Location.y > User.pos . y ) and ( Location.x > User.pos . x ) ) ) or
2011-07-03 22:41:57 +00:00
( ( richtung == Character.south ) and ( Location.y > User.pos . y ) ) or
( ( richtung == Character.southwest ) and
2013-01-27 15:30:17 +01:00
( ( Location.y > User.pos . y ) and ( Location.x < User.pos . x ) ) ) or
2011-07-03 22:41:57 +00:00
( ( richtung == Character.west ) and ( Location.x < User.pos . x ) ) or
( ( richtung == Character.northwest ) and
2016-03-29 21:06:15 +02:00
( ( Location.y < User.pos . y ) and ( Location.x < User.pos . x ) ) ) )
end
2010-05-25 15:12:51 +00:00
2012-09-13 22:47:30 +02:00
--- Check if a character sequence (string) is nil or empty.
-- @param the variable to check
-- @return true in case the text is nil or equal to a empty string
2014-09-19 21:15:18 +02:00
function M . IsNilOrEmpty ( text )
2016-03-29 21:06:15 +02:00
return ( ( text == nil ) or ( text == " " ) )
end
2012-09-13 22:46:20 +02:00
2010-08-13 09:44:08 +00:00
--- Determine the direction from one position to another one
-- @param StartPosition The start position. The place you get the direction you have to look towards to see the target location
-- @param TargetPosition The target position
2011-07-03 22:41:57 +00:00
-- @return value for the direction, possible values are: Character.dir_north, Character.dir_northeast, Character.dir_east, Character.dir_southeast, Character.dir_south, Character.dir_southwest, Character.dir_west, Character.dir_northwest
2014-09-19 21:15:18 +02:00
function M . GetDirection ( StartPosition , TargetPosition )
2010-05-25 15:12:51 +00:00
if ( StartPosition.z < TargetPosition.z ) then
2016-03-29 21:06:15 +02:00
return Character.dir_up
2010-05-25 15:12:51 +00:00
elseif ( StartPosition.z > TargetPosition.z ) then
2016-03-29 21:06:15 +02:00
return Character.dir_down
2010-05-25 15:12:51 +00:00
end
2016-03-29 21:06:15 +02:00
local phi = M.GetPhi ( StartPosition , TargetPosition )
2010-05-25 15:12:51 +00:00
if ( phi < math.pi / 8 ) then
2016-03-29 21:06:15 +02:00
return Character.dir_east
2010-05-25 15:12:51 +00:00
elseif ( phi < 3 * math.pi / 8 ) then
2016-03-29 21:06:15 +02:00
return Character.dir_southeast
2010-05-25 15:12:51 +00:00
elseif ( phi < 5 * math.pi / 8 ) then
2016-03-29 21:06:15 +02:00
return Character.dir_south
2010-05-25 15:12:51 +00:00
elseif ( phi < 7 * math.pi / 8 ) then
2016-03-29 21:06:15 +02:00
return Character.dir_southwest
2010-05-25 15:12:51 +00:00
elseif ( phi < 9 * math.pi / 8 ) then
2016-03-29 21:06:15 +02:00
return Character.dir_west
2010-05-25 15:12:51 +00:00
elseif ( phi < 11 * math.pi / 8 ) then
2016-03-29 21:06:15 +02:00
return Character.dir_northwest
2010-05-25 15:12:51 +00:00
elseif ( phi < 13 * math.pi / 8 ) then
2016-03-29 21:06:15 +02:00
return Character.dir_north
2010-05-25 15:12:51 +00:00
elseif ( phi < 15 * math.pi / 8 ) then
2016-03-29 21:06:15 +02:00
return Character.dir_northeast
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
return Character.dir_east
end
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Turn a character towards a location
-- @param User The character who shall be turned
-- @param Location The position the character shall turn to
2014-09-19 21:15:18 +02:00
function M . TurnTo ( User , Location )
2016-03-29 21:06:15 +02:00
local oldDir = User : getFaceTo ( )
local newDir = M.GetDirection ( User.pos , Location )
2013-01-27 15:12:49 +01:00
2010-05-25 15:12:51 +00:00
if ( newDir ~= oldDir ) then
2016-03-29 21:06:15 +02:00
User : turn ( newDir )
end
end
2010-05-25 15:12:51 +00:00
2014-11-30 00:32:31 +01:00
function M . GetDirectionVector ( dir )
if dir == Character.dir_up then
return 0 , 0 , 1
elseif dir == Character.dir_down then
return 0 , 0 , - 1
elseif dir == Character.north then
return 0 , - 1 , 0
elseif dir == Character.northeast then
return 1 , - 1 , 0
elseif dir == Character.east then
return 1 , 0 , 0
elseif dir == Character.southeast then
return 1 , 1 , 0
elseif dir == Character.south then
return 0 , 1 , 0
elseif dir == Character.southwest then
return - 1 , 1 , 0
elseif dir == Character.west then
return - 1 , 0 , 0
elseif dir == Character.northwest then
return - 1 , - 1 , 0
else
error ( " Unexpected value for parameter \" dir \" " )
end
end
2010-08-13 09:44:08 +00:00
--- Get the position right in front of a character in looking direction
-- @param User The character the front position is wanted
-- @return The position in front of the character
2014-09-19 21:15:18 +02:00
function M . GetFrontPosition ( User , distance , dir )
2014-11-30 00:32:31 +01:00
local direct = dir or User : getFaceTo ( )
local d = distance or 1
local vX , vY = M.GetDirectionVector ( direct )
2010-05-25 15:12:51 +00:00
2014-11-30 00:32:31 +01:00
return position ( User.pos . x + vX * d , User.pos . y + vY * d , User.pos . z )
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Get the item that is in front of the character in case there is one
-- @param User The character whos front area is searched
-- @return The item that was found or nil
2014-09-19 21:15:18 +02:00
function M . GetFrontItem ( User , dir )
2016-03-29 21:06:15 +02:00
local Posi = M.GetFrontPosition ( User , 1 , dir )
2010-05-25 15:12:51 +00:00
if world : isItemOnField ( Posi ) then
2016-03-29 21:06:15 +02:00
return world : getItemOnField ( Posi )
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
return nil
end
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Get the ID of the item that is in front of the character in case there is one
-- @param User The character whos front area is searched
-- @return The ID of the item in front of the character or 0 in case there is none
2014-09-19 21:15:18 +02:00
function M . GetFrontItemID ( User , dir )
2016-03-29 21:06:15 +02:00
local theItem = M.GetFrontItem ( User , dir )
2010-05-25 15:12:51 +00:00
if not theItem then
2016-03-29 21:06:15 +02:00
return 0
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
return theItem.id
end
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Get character who is in front of the character in case there is one
-- @param User The character whos front area is searched
-- @return The character in front of the parameter character or nil
2014-09-19 21:15:18 +02:00
function M . GetFrontCharacter ( User )
2016-03-29 21:06:15 +02:00
local Posi = M.GetFrontPosition ( User )
2011-04-14 16:20:16 +00:00
if world : isCharacterOnField ( Posi ) then
2016-03-29 21:06:15 +02:00
return world : getCharacterOnField ( Posi )
2011-04-14 16:20:16 +00:00
else
2016-03-29 21:06:15 +02:00
return nil
end
end
2011-04-14 16:20:16 +00:00
2014-08-09 14:47:23 +02:00
--- Get the position right behind a character
2011-04-14 16:20:16 +00:00
-- @param User The character the back position is wanted
2011-07-05 15:34:12 +00:00
-- @param distance How far behind the char? Defaults to 1.
2011-04-14 16:20:16 +00:00
-- @return The position behind the character
2014-09-19 21:15:18 +02:00
function M . GetBehindPosition ( User , distance )
2011-04-14 17:05:57 +00:00
2016-03-29 21:06:15 +02:00
local d = distance or 1
local direct = User : getFaceTo ( )
2011-04-14 16:20:16 +00:00
2011-07-03 22:41:57 +00:00
if ( direct == Character.north ) then
2016-03-29 21:06:15 +02:00
return position ( User.pos . x , User.pos . y + d , User.pos . z )
2011-07-03 22:41:57 +00:00
elseif ( direct == Character.northeast ) then
2016-03-29 21:06:15 +02:00
return position ( User.pos . x - d , User.pos . y + d , User.pos . z )
2011-07-03 22:41:57 +00:00
elseif ( direct == Character.east ) then
2016-03-29 21:06:15 +02:00
return position ( User.pos . x - d , User.pos . y , User.pos . z )
2011-07-03 22:41:57 +00:00
elseif ( direct == Character.southeast ) then
2016-03-29 21:06:15 +02:00
return position ( User.pos . x + d , User.pos . y - d , User.pos . z )
2011-07-03 22:41:57 +00:00
elseif ( direct == Character.south ) then
2016-03-29 21:06:15 +02:00
return position ( User.pos . x , User.pos . y - d , User.pos . z )
2011-07-03 22:41:57 +00:00
elseif ( direct == Character.southwest ) then
2016-03-29 21:06:15 +02:00
return position ( User.pos . x + d , User.pos . y - d , User.pos . z )
2011-07-03 22:41:57 +00:00
elseif ( direct == Character.west ) then
2016-03-29 21:06:15 +02:00
return position ( User.pos . x + d , User.pos . y , User.pos . z )
2011-07-03 22:41:57 +00:00
elseif ( direct == Character.northwest ) then
2016-03-29 21:06:15 +02:00
return position ( User.pos . x + d , User.pos . y + d , User.pos . z )
end
2011-04-14 16:20:16 +00:00
2016-03-29 21:06:15 +02:00
debug ( " Invalid user direction " )
end
2011-04-14 16:20:16 +00:00
--- Get the item that is behind the character in case there is one
-- @param User The character whos back area is searched
-- @return The item that was found or nil
2014-09-19 21:15:18 +02:00
function M . GetBehindItem ( User )
2016-03-29 21:06:15 +02:00
local Posi = M.GetBehindPosition ( User )
2011-04-14 16:20:16 +00:00
if world : isItemOnField ( Posi ) then
2016-03-29 21:06:15 +02:00
return world : getItemOnField ( Posi )
2011-04-14 16:20:16 +00:00
else
2016-03-29 21:06:15 +02:00
return nil
end
end
2011-04-14 16:20:16 +00:00
--- Get the ID of the item that is behind the character in case there is one
-- @param User The character whos back area is searched
-- @return The ID of the item in front of the character or 0 in case there is none
2014-09-19 21:15:18 +02:00
function M . GetBehindItemID ( User )
2016-03-29 21:06:15 +02:00
local theItem = M.GetBehindItem ( User )
2011-04-14 16:20:16 +00:00
if not theItem then
2016-03-29 21:06:15 +02:00
return 0
2011-04-14 16:20:16 +00:00
else
2016-03-29 21:06:15 +02:00
return theItem.id
end
end
2011-04-14 16:20:16 +00:00
--- Get character who is behind the character in case there is one
-- @param User The character whos front area is searched
-- @return The character in front of the parameter character or nil
2014-09-19 21:15:18 +02:00
function M . GetBehindCharacter ( User )
2016-03-29 21:06:15 +02:00
local Posi = M.GetBehindPosition ( User )
2010-05-25 15:12:51 +00:00
if world : isCharacterOnField ( Posi ) then
2016-03-29 21:06:15 +02:00
return world : getCharacterOnField ( Posi )
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
return nil
end
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Check if a character is within a area determined with 2 positions. The
-- rectangle of the area is determined by the grid of the tiles
-- @param User The character who is checked for being within the area
-- @param Pos1 The first position to determine the area
-- @param Pos2 The second position to determine the area
-- @return True if the character is in the area, false if not
2014-09-19 21:15:18 +02:00
function M . GetInArea ( User , Pos1 , Pos2 )
2010-08-13 09:44:08 +00:00
if ( User.pos . x <= math.min ( Pos1.x , Pos2.x ) or
User.pos . x >= math.max ( Pos1.x , Pos2.x ) ) then
2016-03-29 21:06:15 +02:00
return false
2010-08-13 09:44:08 +00:00
elseif ( User.pos . y <= math.min ( Pos1.y , Pos2.y ) or
User.pos . y >= math.max ( Pos1.y , Pos2.y ) ) then
2016-03-29 21:06:15 +02:00
return false
2010-08-13 09:44:08 +00:00
elseif ( User.pos . z <= math.min ( Pos1.z , Pos2.z ) or
User.pos . z >= math.max ( Pos1.z , Pos2.z ) ) then
2016-03-29 21:06:15 +02:00
return false
2010-05-25 15:12:51 +00:00
end
2016-03-29 21:06:15 +02:00
return true
2010-05-25 15:12:51 +00:00
end
2015-01-03 18:11:37 +01:00
--- Get all free positions at and around a center location. The search is performed with a square mask. So the search
-- area is actually a rectangle with the with a side length of searchRadius + 1
-- @param centerPosition the center of the search
-- @param searchRadius the radius of the search
-- @param allowPassableItems this has to be set to true in case tiles that contain items that are passable are valid.
-- in case this is set to false. tiles that do contain items are in general invalid
-- (optional, defaults to false)
-- @param shuffle randomize the order the tiles are returned in (optional, defaults to false)
-- @return a function that can be used as iterator that gives all the free locations one by one
function M . GetFreePositions ( centerPosition , searchRadius , allowPassableItems , shuffle )
2015-01-26 18:34:53 +01:00
local coords = { }
for x = - searchRadius , searchRadius do
for y = - searchRadius , searchRadius do
table.insert ( coords , { centerPosition.x + x , centerPosition.y + y } )
end
2015-01-03 18:11:37 +01:00
end
if shuffle then
2015-01-26 18:34:53 +01:00
coords = M.Shuffle ( coords )
2015-01-03 18:11:37 +01:00
end
2014-09-06 14:33:52 +02:00
2015-01-26 18:34:53 +01:00
local currentIndex = 1
local lastIndex = # coords
2015-01-03 18:11:37 +01:00
return function ( )
2015-01-26 18:34:53 +01:00
while currentIndex <= lastIndex do
local xyCoords = coords [ currentIndex ]
local targetPos = position ( xyCoords [ 1 ] , xyCoords [ 2 ] , centerPosition.z )
2015-01-03 18:11:37 +01:00
currentIndex = currentIndex + 1
local field = world : getField ( targetPos )
if field ~= nil then
if field : isPassable ( ) and not world : isCharacterOnField ( targetPos ) then
if allowPassableItems or not world : isItemOnField ( targetPos ) then
return targetPos
end
end
end
end
return nil
end
end
-- Get one random free location
2018-07-18 17:54:14 +02:00
function M . getFreePos ( CenterPos , Rad )
2015-03-12 21:09:10 +01:00
local pos = M.GetFreePositions ( CenterPos , Rad , false , true ) ( )
2015-01-03 18:11:37 +01:00
if pos == nil then
return CenterPos
end
return pos
2014-08-09 14:47:23 +02:00
end
2017-11-17 11:51:04 +01:00
--Counts passable fields around a center location. The search is performed with a square mask. So the search
-- area is actually a rectangle with the with a side length of searchRadius + 2
-- @param centerPosition the center of the search
-- @param searchRadius the radius of the search
-- @return number of passable fields
function M . getNumberOfPassableFieldsInArea ( centerPos , searchRadius )
local countPos = 0
for x = - searchRadius , searchRadius do
for y = - searchRadius , searchRadius do
local pos = position ( centerPos.x + x , centerPos.y + y , centerPos.z )
local field = world : getField ( pos )
if field ~= nil then
if field : isPassable ( ) then
countPos = countPos + 1
end
end
end
end
return countPos
end
--Counts passable fields around a center location. The search is performed with a given relativ mask.
-- @param centerPos the center of the search
-- @param relativePoss table of reltive positions
-- @return number of passable fields
function M . getNumberOfPassableFieldsFromList ( centerPos , relativePos )
local countPos = 0
if relativePos == nil or # relativePos == 0 then
return 0
else
for i = 1 , # relativePos do
local pos = position ( centerPos.x + relativePos [ i ] [ 1 ] , centerPos.y + relativePos [ i ] [ 2 ] , centerPos.z + relativePos [ i ] [ 3 ] )
local field = world : getField ( pos )
if field ~= nil then
if field : isPassable ( ) then
countPos = countPos + 1
end
end
end
end
return countPos
end
2010-08-13 09:44:08 +00:00
--- Check if a ItemStruct is valid for a special character
-- @param User Character who should own the item
-- @param Item Item that shall be checked if its still valid
-- @param altIDs alternative ItemIDs the item could have changed to and is still valid
-- @return True if everything is fine, else false
2014-09-19 21:15:18 +02:00
function M . CheckItem ( User , Item , altIDs )
2015-01-17 11:06:36 +01:00
local ItemCheck
2013-01-25 00:26:34 +01:00
if Item : getType ( ) == scriptItem.field then
2010-05-25 15:12:51 +00:00
if world : isItemOnField ( Item.pos ) then
2013-01-25 00:26:34 +01:00
ItemCheck = world : getItemOnField ( Item.pos )
end
elseif Item : getType ( ) == scriptItem.container then
2015-01-17 11:06:36 +01:00
local found
2013-01-25 00:26:34 +01:00
found , ItemCheck = User : getBackPack ( ) : viewItemNr ( Item.itempos )
if not found then
ItemCheck = nil
end
2010-05-25 15:12:51 +00:00
else
2013-01-25 00:26:34 +01:00
ItemCheck = User : getItemAt ( Item.itempos )
end
2010-05-25 15:12:51 +00:00
if ItemCheck then
2013-01-25 00:26:34 +01:00
if Item.id == ItemCheck.id then
return true
elseif altIDs ~= nil then
2010-05-25 15:12:51 +00:00
for _ , altID in pairs ( altIDs ) do
2013-01-25 00:26:34 +01:00
if ItemCheck.id == altID then
return true
end
end
end
end
return false
2016-03-29 21:06:15 +02:00
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Checks if the Character has a minimal default amount of food points. If not
-- it prints out a inform message.
-- @param User The character whos foodpoints are checked
-- @return True in case the character has enougth food points, false if not
2014-09-19 21:15:18 +02:00
function M . FitForWork ( User )
2016-03-29 21:06:15 +02:00
return M.FitForHardWork ( User , 1000 )
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Checks if the Character has a minimal amount of food points. If not it
-- prints out a inform message-
-- @param User The character whos foodpoints are checked
-- @param required Amount of foodpoints that are required
-- @return True in case the character has enougth food points, false if not
2014-09-19 21:15:18 +02:00
function M . FitForHardWork ( User , required )
2016-07-29 18:24:20 +02:00
2016-07-29 18:54:33 +02:00
if ( User : increaseAttrib ( " foodlevel " , 0 ) < required ) or ( User : increaseAttrib ( " foodlevel " , 0 ) < 1000 ) then
2014-09-25 06:02:44 +02:00
M.InformNLS ( User ,
2016-07-22 21:56:43 +02:00
" Du bist daf<61> r zu ersch<63> pft. Du solltest etwas essen. " ,
" You are too exhausted for that. You should eat something. " )
2016-03-29 21:06:15 +02:00
return false
end
2020-12-06 23:34:30 +01:00
2016-03-29 21:06:15 +02:00
return true
2020-12-06 23:34:30 +01:00
2016-03-29 21:06:15 +02:00
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Decrease the foodpoints of a character and show a warning if the foodpoints
-- are running low.
-- @param User The character that loses foodpoints
-- @param units The amount of foodpoints that are removed
2014-09-19 21:15:18 +02:00
function M . GetHungry ( User , units )
2016-07-29 18:24:20 +02:00
2016-03-29 21:06:15 +02:00
local food = User : increaseAttrib ( " foodlevel " , - units )
2020-12-06 23:34:30 +01:00
2016-08-06 08:46:18 +02:00
if ( food <= 6000 ) and ( ( food + units ) > 6000 ) then
2014-09-25 06:02:44 +02:00
M.InformNLS ( User ,
2016-07-22 21:56:43 +02:00
" Die Arbeit macht dich langsam m<> de und hungrig. " ,
2016-03-29 21:06:15 +02:00
" You are getting tired and hungry from your work. " )
end
2020-12-06 23:34:30 +01:00
2016-03-29 21:06:15 +02:00
end
2010-05-25 15:12:51 +00:00
2017-07-09 13:48:58 +02:00
--[[function set for item quality and durability
item.quality contains quality and durability
637 means : quality = 6 , durability = 37
default value is 333
] ] --
M.ITEM_MAX_QUALITY = 9
M.ITEM_DEFAULT_QUALITY = 3
M.ITEM_MAX_DURABILITY = 99
M.ITEM_DEFAULT_DURABILITY = 33
-- Get durability of an item
-- @param item the item to be checked
2017-07-07 16:49:57 +02:00
-- @return durability (1-99)
2017-07-09 13:48:58 +02:00
function M . getItemDurability ( item )
2017-07-07 16:49:57 +02:00
local durability = math.fmod ( item.quality , 100 )
return durability
end
2020-05-24 13:21:28 +02:00
-- Check if an item is broken (durability 0)
-- @param item the item to be checked
-- @return true or false
function M . isBroken ( item )
local durability = math.fmod ( item.quality , 100 )
2020-12-06 23:34:30 +01:00
if durability == 0 then
2020-05-24 13:21:28 +02:00
return true
else
return false
end
end
2017-07-09 13:48:58 +02:00
-- Set durability of an item
-- @param item the item to be changed
-- @param newDurability (1-99)
function M . setItemDurability ( item , newDurability )
local quality = M.getItemQuality ( item )
item.quality = M.calculateItemQualityDurability ( quality , newDurability )
world : changeItem ( item )
end
-- Get quality of an item
2017-07-07 16:49:57 +02:00
-- @param Item the item to be checked
-- @return quality (1-9)
2017-07-17 09:30:33 +02:00
function M . getItemQuality ( item )
2017-07-07 16:49:57 +02:00
local durability = math.fmod ( item.quality , 100 )
local quality = ( item.quality - durability ) / 100
return quality
end
2017-07-09 13:48:58 +02:00
-- Set quality of an item
-- @param item the item to be changed
-- @param newQuality (1-9)
function M . setItemQuality ( item , newQuality )
local durability = M.getItemDurability ( item )
item.quality = M.calculateItemQualityDurability ( newQuality , durability )
world : changeItem ( item )
end
-- Set quality and durability of an item
-- @param item the item to be checked
-- @param newQuality (1-9)
-- @param newDurability (1-99)
function M . setItemQualityDurability ( item , newQuality , newDurability )
item.quality = M.calculateItemQualityDurability ( newQuality , newDurability )
world : changeItem ( item )
end
-- calculates and verify the item state for item.quality
-- @param quality (1-9)
-- @param durability (1-99)
-- @return value for item.quality
-- default value is 333
-- calculateItemQualityDurability (nil, nil) = 333
function M . calculateItemQualityDurability ( quality , durability )
local qualityNumber
if M.IsNilOrEmpty ( quality ) then
2020-05-15 17:59:21 +02:00
qualityNumber = M.ITEM_DEFAULT_QUALITY
2017-07-09 13:48:58 +02:00
else
qualityNumber = tonumber ( quality )
end
if qualityNumber < 1 or qualityNumber > M.ITEM_MAX_QUALITY then
2020-05-15 17:59:21 +02:00
qualityNumber = M.ITEM_DEFAULT_QUALITY
2017-07-09 13:48:58 +02:00
end
2020-12-06 23:34:30 +01:00
2021-05-31 15:48:15 +02:00
local durabilityNumber
2017-07-09 13:48:58 +02:00
if M.IsNilOrEmpty ( durability ) then
durabilityNumber = M.ITEM_DEFAULT_DURABILITY
2021-11-04 14:02:37 +01:00
else
durabilityNumber = tonumber ( durability )
2017-07-09 13:48:58 +02:00
end
2025-03-06 13:42:06 +01:00
if durabilityNumber < 0 or durabilityNumber > M.ITEM_MAX_DURABILITY then --We allow 0 now for broken items
2017-07-09 13:48:58 +02:00
durabilityNumber = M.ITEM_DEFAULT_DURABILITY
end
2020-12-06 23:34:30 +01:00
2017-07-09 13:48:58 +02:00
return qualityNumber * 100 + durabilityNumber
end
2012-09-23 18:21:08 +02:00
-- Check if a cooldown for an item is expired (e.g. if using it)
-- Set a new cooldown if the old one is expired (or none set yet)
-- @param Item the item to be checked
-- @param dataKey the key of the data the timestamp is saved in (and the new one is saved in if cooldown ran up)
-- @param cooldownDuration length of the cooldown in seconds
-- @return true if the cooldown is expired (or there was none yet) and new one has been set;
-- false if the cooldown is still valid
2014-09-19 21:15:18 +02:00
function M . ItemCooldown ( User , Item , dataKey , cooldownDuration )
2014-09-25 09:28:50 +02:00
local timeNow = M.GetCurrentTimestamp ( )
2015-06-14 16:01:26 +02:00
local timeThen = tonumber ( Item : getData ( dataKey ) )
if ( timeThen == nil ) or ( ( timeNow - timeThen ) >= cooldownDuration ) then
Item : setData ( dataKey , timeNow )
world : changeItem ( Item )
return true
else
2012-09-23 18:21:08 +02:00
return false
2014-08-09 14:47:23 +02:00
end
2012-09-23 18:21:08 +02:00
end
2010-08-13 09:44:08 +00:00
--- Get a timestamp based on the current server time. Resolution in RL seconds.
-- @return The current timestamp
2014-09-19 21:15:18 +02:00
function M . GetCurrentTimestamp ( )
2025-05-17 11:15:17 +02:00
return M.GetCurrentTimestampForDate ( world : getTime ( " year " ) ,
world : getTime ( " month " ) ,
world : getTime ( " day " ) ,
world : getTime ( " hour " ) ,
world : getTime ( " minute " ) ,
world : getTime ( " second " ) )
2010-05-25 15:12:51 +00:00
end
2010-08-13 09:44:08 +00:00
--- Converts a date into a timestamp in with the resolution in RL seconds.
-- @param year the year of the date to convert
-- @param month the month of the date to convert
-- @param day the day of the date to convert
-- @param hour the hour of the date to convert
-- @param minute the minute of the date to convert
-- @param second the second of the date to convert
-- @return The timestamp of the date
2014-09-19 21:15:18 +02:00
function M . GetCurrentTimestampForDate ( year , month , day , hour , minute , second )
2010-05-25 15:12:51 +00:00
return math.floor (
( ( year - 1 ) * 31536000 + -- (year-1)*((15*24) + 5)*24*60*60;
( month - 1 ) * 2073600 + -- (month-1)*24*24*60*60;
( day - 1 ) * 86400 + -- (day-1)*24*60*60;
hour * 3600 + -- hour*60*60
minute * 60 + -- minute*60
second
) / 3
2016-03-29 21:06:15 +02:00
)
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Convert a timestamp back into a full data. This is the inverse function of
-- GetCurrentTimestamp() and GetCurrentTimestampForData(...).
-- @param timestamp The timestamp that shall be converted
-- @return The year of the resulting data and time
-- @return The month of the resulting data and time
-- @return The day of the resulting data and time
-- @return The hour of the resulting data and time
-- @return The minute of the resulting data and time
-- @return The second of the resulting data and time
2014-09-19 21:15:18 +02:00
function M . TimestampToDate ( timestamp )
2016-03-29 21:06:15 +02:00
local year = math.floor ( timestamp / 31536000 )
timestamp = timestamp - ( year * 31536000 )
2010-05-25 15:12:51 +00:00
--Calculating day
--86400 = 60*60*24
2016-03-29 21:06:15 +02:00
local day = math.floor ( timestamp / 86400 )
timestamp = timestamp - ( day * 86400 )
day = day + 1
2010-05-25 15:12:51 +00:00
-- Calculating month
-- 24 days per month
2016-03-29 21:06:15 +02:00
local month = math.floor ( day / 24 )
day = day - ( month * 24 )
2010-05-25 15:12:51 +00:00
-- checking for range borders and fixing the date
if ( day == 0 ) then
if ( month > 0 and month < 16 ) then
2016-03-29 21:06:15 +02:00
day = 24
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
day = 5
2010-05-25 15:12:51 +00:00
end
else
2016-03-29 21:06:15 +02:00
month = month + 1
2010-05-25 15:12:51 +00:00
end
if ( month == 0 ) then
2016-03-29 21:06:15 +02:00
month = 16
year = year - 1
2010-05-25 15:12:51 +00:00
end
-- Calculate the time of day
-- Calculating hour
-- 3600 = 60 * 60
2016-03-29 21:06:15 +02:00
local hour = math.floor ( timestamp / 3600 )
timestamp = timestamp - ( hour * 3600 )
2010-05-25 15:12:51 +00:00
--Calculating minute
2016-03-29 21:06:15 +02:00
local minute = math.floor ( timestamp / 60 )
2010-05-25 15:12:51 +00:00
--seconds
2016-03-29 21:06:15 +02:00
timestamp = timestamp - ( minute * 60 )
return year , month , day , hour , minute , timestamp
2010-05-25 15:12:51 +00:00
end
2010-08-13 09:44:08 +00:00
--- Determines the type of ground a tile has.
-- @param TileID ID of the tile that should be checked
-- @return The ground typ as one of the following constants: GroundType.unknown, GroundType.field, GroundType.forest, GroundType.sand, GroundType.gras, GroundType.rocks, GroundType.water, GroundType.dirt
2014-09-19 21:15:18 +02:00
function M . GetGroundType ( TileID )
2010-05-25 15:12:51 +00:00
if ( TileID == 4 ) then -- field
2016-03-29 21:06:15 +02:00
return M.GroundType . field
2010-05-25 15:12:51 +00:00
elseif ( TileID == 6 ) then -- water
2016-03-29 21:06:15 +02:00
return M.GroundType . water
2010-05-25 15:12:51 +00:00
elseif ( TileID == 9 ) then -- forest
2016-03-29 21:06:15 +02:00
return M.GroundType . forest
2012-03-01 22:28:45 +01:00
elseif ( TileID == 3 or TileID == 12 ) then -- sand
2016-03-29 21:06:15 +02:00
return M.GroundType . sand
2012-10-02 15:44:00 +02:00
elseif ( TileID == 11 ) then -- grass
2016-03-29 21:06:15 +02:00
return M.GroundType . grass
2010-05-25 15:12:51 +00:00
elseif ( TileID == 2 or TileID == 15 ) then -- rocks
2016-03-29 21:06:15 +02:00
return M.GroundType . rocks
2012-03-01 22:28:45 +01:00
elseif ( TileID == 8 or TileID == 16 ) then -- dirt
2016-03-29 21:06:15 +02:00
return M.GroundType . dirt
2010-08-14 11:30:01 +00:00
elseif ( TileID == 10 ) then -- snow
2016-03-29 21:06:15 +02:00
return M.GroundType . snow
end
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
return M.GroundType . unknown
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- This list contains the constants that are returned by GetGroundType.
-- @class Enumerator
2014-10-17 15:03:26 +02:00
M.GroundType = {
2010-05-25 15:12:51 +00:00
[ " unknown " ] = 0 ,
[ " field " ] = 1 ,
[ " forest " ] = 2 ,
[ " sand " ] = 3 ,
2012-10-02 15:44:00 +02:00
[ " grass " ] = 4 ,
2010-05-25 15:12:51 +00:00
[ " rocks " ] = 5 ,
[ " water " ] = 6 ,
2010-08-14 11:30:01 +00:00
[ " dirt " ] = 7 ,
[ " snow " ] = 8
2016-03-29 21:06:15 +02:00
}
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Create random number with normal distribution.
-- @param minVal Minimal value of the random number range
-- @param maxVal Maximal value of the random number range
-- @return The random number
2014-09-19 21:15:18 +02:00
function M . NormalRnd ( minVal , maxVal )
2016-03-29 21:06:15 +02:00
return M.NormalRnd2 ( minVal , maxVal , 10 )
end
2010-05-25 15:12:51 +00:00
2010-08-13 09:44:08 +00:00
--- Create random number with normal distribution.
-- @param minVal Minimal value of the random number range
-- @param maxVal Maximal value of the random number range
-- @param count How often will be diced
-- @return The random number
2014-09-19 21:15:18 +02:00
function M . NormalRnd2 ( minVal , maxVal , count )
2016-03-29 21:06:15 +02:00
local base = 0
2010-05-25 15:12:51 +00:00
for _ = 1 , count do
2016-03-29 21:06:15 +02:00
base = base + math.random ( maxVal - minVal + 1 ) - 1
end
return math.ceil ( base / count ) + minVal
end
2010-05-25 15:12:51 +00:00
2018-04-03 19:58:17 +03:00
--- Sample from categorical distribution, e.g. rolling an N-sided die
-- @param cumulativeProb - array of legth N - cumulative sum of probabilities
-- should be monotonic non-decreasing, first element is the probability of first category
-- every other element is sum of previous element and probability of current category
-- last element should be 1
-- @return int - the index of category that won
--
function M . randomCategorical ( cumulativeProb )
local r = math.random ( )
local category
for i , v in ipairs ( cumulativeProb ) do
if r <= v then
category = i
break
end
end
assert ( category , " Failed sampling categorical distribution, last cumulativeProbability was " .. cumulativeProb [ # cumulativeProb ] )
return category
end
--- Sample from multinomial distribution, i.e. repeated categorical trials
-- @param numTrials - number of categorical trials to simulate. Sum of values in returned array will be equal to this.
-- @param cumulativeProb - cumulative sum of probabilities, same as in randomCategorical function
-- @return array of integers - number of times each category won
--
function M . randomMultinomial ( numTrials , cumulativeProb )
local result = { }
for i = 1 , # cumulativeProb do
result [ i ] = 0
end
for _ = 1 , numTrials do
local category = M.randomCategorical ( cumulativeProb )
result [ category ] = result [ category ] + 1
end
return result
end
2016-03-30 20:14:03 +02:00
local function _isNumber ( value )
return type ( value ) == " number "
end
local function _isTable ( value )
return type ( value ) == " table "
end
2016-04-04 18:35:38 +02:00
local TimeList = { }
function M . spamProtect ( character , delay )
if not isValidChar ( character ) then
error ( " The parameter 'character' is not a valid character as it was expected. " )
end
delay = delay or 1
if not _isNumber ( delay ) then
error ( " The parameter 'delay' is not a number as it was expected. " )
elseif delay < 1 then
error ( " The parameter 'delay' must be a positive number. " )
end
if TimeList [ character.id ] ~= nil then
2016-04-04 19:15:00 +02:00
if ( math.abs ( world : getTime ( " unix " ) - TimeList [ character.id ] ) ) <= delay then
return true
2016-04-04 18:35:38 +02:00
end
end
2016-04-04 19:15:00 +02:00
TimeList [ character.id ] = world : getTime ( " unix " )
2016-04-04 18:35:38 +02:00
return false
end
2016-03-29 21:26:21 +02:00
--[[
CreateItem
Safely create an item
@ return boolean - true if all fits in player ' s inventory, false if something was created on the ground
] ]
2020-04-30 23:14:14 +02:00
function M . CreateItem ( character , id , amount , quality , data )
2016-03-30 20:14:03 +02:00
if not isValidChar ( character ) then
error ( " The parameter 'character' is not a valid character as it was expected. " )
end
if not _isNumber ( id ) then
error ( " The parameter 'id' is not a number as it was expected. " )
elseif id < 1 then
error ( " The parameter 'id' must be a positive number. " )
end
if not _isNumber ( amount ) then
error ( " The parameter 'amount' is not a number as it was expected. " )
elseif amount < 1 then
error ( " The parameter 'amount' must be a positive number. " )
end
if not _isNumber ( quality ) then
error ( " The parameter 'quality' is not a number as it was expected. " )
2021-08-24 11:35:30 +02:00
elseif quality < 100 or quality > 999 then
2021-03-28 11:11:40 +02:00
error ( " The parameter 'quality' must be a number between 100 and 999. " )
2016-03-30 20:14:03 +02:00
end
2020-05-02 19:09:19 +02:00
if data ~= nil and not _isTable ( data ) then
2016-03-30 20:14:03 +02:00
error ( " The parameter 'data' is not a table as it was expected. " )
end
local notCreated = character : createItem ( id , amount , quality , data )
2016-03-29 21:26:21 +02:00
if notCreated == 0 then
return true
end
2016-03-30 20:14:03 +02:00
local maxStack = world : getItemStatsFromId ( id ) . MaxStack
2016-03-29 21:26:21 +02:00
while ( notCreated > 0 ) do
2016-03-30 20:14:03 +02:00
-- work around an issue to prevent creation of stacks of unstackable items
local minimum = math.min ( notCreated , maxStack )
2020-04-30 23:14:14 +02:00
world : createItemFromId ( id , minimum , character.pos , true , quality , data )
2016-03-30 20:14:03 +02:00
notCreated = notCreated - minimum
2016-03-29 21:26:21 +02:00
end
2016-03-30 20:14:03 +02:00
2016-04-04 18:35:38 +02:00
if not M.spamProtect ( character ) then
character : inform (
" Du kannst nichts mehr halten und der Rest f<> llt zu Boden. " ,
" You can't carry any more and the rest drops to the ground. " ,
Player.highPriority )
end
2016-03-29 21:26:21 +02:00
return false
end
2013-12-16 21:13:58 +01:00
--[[
2015-06-14 16:01:26 +02:00
DeleteItemFromStack
Searches for an item with given properties in a stack and deletes it .
@ param PositionStruct - Position of the stack
@ param List - A list containing the properties of the item to be deleted
@ return boolean
2013-12-16 21:13:58 +01:00
] ]
2014-09-19 21:15:18 +02:00
function M . DeleteItemFromStack ( stackPosition , itemProperties )
2014-08-09 14:47:23 +02:00
2015-06-14 16:01:26 +02:00
if not world : isItemOnField ( stackPosition ) then
return false
end
local theField = world : getField ( stackPosition )
local counter = 1
local foundItem = false
while counter <= theField : countItems ( ) do
local checkItem = theField : getStackItem ( theField : countItems ( ) - counter )
2020-12-06 16:56:01 +01:00
if ( itemProperties.itemId == checkItem.id ) and ( not itemProperties.deleteAmount or checkItem.number <= itemProperties.deleteAmount ) and ( not itemProperties.quality or checkItem.quality == itemProperties.quality ) then
2015-06-14 16:01:26 +02:00
if itemProperties.data then
for i = 1 , # itemProperties.data do
2022-08-22 12:04:49 +02:00
if not checkItem : getData ( itemProperties [ " data " ] [ i ] [ " dataKey " ] ) == itemProperties [ " data " ] [ i ] [ " dataValue " ] then
2015-06-14 16:01:26 +02:00
break
end
end
end
foundItem = true
break
end
counter = counter + 1
end
if not foundItem then
return false
end
local deletedItems = { }
for i = 1 , counter do
local deleteItem = world : getItemOnField ( stackPosition )
if i ~= counter then
table.insert ( deletedItems , 1 , deleteItem )
world : erase ( deleteItem , deleteItem.number )
else
world : increase ( deleteItem , - ( itemProperties.deleteAmount or deleteItem.number ) )
end
end
for i = 1 , # deletedItems do
world : createItemFromItem ( deletedItems [ i ] , stackPosition , true )
end
return true
2014-08-09 14:47:23 +02:00
2013-12-16 21:13:58 +01:00
end
2010-05-25 15:12:51 +00:00
--[[
GetItemsOnField
Get a list of all items on a field
IMPORTAINT : All items but the one on top are read only
@ param PositionStruct - The position of the tile that shall be checked
@ return List of ItemStructs - The list of all items on that field
] ]
2014-09-19 21:15:18 +02:00
function M . GetItemsOnField ( Fieldpos )
2016-03-29 21:06:15 +02:00
local Field = world : getField ( Fieldpos )
2010-05-25 15:12:51 +00:00
if ( Field == nil ) then
2016-03-29 21:06:15 +02:00
return { }
2010-05-25 15:12:51 +00:00
end
2016-03-29 21:06:15 +02:00
local ItemsCount = Field : countItems ( )
local retList = { }
2010-05-25 15:12:51 +00:00
if ( ItemsCount > 0 ) then
for i = 0 , ItemsCount - 1 do
2016-03-29 21:06:15 +02:00
local Item = Field : getStackItem ( i )
table.insert ( retList , Item )
end
end
2010-05-25 15:12:51 +00:00
return retList
2016-03-29 21:06:15 +02:00
end
2010-05-25 15:12:51 +00:00
--[[
2012-11-30 11:54:55 +01:00
GetItemInInventory
2010-05-25 15:12:51 +00:00
Search the whole Inventory of a character for a Item and a Data value
@ param CharacterStruct - The characters who ' s inventory is checked
@ param integer - The item ID we are looking for
2012-11-30 11:54:55 +01:00
@ param list ( list ( string ) ) - Optional list of key - value pairs of the sought item . Default is an empty list .
E.g . { { " amount " , " 4 " } , { " valid " , " true " } } . NOTE : key & value have to be both strings .
@ return mixed - The itemstruct of the first found item or nil .
2010-05-25 15:12:51 +00:00
] ]
2014-09-19 21:15:18 +02:00
function M . GetItemInInventory ( User , ItemID , DataValues )
2016-03-29 21:06:15 +02:00
local ItemList = User : getItemList ( ItemID )
2017-10-06 16:03:27 +02:00
local dataOK
local hasData = true
if ( DataValues == nil or # DataValues == 0 ) then
hasData = false
2012-11-30 11:54:55 +01:00
end
for _ , item in pairs ( ItemList ) do
2017-10-06 16:03:27 +02:00
dataOK = true
if hasData then
for _ , data in pairs ( DataValues ) do
if ( tostring ( item : getData ( data [ 1 ] ) ) ~= tostring ( data [ 2 ] ) ) then
dataOK = false
end
end
2012-11-30 11:54:55 +01:00
end
2017-10-06 16:03:27 +02:00
if dataOK then
return item
2016-03-29 21:06:15 +02:00
end
end
return nil
end
2010-05-25 15:12:51 +00:00
--[[
GetStiffness
Generate the sum of all stiffness values of the armor parts a character wears
@ param CharacterStruct - The character whos stiffness is checked
@ return integer - the stiffness value
2012-12-29 23:56:33 +00:00
Reworked by Flux
2010-05-25 15:12:51 +00:00
] ]
2014-09-19 21:15:18 +02:00
function M . GetStiffness ( Character )
2012-12-29 23:56:33 +00:00
2016-03-29 21:06:15 +02:00
local StiffnessVal = 0
local Item
local found
local Armor
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
local Equipmentposition = { 1 , 3 , 4 , 9 , 10 , 11 }
2014-08-09 14:47:23 +02:00
2012-12-30 00:03:20 +00:00
for counter = 1 , # Equipmentposition do
2016-03-29 21:06:15 +02:00
Item = Character : getItemAt ( Equipmentposition [ counter ] )
2015-06-14 16:01:26 +02:00
if Item and ( Item.id ~= 0 ) then
2016-03-29 21:06:15 +02:00
found , Armor = world : getArmorStruct ( Item.id )
2015-06-14 16:01:26 +02:00
if found then
2015-11-29 12:00:59 +01:00
StiffnessVal = StiffnessVal + Armor.Stiffness
2016-03-29 21:06:15 +02:00
end
end
2014-08-09 14:47:23 +02:00
2012-12-30 00:03:20 +00:00
end
2014-08-09 14:47:23 +02:00
2016-03-29 21:06:15 +02:00
return StiffnessVal
end
2010-05-25 15:12:51 +00:00
2020-05-15 17:59:21 +02:00
local interruptTable
2010-05-25 15:12:51 +00:00
--[[
ResetInterruption
Resets the interruption counter state , in case the action did not success
@ param CharacterStruct - The character whos state is resetted
@ param LongTimeActionState - The state of the current action
] ]
2014-09-19 21:15:18 +02:00
function M . ResetInterruption ( Character , ltstate )
2010-05-25 15:12:51 +00:00
if ( ltstate ~= Action.success ) and interruptTable then
2016-03-29 21:06:15 +02:00
interruptTable [ Character.id ] = nil
end
end
2010-05-25 15:12:51 +00:00
--[[
IsInterrupted
Checks if a characters action is interrupted has a minimal amount of actions that
2013-02-11 07:51:43 +01:00
will succeed all time before a interruption is possible .
2010-05-25 15:12:51 +00:00
@ param CharacterStruct - The character who is checked for a interruption
@ return boolean - true in case the action is interruped , false if not
] ]
2014-09-19 21:15:18 +02:00
function M . IsInterrupted ( Character )
2018-07-18 17:54:14 +02:00
if not interruptTable then
interruptTable = { }
interruptTable [ Character.id ] = 2
return false
elseif not interruptTable [ Character.id ] then
2016-03-29 21:06:15 +02:00
interruptTable [ Character.id ] = 2
return false
end
local rnd = math.random ( 20 , 50 )
2010-05-25 15:12:51 +00:00
if ( interruptTable [ Character.id ] > rnd ) then
2016-03-29 21:06:15 +02:00
interruptTable [ Character.id ] = nil
return true
end
interruptTable [ Character.id ] = interruptTable [ Character.id ] + 1
return false
end
2010-05-25 15:12:51 +00:00
--[[
ParalyseCharacter
2017-11-16 16:38:37 +01:00
Paralyses a character with a lte or by the actionpoints for setted time
@ param CharacterStruct - The character that shall be paralysed
@ param integer - The time in seconds the character shall be paralysed
2010-05-25 15:12:51 +00:00
@ param boolean - true if the time shall be added to an exsisting paralizing , false if the old values shall just be overwritten
@ param boolean - true ( default ) if a LTE shall be used to stunn the character . In this case its not intended to release the stunn any time ealier . False if not
@ return boolean - true if anything was done . false if not .
] ]
2014-09-19 21:15:18 +02:00
function M . ParalyseCharacter ( Target , Time , Cumulative , forced )
2010-05-25 15:12:51 +00:00
if not Time or ( Time == 1 ) then
2016-03-29 21:06:15 +02:00
return false
end
2010-05-25 15:12:51 +00:00
if ( forced == nil ) then
2016-03-29 21:06:15 +02:00
forced = true
end
2010-05-25 15:12:51 +00:00
if not forced then
if Cumulative then
2016-03-29 21:06:15 +02:00
Target.actionpoints = Target.actionpoints - ( Time * 10 )
2010-05-25 15:12:51 +00:00
else
Target.actionpoints = 30 - ( Time * 10 )
end
2016-03-29 21:06:15 +02:00
return true
end
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
local foundEffect , Paralysis = Target.effects : find ( 23 )
local timeLeft = 0
2010-05-25 15:12:51 +00:00
if not foundEffect then
if ( Time < 1 ) then
2016-03-29 21:06:15 +02:00
return false
2010-05-25 15:12:51 +00:00
end
2016-03-29 21:06:15 +02:00
Paralysis = LongTimeEffect ( 23 , 1 )
Target.effects : addEffect ( Paralysis )
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
local foundTime
foundTime , timeLeft = Paralysis : findValue ( " timeLeft " )
2010-05-25 15:12:51 +00:00
if not foundTime then
2016-03-29 21:06:15 +02:00
timeLeft = 0
2010-05-25 15:12:51 +00:00
end
2016-03-29 21:06:15 +02:00
end
2010-05-25 15:12:51 +00:00
if ( 0 < Time and Cumulative ) or ( Time < 0 ) then
2016-03-29 21:06:15 +02:00
Time = Time + timeLeft
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
Time = math.max ( Time , timeLeft )
end
2010-05-25 15:12:51 +00:00
2025-07-04 15:09:01 +02:00
if not _G.extendedCastingTimeForParalysis then
_G.extendedCastingTimeForParalysis = { }
end
_G.extendedCastingTimeForParalysis [ Target.id ] = math.max ( 0 , Time )
2010-05-25 15:12:51 +00:00
if ( Time ~= timeLeft ) then
2016-03-29 21:06:15 +02:00
Paralysis : addValue ( " timeLeft " , math.max ( 0 , Time ) )
return true
end
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
return false
end
2010-05-25 15:12:51 +00:00
--[[
IsCharacterParalysed
Checks if the character is stunned by the stunning LTE
@ param CharacterStruct - The character that shall be checked
@ return integer - The amount of setting remaining in the stunn or nil if not stunned
] ]
2014-09-19 21:15:18 +02:00
function M . IsCharacterParalysed ( Character )
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
local foundEffect , Paralysis = Character.effects : find ( 23 )
2010-05-25 15:12:51 +00:00
if not foundEffect then
2016-03-29 21:06:15 +02:00
return nil
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
local foundTime , timeLeft = Paralysis : findValue ( " timeLeft " )
2010-05-25 15:12:51 +00:00
if foundTime then
2016-03-29 21:06:15 +02:00
return timeLeft
end
end
return nil
end
2010-05-25 15:12:51 +00:00
2014-11-18 15:14:21 +01:00
local circleCache = { }
2010-05-25 15:12:51 +00:00
--[[
CreateCircle
Calculates a circle based on a center position and a radius . It triggers a Event function
on every position of the circle with the position of the circle as parameter
@ param PositionStruct - Center position of the circle
@ param integer - Radius of the circle
@ param function ( PositionStruct ) - Event function that is triggered for every position
] ]
2025-02-09 18:52:39 +01:00
function M . CreateCircle ( CenterPos , Radius , Event , monster )
2010-05-25 15:12:51 +00:00
if not Event then
2014-11-18 15:14:21 +01:00
return
end
if not circleCache [ Radius ] then
2016-03-29 21:06:15 +02:00
local irad = math.ceil ( Radius )
local map = { }
circleCache [ Radius ] = { }
2010-05-25 15:12:51 +00:00
for x = - irad - 1 , irad do
2016-03-29 21:06:15 +02:00
map [ x ] = { }
2010-05-25 15:12:51 +00:00
for y = - irad - 1 , irad do
2016-03-29 21:06:15 +02:00
map [ x ] [ y ] = ( x + 0.5 ) * ( x + 0.5 ) + ( y + 0.5 ) * ( y + 0.5 ) - irad * irad > 0
end
end
2010-05-25 15:12:51 +00:00
for x = - irad , irad do
for y = - irad , irad do
if not ( map [ x ] [ y ] and map [ x - 1 ] [ y ] and map [ x ] [ y - 1 ] and map [ x - 1 ] [ y - 1 ] )
and ( map [ x ] [ y ] or map [ x - 1 ] [ y ] or map [ x ] [ y - 1 ] or map [ x - 1 ] [ y - 1 ] ) then
2016-03-29 21:06:15 +02:00
table.insert ( circleCache [ Radius ] , position ( x , y , 0 ) )
end
end
end
end
local go_on
2014-11-18 15:14:21 +01:00
for _ , posi in pairs ( circleCache [ Radius ] ) do
2025-02-09 18:52:39 +01:00
go_on = Event ( position ( CenterPos.x + posi.x , CenterPos.y + posi.y , CenterPos.z ) , monster )
2010-05-25 15:12:51 +00:00
if ( go_on == false and go_on ~= nil ) then
2016-03-29 21:06:15 +02:00
return true
end
end
end
2010-05-25 15:12:51 +00:00
--[[
CreateLine
Calculates a straight line between 2 position and triggers a Event function for every position
@ param PositionStruct - First position of the line
@ param PositionStruct - Second position of the line
@ param function ( PositionStruct ) - Event function that is triggered for every position
] ]
2014-09-19 21:15:18 +02:00
function M . CreateLine ( StartPos , TargetPos , Event )
2016-03-29 21:06:15 +02:00
local XDiff2 = math.abs ( StartPos.x - TargetPos.x )
local YDiff2 = math.abs ( StartPos.y - TargetPos.y )
local PriDiff = math.max ( XDiff2 , YDiff2 )
local SecDiff = math.min ( YDiff2 , XDiff2 )
local XMod = ( StartPos.x < TargetPos.x and - 1 or 1 )
local YMod = ( StartPos.y < TargetPos.y and - 1 or 1 )
local PathPos
local go_on
2010-05-25 15:12:51 +00:00
for i = 1 , PriDiff do
if ( PriDiff == XDiff2 ) then
2016-03-29 21:06:15 +02:00
PathPos = position ( StartPos.x - XMod * i , StartPos.y - YMod * math.floor ( i / PriDiff * SecDiff ) , StartPos.z )
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
PathPos = position ( StartPos.x - XMod * math.floor ( i / PriDiff * SecDiff ) , StartPos.y - YMod * i , StartPos.z )
end
go_on = Event ( PathPos )
2010-05-25 15:12:51 +00:00
if ( go_on == false and go_on ~= nil ) then
2016-03-29 21:06:15 +02:00
return true
end
end
end
2010-05-25 15:12:51 +00:00
--[[
CreateTangentLine
Calculates a tangent line from the perspective of the center pos with the middle on the TargetPos
@ param PositionStruct - The center position
@ param PositionStruct - the target position ( center of this line is on this position )
@ param integer - length of one arm of the line . Effective length is ArmLength * 2 + 1
@ param function ( PositionStruct ) - Event function that is triggered for every position
] ]
2014-09-19 21:15:18 +02:00
function M . CreateTangentLine ( CenterPos , TargetPos , ArmLength , Event )
2016-03-29 21:06:15 +02:00
local phi = M.GetPhi ( CenterPos , TargetPos ) + math.pi / 2
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
local xoffset = math.cos ( phi )
local yoffset = math.sin ( phi )
local maxoffset = math.max ( math.abs ( xoffset ) , math.abs ( yoffset ) )
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
yoffset = yoffset * ( 1 / maxoffset )
xoffset = xoffset * ( 1 / maxoffset )
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
local go_on = Event ( TargetPos )
2010-05-25 15:12:51 +00:00
if ( go_on == false and go_on ~= nil ) then
2016-03-29 21:06:15 +02:00
return
2010-05-25 15:12:51 +00:00
end
2016-03-29 21:06:15 +02:00
local first_go_on = true
local second_go_on = true
2010-05-25 15:12:51 +00:00
for i = 1 , ArmLength do
if first_go_on then
2015-01-02 23:22:20 +01:00
first_go_on = Event ( position ( M.Round ( TargetPos.x + i * xoffset ) ,
2016-03-29 21:06:15 +02:00
M.Round ( TargetPos.y + i * yoffset ) , TargetPos.z ) )
first_go_on = ( first_go_on ~= nil and first_go_on or true )
end
2010-05-25 15:12:51 +00:00
if second_go_on then
2015-01-02 23:22:20 +01:00
second_go_on = Event ( position ( M.Round ( TargetPos.x - i * xoffset ) ,
2016-03-29 21:06:15 +02:00
M.Round ( TargetPos.y - i * yoffset ) , TargetPos.z ) )
second_go_on = ( second_go_on ~= nil and second_go_on or true )
end
2010-05-25 15:12:51 +00:00
if not first_go_on and not second_go_on then
2016-03-29 21:06:15 +02:00
return
end
end
end
2010-05-25 15:12:51 +00:00
--[[
Scale
Calculates a linear interpolation between two values based upon a third value
@ param integer - Start of the linear interpolation . Value at 0
@ param integer - End of the linear interpolation . Value at 100
@ param integer - Value for the interpolation . Causes the function to return the first value if its set to 0 and the second if its set to 100
For any value between 0 and 100 it returns a value between the start and the end value
@ return integer - interpolated value
] ]
2014-09-19 21:15:18 +02:00
function M . Scale ( ScBegin , ScEnd , value )
2016-03-29 21:06:15 +02:00
return M.ScaleUnlimited ( ScBegin , ScEnd , M.Limit ( value , 0 , 100 ) )
end
2010-05-25 15:12:51 +00:00
--[[
ScaleUnlimited
Calculates a linear interpolation between two values based upon a third value
In case the value is aboth 100 the return value will be aboth the end value
@ param integer - Start of the linear interpolation . Value at 0
@ param integer - End of the linear interpolation . Value at 100
@ param integer - Value for the interpolation . Causes the function to return the first value if its set to 0 and the second if its set to 100
For any value between 0 and 100 it returns a value between the start and the end value
@ return integer - interpolated value
] ]
2014-09-19 21:15:18 +02:00
function M . ScaleUnlimited ( ScBegin , ScEnd , value )
2016-03-29 21:06:15 +02:00
return ( ScEnd - ScBegin ) / 100 * value + ScBegin
end
2010-05-25 15:12:51 +00:00
--[[
Limit
Sets the value into the given borders . If the borders are set to nil or false , they are interpreted as non - exsistant
@ param integer - The value that shall be checked
@ param integer - The minimal border . The return value will be not smaller then this
@ param integer - The maximal border . The return value will not be bigger then this
@ return integer - the value or the border in case the value is bigger or smaller then the given range
] ]
2014-09-19 21:15:18 +02:00
function M . Limit ( value , min , max )
2010-05-25 15:12:51 +00:00
if min and ( value < min ) then
2016-03-29 21:06:15 +02:00
return min
end
2010-05-25 15:12:51 +00:00
if max and ( value > max ) then
2016-03-29 21:06:15 +02:00
return max
end
return value
end
2010-05-25 15:12:51 +00:00
--[[
Round
Round a value at a given precision up at x .5 and bigger , down in any other cases
@ param integer - the value that shall be rounded
@ param integer - the precision , default 0
@ return integer - the rounded value
] ]
2014-09-19 21:15:18 +02:00
function M . Round ( value , precision )
2010-05-25 15:12:51 +00:00
if not precision then
2016-03-29 21:06:15 +02:00
precision = 0
end
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
value = value * 10 ^ precision
2010-05-25 15:12:51 +00:00
2014-08-18 11:55:28 +02:00
if ( math.fmod ( value , 1 ) >= 0.5 ) then
2016-03-29 21:06:15 +02:00
value = math.ceil ( value )
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
value = math.floor ( value )
end
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
value = value / 10 ^ precision
return value
end
2010-05-25 15:12:51 +00:00
--[[
GetPhi
Calculate phi between 2 positions
@ param PositionStruct - The start position
@ param PositionStruct - The target position
@ return float - phi
] ]
2014-09-19 21:15:18 +02:00
function M . GetPhi ( StartPos , TargetPos )
2012-10-27 18:09:13 +02:00
if StartPos == TargetPos then
2016-03-29 21:06:15 +02:00
return math.random ( ) * 2
2010-05-25 15:12:51 +00:00
end
2016-03-29 21:06:15 +02:00
local dx = TargetPos.x - StartPos.x
local dy = TargetPos.y - StartPos.y
local phi
2010-05-25 15:12:51 +00:00
if ( dx == 0 ) then
if ( dy > 0 ) then
2016-03-29 21:06:15 +02:00
phi = math.pi / 2
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
phi = math.pi + math.pi / 2
end
2010-05-25 15:12:51 +00:00
elseif ( dx > 0 ) then
if ( dy >= 0 ) then
2016-03-29 21:06:15 +02:00
phi = math.atan ( dy / dx )
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
phi = math.atan ( dy / dx ) + 2 * math.pi
end
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
phi = math.atan ( dy / dx ) + math.pi
end
return phi
2010-05-25 15:12:51 +00:00
end
--[[
ChangeAttribute
Change a attribute by a given value and return the effective change
@ param CharacterStruct - The character whos attribute shall be changed
@ param string - The attribute that shall be changed
@ param integer - The value that the attribute shall be changed by
@ param integer - The bottom border [ optional ]
@ param integer - The top border [ optional ]
@ return integer - Change Value
] ]
2014-09-19 21:15:18 +02:00
function M . ChangeAttribute ( Character , attrib , value , bottomBorder , topBorder )
2010-05-25 15:12:51 +00:00
if ( value < - 255 or value > 255 ) then
2016-03-29 21:06:15 +02:00
return 0
end
2010-05-25 15:12:51 +00:00
if ( bottomBorder <= 0 or bottomBorder >= 255 ) then
2016-03-29 21:06:15 +02:00
bottomBorder = 1
end
2010-05-25 15:12:51 +00:00
if ( topBorder <= 0 or topBorder >= 255 ) then
2016-03-29 21:06:15 +02:00
topBorder = 255
end
2010-05-25 15:12:51 +00:00
if bottomBorder >= topBorder then
2016-03-29 21:06:15 +02:00
bottomBorder = 1
topBorder = 255
end
local oldValue = Character : increaseAttrib ( attrib , 0 )
local newValue = oldValue + value
2010-05-25 15:12:51 +00:00
if ( newValue < bottomBorder ) then
2016-03-29 21:06:15 +02:00
value = bottomBorder - newValue
2010-05-25 15:12:51 +00:00
elseif ( newValue > topBorder ) then
2016-03-29 21:06:15 +02:00
value = newValue - topBorder
end
Character : increaseAttrib ( attrib , value )
return ( value + 255 )
end
2010-05-25 15:12:51 +00:00
--[[
RevertAttribute
Revert the change of a attribute done with ChangeAttribute
@ param CharacterStruct - The character whos attribute shall be changed
@ param string - The attribute that shall be changed
@ param integer - The value returned by ChangeAttribute
@ return boolean - success or not
] ]
2014-09-19 21:15:18 +02:00
function M . RevertAttribute ( Character , attrib , value )
2010-05-25 15:12:51 +00:00
if ( value < 0 or value > 510 ) then
2016-03-29 21:06:15 +02:00
return false
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
value = value - 255
end
2010-05-25 15:12:51 +00:00
2016-03-29 21:06:15 +02:00
Character : increaseAttrib ( attrib , - value )
return true
end
2010-05-25 15:12:51 +00:00
--[[
CopyPosition
Copies a position struct to a new one
@ param position - the position that shall be copied
@ return position - the new position struct
] ]
2014-09-19 21:15:18 +02:00
function M . CopyPosition ( Posi )
2016-03-29 21:06:15 +02:00
return position ( Posi.x , Posi.y , Posi.z )
2010-05-25 15:12:51 +00:00
end
--[[
split
splits a string into different parts
@ param str - the string which should be splitted
@ return pat - the pattern around what the string should be splittet
example = split ( " hello:my:split:world " , " : " ) returns { " hello " , " my " , " split " , " world " }
2014-08-09 14:47:23 +02:00
2015-06-14 16:01:26 +02:00
In case the pattern is only one character it is better to use string.gmatch .
2010-05-25 15:12:51 +00:00
] ]
2014-09-19 21:15:18 +02:00
function M . split ( str , pat )
2013-05-27 22:27:51 +02:00
local t = { }
2015-06-14 16:01:26 +02:00
if ( string.len ( pat ) == 1 ) then
for element in string.gmatch ( str , " ([^ " .. pat .. " ]+)[ " .. pat .. " ]? " ) do
2016-03-29 21:06:15 +02:00
table.insert ( t , element )
end
2015-06-14 16:01:26 +02:00
else
local fpat = " (.-) " .. pat
local last_end = 1
local s , e , cap = str : find ( fpat , 1 )
while s do
if s ~= 1 or cap ~= " " then
2016-03-29 21:06:15 +02:00
table.insert ( t , cap )
end
last_end = e + 1
s , e , cap = str : find ( fpat , last_end )
end
2015-06-14 16:01:26 +02:00
if ( last_end <= str : len ( ) ) then
2016-03-29 21:06:15 +02:00
cap = str : sub ( last_end )
table.insert ( t , cap )
end
end
return t
2010-05-25 15:12:51 +00:00
end
2010-08-10 21:48:35 +00:00
--[[
Split_number
Splits a digit chain into digits and returns it in a table
Example : split_number ( 3015 , 4 ) returns following table : table = { 3 , 0 , 1 , 5 }
2015-06-14 16:01:26 +02:00
@ param Number - the number chain that shall be splitted
2010-08-10 21:48:35 +00:00
@ param AmountOfDigits - the amount of digits the number chain has
@ return table - the splitted number chain into digits
] ]
2014-09-19 21:15:18 +02:00
function M . Split_number ( Number , AmountOfDigits )
2010-08-10 21:48:35 +00:00
2016-03-29 21:06:15 +02:00
local temptable = { }
local tempcnt = 0
2017-10-06 16:03:27 +02:00
local tempnumber
2010-08-10 21:48:35 +00:00
2017-10-06 16:03:27 +02:00
tempnumber = math.floor ( Number / ( 10 ^ AmountOfDigits ) )
Number = Number - tempnumber * ( 10 ^ AmountOfDigits )
2010-08-10 21:48:35 +00:00
2015-06-14 16:01:26 +02:00
for i = ( AmountOfDigits - 1 ) , 0 , - 1 do
2016-03-29 21:06:15 +02:00
tempcnt = tempcnt + 1
2017-10-06 16:03:27 +02:00
tempnumber = math.floor ( Number / ( 10 ^ i ) )
temptable [ tempcnt ] = tempnumber
Number = Number - tempnumber * ( 10 ^ i )
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
return temptable
2010-08-10 21:48:35 +00:00
end
2010-05-25 15:12:51 +00:00
--[[
2014-05-28 12:16:58 +02:00
Converts a given hour of day to a string like : early morning , morning ,
2010-05-25 15:12:51 +00:00
@ param hour the hour which should be converted
@ return ger , eng strings
] ]
2014-09-19 21:15:18 +02:00
function M . Hour_To_String ( hour )
2010-05-25 15:12:51 +00:00
if ( hour >= 0 and hour < 2 or hour == 24 ) then
2016-03-29 21:06:15 +02:00
return " gegen Mitternacht " , " around midnight "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 2 and hour < 4 ) then
2016-03-29 21:06:15 +02:00
return " nach Mitternacht " , " after midnight "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 4 and hour < 6 ) then
2016-03-29 21:06:15 +02:00
return " vor Sonnenaufgang " , " before sunrise "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 6 and hour < 8 ) then
2016-03-29 21:06:15 +02:00
return " fr<EFBFBD> her Morgen " , " in the early morning "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 8 and hour < 10 ) then
2016-03-29 21:06:15 +02:00
return " morgens " , " in the morning "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 10 and hour < 12 ) then
2016-03-29 21:06:15 +02:00
return " vormittags " , " before noon "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 12 and hour < 14 ) then
2016-03-29 21:06:15 +02:00
return " gegen Mittag " , " around noon "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 14 and hour < 16 ) then
2016-03-29 21:06:15 +02:00
return " nachmittags " , " in the afternoon "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 16 and hour < 18 ) then
2016-03-29 21:06:15 +02:00
return " fr<EFBFBD> her Abend " , " in the early evening "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 18 and hour < 20 ) then
2016-03-29 21:06:15 +02:00
return " abends " , " in the evening "
2010-05-25 15:12:51 +00:00
elseif ( hour >= 20 and hour < 22 ) then
2016-03-29 21:06:15 +02:00
return " sp<EFBFBD> ter Abend " , " in the late evening "
2010-05-25 15:12:51 +00:00
else
2016-03-29 21:06:15 +02:00
return " vor Mitternacht " , " before midnight "
end
end
2010-05-25 15:12:51 +00:00
2014-05-28 12:16:58 +02:00
--[[
Converts a numeric month to a string like : Elos , Tanos , ...
@ param month the moth which should be converted
@ return string
] ]
2014-09-19 21:15:18 +02:00
function M . Month_To_String ( month )
2014-05-28 12:16:58 +02:00
2016-03-29 21:06:15 +02:00
local MonthNames = { " Elos " , " Tanos " , " Zhas " , " Ushos " , " Siros " , " Ronas " , " Bras " , " Eldas " , " Irmas " , " Malas " , " Findos " , " Olos " , " Adras " , " Naras " , " Chos " , " Mas " } --List of our months
2014-08-09 14:47:23 +02:00
2014-05-28 12:16:58 +02:00
if ( month >= 1 ) and ( month <= 16 ) then --only valid months
2016-03-29 21:06:15 +02:00
return MonthNames [ month ] --return the month as string
2014-05-28 12:16:58 +02:00
else
2016-03-29 21:06:15 +02:00
return " [ERROR] Corrupted date, please inform a developer. "
2014-05-28 12:16:58 +02:00
end
2016-03-29 21:06:15 +02:00
end
2014-05-28 12:16:58 +02:00
2014-09-19 21:15:18 +02:00
function M . fold ( ar , f , neutral )
2016-03-29 21:06:15 +02:00
local result = neutral
2010-05-25 15:12:51 +00:00
for i , v in ipairs ( ar ) do
result = f ( result , v )
end
2016-03-29 21:06:15 +02:00
return result
2010-05-25 15:12:51 +00:00
end
2014-09-19 21:15:18 +02:00
function M . map ( ar , f )
2010-05-25 15:12:51 +00:00
for i , v in ipairs ( ar ) do
2016-03-29 21:06:15 +02:00
ar [ i ] = f ( v )
2010-05-25 15:12:51 +00:00
end
2016-03-29 21:06:15 +02:00
return ar
2010-05-26 19:44:59 +00:00
end
2018-04-03 19:58:17 +03:00
---
-- Turn a list into a "set"
-- Use set[item] to check if an item is contained in set
-- Taken from: http://lua-users.org/wiki/SetOperations
-- For more operaions (union, intersection) see the above and also https://www.lua.org/pil/13.1.html
--@param t list of values to be included in the set
function M . setFromList ( t )
local s = { }
for _ , v in pairs ( t ) do s [ v ] = true end
return s
end
---
-- Take two sets and create a union (set containing all items from the sets, without repetition)
-- @param a - a set
-- @param b - a set
function M . setUnion ( a , b )
local s = { }
for v , _ in pairs ( a ) do s [ v ] = true end
for v , _ in pairs ( b ) do s [ v ] = true end
return s
end
2010-05-26 19:44:59 +00:00
--[[
This is some strange method written by Vilarion . Its extremly inefficient ,
very creepy and most likely totally buggy .
Usage is not adviced .
--]]
2014-09-19 21:15:18 +02:00
function M . isItemIdInFieldStack ( id , pos )
2010-05-26 19:44:59 +00:00
2016-03-29 21:06:15 +02:00
local stack = { }
local i = - 1
local found = false
local itm
2010-05-26 19:44:59 +00:00
while ( world : isItemOnField ( pos ) and ( not found ) ) do
2016-03-29 21:06:15 +02:00
itm = world : getItemOnField ( pos )
2010-05-26 19:44:59 +00:00
if ( itm.id == id ) then
2016-03-29 21:06:15 +02:00
found = true
2010-05-26 19:44:59 +00:00
else
2016-03-29 21:06:15 +02:00
i = i + 1
stack [ i ] = itm
world : erase ( itm , itm.number )
end
end
2010-05-26 19:44:59 +00:00
while ( i >= 0 ) do
2016-03-29 21:06:15 +02:00
world : createItemFromItem ( stack [ i ] , pos , true )
i = i - 1
end
return found
2010-05-26 19:44:59 +00:00
end
--[[
This is some strange method written by Vilarion . Its extremly inefficient ,
very creepy and most likely totally buggy .
Usage is not adviced .
--]]
2014-09-19 21:15:18 +02:00
function M . removeItemIdFromFieldStack ( id , pos )
2010-05-26 19:44:59 +00:00
2016-03-29 21:06:15 +02:00
local stack = { }
local i = - 1
local found = false
local itm
2010-05-26 19:44:59 +00:00
while ( world : isItemOnField ( pos ) and ( not found ) ) do
2016-03-29 21:06:15 +02:00
itm = world : getItemOnField ( pos )
2010-05-26 19:44:59 +00:00
if ( itm.id == id ) then
2016-03-29 21:06:15 +02:00
found = true
2010-05-26 19:44:59 +00:00
else
2016-03-29 21:06:15 +02:00
i = i + 1
stack [ i ] = itm
end
world : erase ( itm , itm.number )
end
2010-05-26 19:44:59 +00:00
while ( i >= 0 ) do
2016-03-29 21:06:15 +02:00
world : createItemFromItem ( stack [ i ] , pos , true )
i = i - 1
end
2010-10-09 10:07:30 +00:00
end
--[[
ExtgetPlayersInRangeOf
An extended Version of the world - function getPlayersInRangeOf
2015-06-14 16:01:26 +02:00
includes players who are in the diagonal direction in range of the radius
2010-10-09 10:07:30 +00:00
2015-06-14 16:01:26 +02:00
@ param pos - the center position struct from where there shall be looked at
2010-10-09 10:07:30 +00:00
@ param radius - the range at which we want to look for players
@ return struct table - a table with players or nil
] ]
2014-09-19 21:15:18 +02:00
function M . ExtgetPlayersInRangeOf ( posi , radius )
2010-10-09 10:07:30 +00:00
2016-03-29 21:06:15 +02:00
local ext = 2
local plyList = world : getPlayersInRangeOf ( posi , radius + ext )
2010-10-09 10:07:30 +00:00
2015-06-14 16:01:26 +02:00
for i , player in pairs ( plyList ) do
-- player not in rect, remove from list
if not M.isInRect ( player.pos , posi , radius ) then
2016-03-29 21:06:15 +02:00
plyList [ i ] = nil
2015-06-14 16:01:26 +02:00
end
end
2014-08-09 14:47:23 +02:00
2016-03-29 21:06:15 +02:00
return plyList
2010-10-09 10:07:30 +00:00
end
--[[
isInRect
2015-06-14 16:01:26 +02:00
checks whether targetpos is within a rect with distance " range "
and center position " posi "
2010-10-09 10:07:30 +00:00
2015-06-14 16:01:26 +02:00
@ param targetpos - the target pos that shall be looked for
2010-10-09 10:07:30 +00:00
@ param posi - center position of the rect
@ param range - distance of the rect borders from the center
@ return bool - true when targetpos is in rect else false
] ]
2014-09-19 21:15:18 +02:00
function M . isInRect ( targetpos , posi , range )
2010-10-09 10:07:30 +00:00
2015-06-14 16:01:26 +02:00
if targetpos.x >= ( posi.x - range ) and targetpos.x <= ( posi.x + range ) then --checks the x-Coordinates with the borders
if targetpos.y >= ( posi.y - range ) and targetpos.y <= ( posi.y + range ) then --checks the y-Coordinates with the borders
2016-03-29 21:06:15 +02:00
return true
2015-06-14 16:01:26 +02:00
else
2016-03-29 21:06:15 +02:00
return false
2015-06-14 16:01:26 +02:00
end
else
2016-03-29 21:06:15 +02:00
return false
2015-06-14 16:01:26 +02:00
end
2010-10-09 14:10:56 +00:00
end
--- Check if a given monster is docile
-- @param id The ID of the monster in question
-- @return true if monster is docile
2014-09-19 21:15:18 +02:00
function M . IsMonsterDocile ( id )
2015-06-14 16:01:26 +02:00
local docileList = { 6 , 16 , 26 , 36 , 46 , 56 , 96 , 106 , 116 , 181 , 182 , 241 , 242 , 243 , 244 , 371 , 372 , 373 , 396 , 581 , 584 , 591 , 615 , 616 , 621 , 1054 , 1131 , 1132 , 1133 , 1134 , 1135 , 1136 , 1151 , 1152 , 1161 , 1162 , 1163 , 1164 , 1165 }
for i , v in pairs ( docileList ) do
if id == v then
2016-03-29 21:06:15 +02:00
return true
2015-06-14 16:01:26 +02:00
end
end
2016-03-29 21:06:15 +02:00
return false
2010-10-09 14:10:56 +00:00
end
2010-10-10 12:43:22 +00:00
--- Checks if an item is in the hand tools slots
-- @param item The item to check
-- @return true if the item is in a hand tools slot, false otherwise
2014-09-19 21:15:18 +02:00
function M . IsItemInHands ( item )
2015-06-14 16:01:26 +02:00
if item : getType ( ) == 4 then
2016-03-29 21:06:15 +02:00
local itempos = item.itempos
2015-06-14 16:01:26 +02:00
if itempos == 5 or itempos == 6 then
2016-03-29 21:06:15 +02:00
return true
2015-06-14 16:01:26 +02:00
end
end
2016-03-29 21:06:15 +02:00
return false
2010-10-10 12:43:22 +00:00
end
2017-11-17 11:51:04 +01:00
--[[Check if a char holds an item from a list in hand
2024-12-06 20:09:16 +01:00
@ return true if item with id is in any hand slot
If true , returns the item in question ] ] --
2017-11-17 11:51:04 +01:00
function M . hasItemIdInHand ( user , itemIds )
2020-05-15 17:59:21 +02:00
if type ( itemIds ) ~= " table " then
2017-11-17 11:51:04 +01:00
itemIds = { itemIds }
end
local leftTool = user : getItemAt ( Character.left_tool )
if M.isInList ( leftTool.id , itemIds ) then
2024-12-06 20:09:16 +01:00
return true , leftTool
2017-11-17 11:51:04 +01:00
end
local rightTool = user : getItemAt ( Character.right_tool )
if M.isInList ( rightTool.id , itemIds ) then
2024-12-06 20:09:16 +01:00
return true , rightTool
2017-11-17 11:51:04 +01:00
end
return false
end
--[[Check if a char holds an item from a list in hand
@ return item if item with id is in any hand slot ] ] --
function M . getItemInHand ( user , itemIds )
2020-05-15 17:59:21 +02:00
if type ( itemIds ) ~= " table " then
2017-11-17 11:51:04 +01:00
itemIds = { itemIds }
end
local leftTool = user : getItemAt ( Character.left_tool )
if M.isInList ( leftTool.id , itemIds ) then
return leftTool
end
local rightTool = user : getItemAt ( Character.right_tool )
if M.isInList ( rightTool.id , itemIds ) then
return rightTool
end
return nil
end
2010-10-09 14:10:56 +00:00
--- Gets the target item for Use-With like commands. Both, source and target items have to be in hand tool slots.
-- @param character The character who wants to use the items
-- @param source The source item that is "used with" the target item
-- @return The target item (in the other hand slot than the source) or nil if no target is found
2014-09-19 21:15:18 +02:00
function M . GetTargetItem ( character , source )
2015-06-14 16:01:26 +02:00
if not M.IsItemInHands ( source ) then
2016-03-29 21:06:15 +02:00
return nil
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
local tpos = 11 - source.itempos -- either 5 or 6
local target = character : getItemAt ( tpos )
2015-06-14 16:01:26 +02:00
if target.id == 0 then
2016-03-29 21:06:15 +02:00
return nil
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
return target
2010-10-10 17:30:54 +00:00
end
2017-11-21 18:53:29 +01:00
--[[ Gets the target item for Use-With like commands.
@ param character The character who wants to use the items
@ param items The items , searched for
Sucess :
one of the items in hand
else #
one of the items in belt
else
one of the items in bag
itemNumber > 1 means in one step more than 1 possible item was found
@ return itemNumber ( 0 if not found ) , foundItem last found item ] ] --
function M . GetTargetItemAnywhere ( user , itemList )
local tmpItem
if type ( itemList ) ~= " table " then
itemList = { itemList }
end
local countItems = 0
local foundItem = nil
for i = 5 , 6 do -- hands
tmpItem = user : getItemAt ( i )
if M.isInList ( tmpItem.id , itemList ) then
countItems = countItems + 1
foundItem = tmpItem
end
end
if countItems > 0 then
return countItems , foundItem
end
for i = 12 , 17 do -- belt
tmpItem = user : getItemAt ( i )
if M.isInList ( tmpItem.id , itemList ) then
countItems = countItems + 1
foundItem = tmpItem
end
end
if countItems > 0 then
return countItems , foundItem
end
local backpack = user : getBackPack ( )
if backpack then
for i = 0 , 99 do
2021-03-28 14:35:59 +02:00
local isItem , tmpItemb , _ = backpack : viewItemNr ( i )
2017-11-21 18:53:29 +01:00
if isItem then
2021-03-28 14:35:59 +02:00
if M.isInList ( tmpItemb.id , itemList ) then
2017-11-21 18:53:29 +01:00
countItems = countItems + 1
2021-03-28 14:35:59 +02:00
foundItem = tmpItemb
2017-11-21 18:53:29 +01:00
end
end
end
end
return countItems , foundItem
end
2010-10-10 17:30:54 +00:00
--- Returns the real date and time as a String
-- @return date and time in format: YYYY-MM-DD | hh:mm:ss
2014-09-19 21:15:18 +02:00
function M . GetRealDateTimeString ( )
2016-03-29 21:06:15 +02:00
local year , month , day , hour , minute , second = M.GetRealDate ( )
2015-06-14 16:01:26 +02:00
local timeString =
function ( int )
if int < 10 then
2016-03-29 21:06:15 +02:00
return " 0 " .. int
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
return " " .. int
2015-06-14 16:01:26 +02:00
end
return timeString ( year ) .. " - " .. timeString ( month ) .. " - " ..
2016-03-29 21:06:15 +02:00
timeString ( day ) .. " | " .. timeString ( hour ) .. " : " .. timeString ( minute ) .. " : " .. timeString ( second )
2010-10-10 17:30:54 +00:00
end
--- Returns the real date as a String
-- @return date in format: YYYY-MM-DD
2014-09-19 21:15:18 +02:00
function M . GetRealDateString ( )
2021-03-28 14:35:59 +02:00
local year , month , day , _ , _ , _ = M.GetRealDate ( )
2015-06-14 16:01:26 +02:00
local timeString =
function ( int )
if int < 10 then
2016-03-29 21:06:15 +02:00
return " 0 " .. int
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
return " " .. int
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
return timeString ( year ) .. " - " .. timeString ( month ) .. " - " .. timeString ( day )
2010-10-10 17:30:54 +00:00
end
--- Returns the real time as a String
-- @return time in format: hh:mm:ss
2014-09-19 21:15:18 +02:00
function M . GetRealTimeString ( )
2021-05-31 15:48:15 +02:00
local _ , _ , _ , hour , minute , second = M.GetRealDate ( )
2015-06-14 16:01:26 +02:00
local timeString =
function ( int )
if int < 10 then
2016-03-29 21:06:15 +02:00
return " 0 " .. int
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
return " " .. int
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
return timeString ( hour ) .. " : " .. timeString ( minute ) .. " : " .. timeString ( second )
2010-10-10 17:30:54 +00:00
end
--- Converts the unix timestamp to a real date (GMT 0)
-- @return year
-- @return month
-- @return day
-- @return hour
-- @return minute
-- @return second
2014-09-19 21:15:18 +02:00
function M . GetRealDate ( )
2016-03-29 21:06:15 +02:00
local timestamp = world : getTime ( " unix " )
2015-06-14 16:01:26 +02:00
2016-03-29 21:06:15 +02:00
local year , month , day , hour , minute , second , tmp
2015-06-14 16:01:26 +02:00
year = math.floor ( timestamp / 31557600 ) -- (365.25*24*60*60)
2016-03-29 21:06:15 +02:00
local leapDays = math.floor ( ( year + 1 ) / 4 ) -- without the current year
timestamp = timestamp - ( year * 365 + leapDays ) * 86400 -- 24*60*60
2015-06-14 16:01:26 +02:00
2016-03-29 21:06:15 +02:00
local leapYear = 0
2015-06-14 16:01:26 +02:00
if ( year % 4 ) == 2 then
2016-03-29 21:06:15 +02:00
leapYear = 1 -- this year is a leap year
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
year = year + 1970 -- unix time starts there
2015-06-14 16:01:26 +02:00
2016-03-29 21:06:15 +02:00
local dayList = { 0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 }
tmp = math.floor ( timestamp / 86400 ) -- days so far this year
month = 1
2015-06-14 16:01:26 +02:00
for i = 12 , 2 , - 1 do
2016-03-29 21:06:15 +02:00
local check = tmp - dayList [ i ]
2015-06-14 16:01:26 +02:00
if i > 2 then
2016-03-29 21:06:15 +02:00
check = check - leapYear
2015-06-14 16:01:26 +02:00
end
if check > 0 then
2016-03-29 21:06:15 +02:00
month = i
break
2015-06-14 16:01:26 +02:00
end
end
2016-03-29 21:06:15 +02:00
tmp = dayList [ month ] -- days without current month
2015-06-14 16:01:26 +02:00
if month > 2 then
2016-03-29 21:06:15 +02:00
tmp = tmp + leapYear
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
timestamp = timestamp - ( ( tmp - 1 ) * 86400 ) -- days this month (in seconds)
day = math.floor ( timestamp / 86400 )
timestamp = timestamp - ( day * 86400 )
hour = math.floor ( timestamp / 3600 )
timestamp = timestamp - ( hour * 3600 )
minute = math.floor ( timestamp / 60 )
second = timestamp - ( minute * 60 )
return year , month , day , hour , minute , second
2011-03-19 13:01:30 +00:00
end
2015-02-15 22:51:46 +01:00
-- Returns an NPC in case it is found within a given range of a give position
-- @param centerPosition Position structure that is the center of the search area.
-- @param radius Radius within which is searched
-- @param npcName Name of the npc to search for
-- @return Returns the NPC in case it was found else false
2015-11-29 10:24:58 +01:00
function M . getNpc ( centerPosition , radius , npcName )
2015-02-16 01:43:50 +01:00
2015-02-15 22:51:46 +01:00
local npcs = world : getNPCSInRangeOf ( centerPosition , radius )
for _ , candidateNpc in pairs ( npcs ) do
2015-11-29 10:00:18 +01:00
if ( npcName and candidateNpc.name == npcName ) then
2015-02-15 22:51:46 +01:00
return candidateNpc
end
2011-03-19 13:01:30 +00:00
end
2015-02-16 01:43:50 +01:00
2015-02-15 22:51:46 +01:00
return false
2015-02-16 01:43:50 +01:00
2011-05-23 20:11:34 +00:00
end
2011-08-06 12:08:43 +02:00
2014-09-19 21:15:18 +02:00
function M . PositionToText ( pos )
2011-08-06 12:08:43 +02:00
if pos ~= nil then
2016-03-29 21:06:15 +02:00
local retString = " ( " .. pos.x .. " , " .. pos.y .. " , " .. pos.z .. " ) "
return retString
2011-08-06 12:08:43 +02:00
end
return " nil " , " nil " , " nil "
2012-04-18 23:22:03 +02:00
end
2017-07-03 15:46:57 +02:00
--[[
\ fn : IsInList
\ brief : returns true if an valueChecked is in the valueList
\ attention : valueList must be a list !
] ]
function M . isInList ( valueChecked , valueList )
if valueChecked == nil then
return false
end
2017-11-17 11:51:04 +01:00
if type ( valueList ) ~= " table " then
valueList = { valueList }
2017-07-03 15:46:57 +02:00
end
2017-11-17 11:51:04 +01:00
for _ , value in pairs ( valueList ) do
if valueChecked == value then
2017-07-03 15:46:57 +02:00
return true
end
end
return false
end
2017-08-17 14:41:36 +02:00
--[[
\ fn : posInList
\ brief : returns position of an valueChecked in the valueList
\ attention : valueList must be a list !
] ]
function M . posInList ( valueChecked , valueList )
if valueChecked == nil then
return 0
end
2017-11-17 11:51:04 +01:00
if type ( valueList ) ~= " table " then
2017-08-17 14:41:36 +02:00
return 0
end
2017-11-17 11:51:04 +01:00
for i , value in pairs ( valueList ) do
if valueChecked == value then
2017-08-17 14:41:36 +02:00
return i
end
end
return 0
end
2017-11-17 11:51:04 +01:00
--[[
\ fn : getOneOutOfList
\ brief : returns one random row out of valueList
if valueList isn ' t a table, valueList is returned
] ]
function M . getOneOutOfList ( valueList )
if type ( valueList ) ~= " table " then
return valueList
end
local tmpList = { }
for i , row in pairs ( valueList ) do
table.insert ( tmpList , i )
end
return valueList [ math.random ( 1 , # tmpList ) ]
end
2012-04-18 23:22:03 +02:00
--[[
\ fn : ListToNumber
\ brief : transforms a number list into a number sequence which can be stored for example as quest status
\ usage : list = { 5 , 2 , 7 , 9 , 1 } ;
ListToNumber ( list ) returns following number : 52791
2014-08-09 14:47:23 +02:00
@ param NumberList - the number list that shall be transformed into a number
2012-04-18 23:22:03 +02:00
@ return result - the list as number
2014-08-09 14:47:23 +02:00
2012-04-18 23:22:03 +02:00
\ attention : Only Lists holding elements greater 1 are allowed !
] ]
2014-09-19 21:15:18 +02:00
function M . ListToNumber ( NumberList )
2014-08-09 14:47:23 +02:00
2016-03-29 21:06:15 +02:00
local result = NumberList [ 1 ]
2012-04-18 23:22:03 +02:00
2014-08-18 11:22:48 +02:00
for i = 2 , # NumberList do
2012-04-18 23:22:03 +02:00
2016-03-29 21:06:15 +02:00
result = result .. NumberList [ i ]
2014-08-09 14:47:23 +02:00
2012-04-18 23:22:03 +02:00
end
2016-03-29 21:06:15 +02:00
result = tonumber ( result ) --convert the string result into a number
return result
2012-04-18 23:22:03 +02:00
end
--[[
\ fn : Shuffle
2012-09-22 10:48:56 +02:00
\ brief : Shuffles the elements of a list ( with consecutively number indices , no key strings ! ) using the modern Fisher - Yates algorithm .
2012-04-18 23:22:03 +02:00
\ usage : list = { 5 , 2 , 7 , 9 , 1 } ;
list = Shuffle ( list ) shuffles the list and could return : list = { 1 , 7 , 5 , 9 , 2 }
2014-08-09 14:47:23 +02:00
2012-09-22 10:48:56 +02:00
@ param List The list that shall be shuffled
@ return The shuffled list
2012-04-18 23:22:03 +02:00
] ]
2014-09-19 21:15:18 +02:00
function M . Shuffle ( List )
2015-06-14 16:01:26 +02:00
local minIndex = 1
local maxIndex = # List
if ( List [ 0 ] ~= nil ) then -- check if zero index is used
minIndex = 0
maxIndex = maxIndex - 1
end
for i = maxIndex , minIndex + 1 , - 1 do -- shuffle all elements
2021-05-31 15:48:15 +02:00
local j = math.random ( minIndex , i )
2015-01-03 18:11:37 +01:00
List [ i ] , List [ j ] = List [ j ] , List [ i ]
2015-06-14 16:01:26 +02:00
end
return List
2012-04-18 23:22:03 +02:00
end
--[[
\ fn : CreateRandomNumberList
\ brief : creates a random number list with AmntElements elements
\ usage : list = CreateRandomNumberList ( 6 , 1 , 4 ) ;
returned list could hold : list = { 3 , 1 , 4 , 2 , 2 , 4 }
2014-08-09 14:47:23 +02:00
2012-04-18 23:22:03 +02:00
@ param AmntElements - the amount of elements the list shall have
@ param minval - the smallest value an element can have
@ param maxval - the highest value an element can have
@ return reslist - a list holding AmntElements elements with values between minval and maxval
] ]
2014-09-19 21:15:18 +02:00
function M . CreateRandomNumberList ( AmntElements , minval , maxval )
2014-08-09 14:47:23 +02:00
2016-03-29 21:06:15 +02:00
local reslist = { }
2014-08-09 14:47:23 +02:00
2012-04-18 23:22:03 +02:00
for i = 1 , AmntElements do
2014-08-09 14:47:23 +02:00
2016-03-29 21:06:15 +02:00
reslist [ i ] = math.random ( minval , maxval )
2014-08-09 14:47:23 +02:00
2015-06-14 16:01:26 +02:00
if ( reslist [ i ] == reslist [ i - 1 ] ) then -- same number like before, try getting a new number
2016-03-29 21:06:15 +02:00
reslist [ i ] = math.random ( minval , maxval )
2012-04-18 23:22:03 +02:00
end
2014-08-09 14:47:23 +02:00
2012-04-18 23:22:03 +02:00
end
2014-08-09 14:47:23 +02:00
2016-03-29 21:06:15 +02:00
return reslist
2012-04-18 23:22:03 +02:00
end
2018-07-18 17:54:14 +02:00
--[[Searches the Online List for a Player by name
2015-06-14 16:01:26 +02:00
if a player was found it returns : true , Char Struct
-- if not, nil]]
2012-04-18 23:22:03 +02:00
2018-07-18 17:54:14 +02:00
function M . CheckIfOnline ( playername )
2016-03-29 21:06:15 +02:00
local playerlist = world : getPlayersOnline ( )
2015-06-14 16:01:26 +02:00
for i = 1 , # ( playerlist ) do
2018-07-18 17:54:14 +02:00
if playerlist [ i ] . name == playername then
2015-06-14 16:01:26 +02:00
return playerlist [ i ]
end
end
return nil
2012-09-13 00:49:19 +02:00
end
2012-11-24 17:25:43 +01:00
--- Looks up the name of the defined lead attribute of a given skill name.
-- @param Skill The name of the skill.
-- @return String The name of the corresponding lead attribute.
-- NOTE: in case there is no lead attribute, nil will be returned.
2016-03-29 21:06:15 +02:00
local leadAttribTable = { }
--Dexterity: All crafting skills for final products
2025-03-21 20:41:26 +01:00
leadAttribTable [ Character.tailoring ] = { first = " intelligence " , second = " dexterity " }
leadAttribTable [ Character.blacksmithing ] = { first = " constitution " , second = " dexterity " }
leadAttribTable [ Character.gemcutting ] = { first = " essence " , second = " dexterity " }
leadAttribTable [ Character.carpentry ] = { first = " willpower " , second = " dexterity " }
leadAttribTable [ Character.cookingAndBaking ] = { first = " perception " , second = " dexterity " }
leadAttribTable [ Character.finesmithing ] = { first = " essence " , second = " dexterity " }
leadAttribTable [ Character.glassBlowing ] = { first = " willpower " , second = " dexterity " }
leadAttribTable [ Character.pottery ] = { first = " agility " , second = " dexterity " }
leadAttribTable [ Character.armourer ] = { first = " strength " , second = " dexterity " }
leadAttribTable [ Character.brewing ] = { first = " perception " , second = " dexterity " }
2016-03-29 21:06:15 +02:00
2025-05-26 18:41:30 +02:00
--Dexterity: Instruments
2016-03-29 21:06:15 +02:00
leadAttribTable [ Character.harp ] = " dexterity "
leadAttribTable [ Character.horn ] = " dexterity "
leadAttribTable [ Character.flute ] = " dexterity "
leadAttribTable [ Character.lute ] = " dexterity "
2025-05-26 18:41:30 +02:00
leadAttribTable [ Character.panpipe ] = " dexterity "
leadAttribTable [ Character.clavichord ] = " dexterity "
2016-03-29 21:06:15 +02:00
2025-03-21 20:41:26 +01:00
--Constitution: All gathering skills have it as their second attrib, with varying first attribs
leadAttribTable [ Character.herblore ] = { first = " perception " , second = " constitution " }
leadAttribTable [ Character.mining ] = { first = " strength " , second = " constitution " }
leadAttribTable [ Character.fishing ] = { first = " intelligence " , second = " constitution " }
leadAttribTable [ Character.farming ] = { first = " dexterity " , second = " constitution " }
leadAttribTable [ Character.woodcutting ] = { first = " agility " , second = " constitution " }
leadAttribTable [ Character.tanningAndWeaving ] = { first = " willpower " , second = " constitution " }
leadAttribTable [ Character.husbandry ] = { first = " essence " , second = " constitution " }
leadAttribTable [ Character.digging ] = { first = " strength " , second = " constitution " }
2026-04-21 19:01:27 +02:00
leadAttribTable [ Character.natureResistance ] = { first = " willpower " , second = " constitution " }
2016-03-29 21:06:15 +02:00
--Agility: Defensive fighting skills
leadAttribTable [ Character.parry ] = " agility "
leadAttribTable [ Character.heavyArmour ] = " agility "
leadAttribTable [ Character.mediumArmour ] = " agility "
leadAttribTable [ Character.lightArmour ] = " agility "
2026-02-10 14:30:33 +01:00
--Perception: Archery, poison weapons and druids
2016-03-29 21:06:15 +02:00
leadAttribTable [ Character.distanceWeapons ] = " perception "
2026-02-10 14:30:33 +01:00
leadAttribTable [ Character.poisoning ] = " perception "
leadAttribTable [ Character.witherweave ] = " perception "
leadAttribTable [ Character.bloomweave ] = " perception "
leadAttribTable [ Character.deepweave ] = " perception "
leadAttribTable [ Character.stoneweave ] = " perception "
leadAttribTable [ Character.wildweave ] = " perception "
2016-03-29 21:06:15 +02:00
--Strength: Offensive fighting skills
leadAttribTable [ Character.slashingWeapons ] = " strength "
leadAttribTable [ Character.wrestling ] = " strength "
leadAttribTable [ Character.concussionWeapons ] = " strength "
leadAttribTable [ Character.punctureWeapons ] = " strength "
2026-02-10 14:30:33 +01:00
--Essence: alchemy and enchanting
2025-10-24 16:39:19 +02:00
leadAttribTable [ Character.alchemy ] = { first = " essence " , second = " perception " }
2026-02-10 14:30:33 +01:00
leadAttribTable [ Character.enchanting ] = { first = " essence " , second = " intelligence " }
--Intelligence: Magic and transmutation
leadAttribTable [ Character.transmutation ] = { first = " intelligence " , second = " perception " }
2020-04-08 05:11:33 +02:00
leadAttribTable [ Character.fireMagic ] = " intelligence "
leadAttribTable [ Character.spiritMagic ] = " intelligence "
leadAttribTable [ Character.windMagic ] = " intelligence "
leadAttribTable [ Character.earthMagic ] = " intelligence "
leadAttribTable [ Character.waterMagic ] = " intelligence "
2026-02-10 14:30:33 +01:00
leadAttribTable [ Character.spatialMagic ] = { first = " willpower " , second = " intelligence " }
2017-01-12 18:42:43 +01:00
2024-10-14 19:22:58 +02:00
--Willpower: Priests and magic resistance
2020-04-08 05:11:33 +02:00
leadAttribTable [ Character.blessing ] = " willpower "
leadAttribTable [ Character.praying ] = " willpower "
leadAttribTable [ Character.vowing ] = " willpower "
leadAttribTable [ Character.confessing ] = " willpower "
leadAttribTable [ Character.ceremony ] = " willpower "
leadAttribTable [ Character.consecrateWeapons ] = " willpower "
leadAttribTable [ Character.consecrateArmours ] = " willpower "
2024-10-14 19:22:58 +02:00
leadAttribTable [ Character.magicResistance ] = " willpower "
2016-03-29 21:06:15 +02:00
function M . GetLeadAttributeName ( Skill )
return leadAttribTable [ Skill ]
2012-11-28 15:58:19 +01:00
end
2015-06-14 15:58:09 +02:00
--- Looks up the lead attribute value of a given skill name.
-- @param Skill The name of the skill.
-- @param user The character whose attributes are evaluated
-- @return The value of the corresponding lead attribute.
-- NOTE: in case there is no lead attribute, 10 will be returned.
function M . GetLeadAttrib ( user , Skill )
local leadAttribName = M.GetLeadAttributeName ( Skill )
2016-03-29 21:06:15 +02:00
2025-03-21 20:41:26 +01:00
2015-06-14 15:58:09 +02:00
if leadAttribName ~= nil then
2016-03-29 21:06:15 +02:00
2025-03-21 20:41:26 +01:00
if type ( leadAttribName ) == " table " then --Currently only crafts/gathering have two attribs instead of one
local leadAttribValue1 = user : increaseAttrib ( leadAttribName.first , 0 ) * 0.6 -- 60% of the impact dex had on its own in the past
local leadAttribValue2 = user : increaseAttrib ( leadAttribName.second , 0 ) * 0.4 -- 40% of the impact dex had on its own in the past
return leadAttribValue1 + leadAttribValue2
else
return user : increaseAttrib ( leadAttribName , 0 )
end
2016-03-29 21:06:15 +02:00
2015-06-14 15:58:09 +02:00
end
2016-03-29 21:06:15 +02:00
2015-06-14 15:58:09 +02:00
return 10 --10 should be default
2016-03-29 21:06:15 +02:00
2015-06-14 15:58:09 +02:00
end
--- Calculates an universal attribute bonus
-- Recommendation: Use together with GetLeadAttrib to calculate the bonus of a lead attribute on a skill related action
2020-04-04 08:28:30 +02:00
-- @param attributeValue The value of the attribute
2020-04-16 06:55:30 +02:00
-- @param range The range of the bonus (+/- range)
2015-06-14 15:58:09 +02:00
-- @return The value of the bonus
2020-04-16 06:55:30 +02:00
-- NOTE: 0 will be returned if something goes wrong
2015-06-14 15:58:09 +02:00
2024-12-06 20:09:16 +01:00
function M . GetAttributeBonus ( attributeValue , range , skillName , user )
if skillName then -- In this case, attributeValue is left blank and skillName/user is instead filled in to fetch the lead attribute on its own
local leadAttribName = M.GetLeadAttributeName ( skillName )
2025-03-21 20:41:26 +01:00
if type ( leadAttribName ) == " table " then --Currently only crafts/gathering have two attribs instead of one
local leadAttribValue1 = user : increaseAttrib ( leadAttribName.first , 0 ) * 0.6 -- 60% of the impact dex had on its own in the past
local leadAttribValue2 = user : increaseAttrib ( leadAttribName.second , 0 ) * 0.4 -- 40% of the impact dex had on its own in the past
attributeValue = leadAttribValue1 + leadAttribValue2
else
attributeValue = user : increaseAttrib ( leadAttribName , 0 )
end
2024-12-06 20:09:16 +01:00
end
2015-06-14 15:58:09 +02:00
2016-03-29 21:06:15 +02:00
local bonus
2020-04-04 10:32:44 +02:00
if attributeValue ~= nil and attributeValue ~= 0 and range < 1 and range > 0 then
2025-02-06 20:32:17 +01:00
bonus = range * ( ( attributeValue - 1 ) / 9.5 ) - range -- +/- range for attributes 1-20. Bonus capped at attribute 30. Neutral attribute (bonus = 0) at 10.5
2015-06-14 16:01:26 +02:00
else
2020-04-16 06:55:30 +02:00
bonus = 0 --default
2015-06-14 16:01:26 +02:00
end
2016-03-29 21:06:15 +02:00
2015-06-14 15:58:09 +02:00
return bonus
end
2020-12-06 23:34:30 +01:00
2024-12-06 20:09:16 +01:00
function M . GetAttributeBonusLow ( attributeValue , skillName , user )
return M.GetAttributeBonus ( attributeValue , 0.1 , skillName , user )
2020-04-16 06:55:30 +02:00
end
2024-12-06 20:09:16 +01:00
function M . GetAttributeBonusMedium ( attributeValue , skillName , user )
return M.GetAttributeBonus ( attributeValue , 0.2 , skillName , user )
2020-04-16 06:55:30 +02:00
end
2024-12-06 20:09:16 +01:00
function M . GetAttributeBonusHigh ( attributeValue , skillName , user )
return M.GetAttributeBonus ( attributeValue , 0.5 , skillName , user )
2020-04-16 06:55:30 +02:00
end
2020-04-04 08:28:30 +02:00
--- Calculates an universal quality bonus
2020-04-04 08:30:27 +02:00
-- @param item The item for which the bonus shall be calculated
2020-04-16 06:55:30 +02:00
-- @param range The range of the bonus (+/- range)
2020-04-04 08:28:30 +02:00
-- @return The value of the bonus
2020-04-16 06:55:30 +02:00
-- NOTE: 0 will be returned if something goes wrong
2020-04-04 08:28:30 +02:00
function M . GetQualityBonus ( item , range )
local bonus
2020-05-15 15:41:56 +02:00
local quality
2020-04-04 10:32:44 +02:00
if item ~= nil and item.quality ~= 0 and item.quality < 1000 and range < 1 and range > 0 then
2020-04-04 08:30:27 +02:00
quality = math.floor ( item.quality / 100 )
2020-04-16 06:55:30 +02:00
bonus = range * ( ( quality - 1 ) / 4 ) - range -- +/- range for quality 1-9. Neutral quality (bonus = 0) at 5
2020-04-04 08:28:30 +02:00
else
2020-04-16 06:55:30 +02:00
bonus = 0 --default
2020-04-04 08:28:30 +02:00
end
return bonus
end
2020-04-20 20:37:12 +02:00
function M . GetQualityBonusStandard ( item )
return M.GetQualityBonus ( item , 0.1 )
end
2012-11-28 15:58:19 +01:00
--- Searches in an area for an item id.
-- @param CenterPos Position struct of the center position of the area.
-- @param ItemId The ID of the sought item.
-- @param Radius Around the CenterPos, an area of +/-Radius is queried in x and y direction.
-- Default value is 1.
2012-11-30 11:54:55 +01:00
-- @param OnlyWriteable Bool that defines if only writeable items should be returned.
-- Default value is false.
2012-11-28 15:58:19 +01:00
-- @return scrItem If an item with ItemId is found, this is the first one found. Otherwise it is nil.
-- @return bool Writeable flag. If there are multiple items on the field, only the top one is writeable.
-- If none is found, nil is returned.
2022-10-09 12:34:43 +02:00
function M . GetItemInArea ( CenterPos , ItemId , Radius , OnlyWriteable , itemData )
if ( Radius == nil ) then
Radius = 1
end
if ( OnlyWriteable == nil ) then
OnlyWriteable = false
end
for x =- Radius , Radius do
for y =- Radius , Radius do
local field = world : getField ( position ( CenterPos.x + x , CenterPos.y + y , CenterPos.z ) )
if field ~= nil then
local itemCount = field : countItems ( )
if ( itemCount > 0 ) then
if ( OnlyWriteable ) then
itemCount = 1
end
for i = 0 , itemCount - 1 do
local item = field : getStackItem ( i )
if ( item.id == ItemId ) then
item.pos . x = CenterPos.x + x
item.pos . y = CenterPos.y + y
item.pos . z = CenterPos.z
if itemData then
local dataValue = item : getData ( itemData.key )
if dataValue == itemData.value then
return item , ( i == 0 )
end
else
return item , ( i == 0 )
end
end
end
end
2017-11-17 11:51:04 +01:00
end
2022-10-09 12:34:43 +02:00
end
2012-11-28 15:58:19 +01:00
end
2022-10-09 12:34:43 +02:00
return nil , nil
2012-11-30 20:49:02 +01:00
end
2012-12-17 19:31:33 +01:00
2015-01-22 12:21:21 -06:00
-- Checks if a given position is located in the prison mine.
-- @param posStruct Pos The position to check.
-- @return bool True if position is located in Prison mine, false otherwise.
2015-01-22 14:45:14 -06:00
function M . isInPrison ( Pos )
2015-01-22 12:21:21 -06:00
if ( Pos.z == - 40 ) then
2016-03-29 21:06:15 +02:00
return true
2015-01-22 12:21:21 -06:00
end
2016-03-29 21:06:15 +02:00
return false
2015-01-22 12:21:21 -06:00
end
2017-11-16 14:34:16 +01:00
--- Convert a RGB colour to a HSV color.
-- @param red the red colour component in a range from 0 to 255
-- @param green the green colour component in a range from 0 to 255
-- @param blue the blue colour component in a range from 0 to 255
-- @return hue, saturation, value colour components. Hue in a range from 0<> to 360<36> . Saturation and value in a range
2014-11-23 13:12:53 +01:00
-- from 0 to 1
function M . RGBtoHSV ( red , green , blue )
local max = math.max ( red , green , blue )
local min = math.min ( red , green , blue )
if max > 255 or min < 0 then
2017-11-16 14:34:16 +01:00
error ( " The colour values are rgb to hsv conversation are out of bounds. " )
2014-11-23 13:12:53 +01:00
end
local delta = max - min
local hue
if delta == 0 then
hue = 0
elseif max == red and green >= blue then
hue = 60 * ( green - blue ) / delta
elseif max == red and green < blue then
hue = 60 * ( green - blue ) / delta + 360
elseif max == green then
hue = 60 * ( blue - red ) / delta + 120
elseif max == blue then
hue = 60 * ( red - green ) / delta + 240
end
local saturation
if max == 0 then
saturation = 0
else
saturation = 1 - min / max
end
return hue , saturation , max / 255
end
2017-11-16 14:34:16 +01:00
--- Convert a HSV colour value to a RGB colour value.
2014-11-23 13:12:53 +01:00
-- @param hue The hue value in degrees. Values larger then 360<36> are wrapped until they fit the range from 0<> to 360<36>
-- @param saturation The saturation value in a range from 0 to 1
2017-11-16 14:34:16 +01:00
-- @param value The colour value in a range from 0 to 1
-- @return red, green, blue colour value in a range from 0 to 255 as integer value
2014-11-23 13:12:53 +01:00
function M . HSVtoRGB ( hue , saturation , value )
2014-11-25 19:45:51 +01:00
local realHue = hue % 360
2014-11-23 13:12:53 +01:00
if saturation < 0 or saturation > 1 then
error ( " Saturation is out of bounds: " + saturation )
end
if value < 0 or value > 1 then
error ( " Saturation is out of bounds: " + value )
end
local c = saturation * value
2014-12-25 01:15:12 +01:00
local x = c * ( 1 - math.abs ( ( ( realHue / 60 ) % 2 ) - 1 ) )
2014-11-23 13:12:53 +01:00
local m = value - c
local red = 0
local green = 0
local blue = 0
if realHue >= 0 and realHue < 60 then
red = c
green = x
elseif realHue >= 60 and realHue < 120 then
red = x
green = c
elseif realHue >= 120 and realHue < 180 then
green = c
blue = x
elseif realHue >= 180 and realHue < 240 then
green = x
blue = c
elseif realHue >= 240 and realHue < 300 then
red = x
blue = c
elseif realHue >= 300 and realHue < 360 then
red = c
blue = x
end
local function fixValue ( v )
return math.floor ( ( v + m ) * 255 + 0.5 )
end
return fixValue ( red ) , fixValue ( green ) , fixValue ( blue )
end
2015-09-21 17:54:48 +02:00
-- Checks wether an item has a special name, description or the specialItem data set
-- @param the item to be checked
-- @return bool True if it is special, else false
function M . isSpecialItem ( item )
if item : getData ( " nameEn " ) ~= " " or item : getData ( " descriptionEn " ) ~= " " or item : getData ( " specialItem " ) ~= " " then
return true
end
return false
end
2017-08-17 14:41:36 +02:00
--[[Binary functions
Checks whether bit n is set or not
bitN must be a number in between 1 and 15 ( limit 16 bit integer )
2017-09-14 18:09:43 +02:00
@ return bool : true if set
2017-08-17 14:41:36 +02:00
] ] --
function M . isBitSet ( checkedValue , bitN )
local bitNumber = tonumber ( bitN )
local valueNumber = tonumber ( checkedValue )
if bitNumber == nil or valueNumber == nil then
return false
end
if bitNumber < 1 or bitNumber > 15 then
return false
end
local bitValue = math.pow ( 2 , bitNumber - 1 )
if bit32.band ( valueNumber , bitValue ) == 0 then
return false
end
return true
end
--[[Binary functions
Set bit n
bitN must be a number in between 1 and 15 ( limit 16 bit integer )
2020-12-06 23:34:30 +01:00
setBit must be a number
2017-08-17 14:41:36 +02:00
@ return int : setValue
] ] --
function M . addBit ( setValue , bitN )
local bitNumber = tonumber ( bitN )
local valueNumber = tonumber ( setValue )
if bitNumber == nil or valueNumber == nil then
return nil
end
if bitNumber < 1 or bitNumber > 15 then
return valueNumber
end
if not M.isBitSet ( valueNumber , bitNumber ) then
valueNumber = valueNumber + math.pow ( 2 , bitNumber - 1 )
end
return valueNumber
end
--[[Binary functions
Remove bit n
bitN must be a number in between 1 and 15 ( limit 16 bit integer )
2020-12-06 23:34:30 +01:00
setBit must be a number
2017-08-17 14:41:36 +02:00
@ return int : setValue
] ] --
function M . removeBit ( setValue , bitN )
local bitNumber = tonumber ( bitN )
local valueNumber = tonumber ( setValue )
if bitNumber == nil or valueNumber == nil then
return nil
end
if bitNumber < 1 or bitNumber > 15 then
return valueNumber
end
if M.isBitSet ( valueNumber , bitNumber ) then
valueNumber = valueNumber - math.pow ( 2 , bitNumber - 1 )
end
return valueNumber
end
--[[Binary functions
2017-09-14 18:09:43 +02:00
Returns the number of set bits .
2020-12-06 23:34:30 +01:00
setBit must be a number
2017-08-17 14:41:36 +02:00
@ return int : number of bits = 1
] ] --
function M . countBit ( checkedValue )
local bitNumber = 1
local valueNumber = tonumber ( checkedValue )
local countBit = 0
if valueNumber == nil then
return 0
end
for i = 1 , 15 do
if bit32.band ( valueNumber , bitNumber ) > 0 then
countBit = countBit + 1
end
bitNumber = bitNumber * 2
end
return countBit
end
2017-11-17 11:51:04 +01:00
--[[Item name
Considers special item names
@ return string ] ] --
function M . getItemName ( item , lang )
local specialname = ( lang == Player.german and item : getData ( " nameDe " ) or item : getData ( " nameEn " ) )
if ( M.IsNilOrEmpty ( specialname ) ) then
return world : getItemName ( item.id , lang )
else
return specialname
end
end
--[[warp a character back
@ return : real distance ( 0 if not warped ) ] ] --
2024-07-06 11:48:09 +02:00
function M . pushBack ( user , distance , centerPos )
local directionX = 0
local directionY = 0
local xDiff = 0
local yDiff = 0
if user.pos . x > centerPos.x then
xDiff = user.pos . x - centerPos.x
directionX = 1
elseif user.pos . x < centerPos.x then
xDiff = centerPos.x - user.pos . x
directionX = - 1
2017-11-17 11:51:04 +01:00
end
2022-10-10 18:22:43 +02:00
2024-07-06 11:48:09 +02:00
if user.pos . y > centerPos.y then
yDiff = user.pos . y - centerPos.y
directionY = 1
elseif user.pos . y < centerPos.y then
yDiff = centerPos.y - user.pos . y
directionY = - 1
end
local distY = 0
local distX = 0
if yDiff >= xDiff / 2 and xDiff >= yDiff / 2 then -- diagonal pushback when they are both more or the same as half of the other
distY = directionY * ( distance / 2 )
distX = directionX * ( distance - ( distance / 2 ) )
elseif yDiff >= xDiff / 2 then -- Only pushback on y axis when X is less than half the Y diff
distY = directionY * distance
elseif xDiff >= yDiff / 2 then -- same as above but x/y inverted
distX = directionX * distance
end
2017-11-17 11:51:04 +01:00
2024-07-06 11:48:09 +02:00
local posX = user.pos . x + distX
local posY = user.pos . y + distY
2017-11-17 11:51:04 +01:00
local posZ = user.pos . z
2024-07-06 11:48:09 +02:00
local targetPos = position ( posX , posY , posZ )
2017-11-17 11:51:04 +01:00
local isNotBlocked = function ( pos )
if world : getField ( pos ) : isPassable ( ) then
targetPos = pos
return true
else
return false
end
end
M.CreateLine ( user.pos , position ( posX , posY , posZ ) , isNotBlocked )
local realDistance = user : distanceMetricToPosition ( targetPos )
user : warp ( targetPos )
return realDistance
end
--[[Find items out of a list from inventory and bag
user : The cheched character
itemIdList : Table of item ID ' s or item ID
return : hasItem , returnList
hasItem : true if all items are in inventory
returnList : all found items
] ] --
function M . userHasItems ( user , itemIdList )
local idItem
local numberItem
local numberExists
local returnList = { }
local allItems = false
if type ( itemIdList ) ~= " table " then
itemIdList = { itemIdList }
end
if user : getType ( ) == Character.player and # itemIdList > 0 then
allItems = true
for _ , row in pairs ( itemIdList ) do
if type ( row ) == " table " then
idItem = row [ 1 ]
numberItem = row [ 2 ] or 1
else
idItem = row
numberItem = 1
end
local l = user : getItemList ( idItem )
numberExists = 0
for _ , i in pairs ( l ) do
numberExists = numberExists + i.number
table.insert ( returnList , i )
end
if numberExists < numberItem then
allItems = false
end
end
end
return allItems , returnList
end
--[[ Drop a blood spot on the ground at a specified location.
@ param Posi The location where the blood spot is placed ] ] --
function M . dropBlood ( Posi , bloodWear )
2020-05-15 15:41:56 +02:00
local usedWear = bloodWear or 2
2017-11-17 11:51:04 +01:00
if world : isItemOnField ( Posi ) then
return --no blood on tiles with items on them!
end
local field = world : getField ( Posi )
local tileId = field : tile ( )
if tileId == 6 or tileId == 0 or tileId == 34 then
return -- no blood on water and invisible tiles
end
2020-12-06 23:34:30 +01:00
2017-11-17 11:51:04 +01:00
local Blood = world : createItemFromId ( 3101 , 1 , Posi , true , 333 , nil )
Blood.wear = usedWear
world : changeItem ( Blood )
end
--[[ Drop some of blood. This function drops blood on every tile around the centerPos with a certain probability
@ param centerPos : The center of the bloody area
@ param : bloodProbability 0 - 1
@ param : bllodWear Time the blood remains x 3 min
] ] --
function M . dropSomeBlood ( centerPos , bloodProbability , bloodWear )
2017-11-26 22:00:30 +01:00
local usedProbability = bloodProbability or 0.33
local usedWear = bloodWear or 1
local centerPosX = centerPos.x - 1
local centerPosY = centerPos.y - 1
local centerPosZ = centerPos.z
2017-11-17 11:51:04 +01:00
for i = 1 , 3 do
for j = 1 , 3 do
if math.random ( ) < usedProbability then
2017-11-26 22:00:30 +01:00
M.dropBlood ( position ( centerPosX , centerPosY , centerPosZ ) , usedWear )
2017-11-17 11:51:04 +01:00
end
2017-11-26 22:00:30 +01:00
centerPosX = centerPosX + 1
2017-11-17 11:51:04 +01:00
end
2017-11-26 22:00:30 +01:00
centerPosX = centerPosX - 3
centerPosY = centerPosY + 1
2017-11-17 11:51:04 +01:00
end
end
--[[ Drop much of blood. This function drops blood on about every 3rd tile around the position set as center.
@ param centerPos The center of the bloody area ] ] --
function M . dropMuchBlood ( centerPos )
M.dropSomeBlood ( centerPos , 1 , 2 )
end
--[[Counts player around a position including the levels -2, -1, +1, +2
center pos : center of search
range : range of search
@ return : number of character ] ] --
function M . countPlayersInRangeOf ( centerPos , range )
local users = world : getPlayersInRangeOf ( centerPos , range )
local count = 0
for _ , user in pairs ( users ) do
count = count + 1
end
return count
end
2018-07-18 17:54:14 +02:00
--[[Direction hints]] --
--[[Translate a distance into text
distance : number of tiles
@ return : textDe , textEn ] ] --
function M . getTextForDistance ( distance )
distance = tonumber ( distance )
if distance == nil then
return " unscharf " , " diffuse "
elseif distance < 20 then
return " sehr nah " , " very close "
elseif distance < 60 then
return " nah " , " close "
elseif distance < 200 then
return " fern " , " far "
elseif distance < 500 then
return " sehr fern " , " very far "
else
return " <EFBFBD> u<EFBFBD> erst fern" , " extremely far "
end
end
--[[Translate a direction into text
direction : direction value
@ return : textDe , textEn ] ] --
function M . getTextForDirection ( direction )
if direction == Character.dir_north then
return " Norden " , " north "
elseif direction == Character.dir_northeast then
return " Nordosten " , " northeast "
elseif direction == Character.dir_east then
return " Osten " , " east "
elseif direction == Character.dir_southeast then
return " S<EFBFBD> dosten " , " southeast "
elseif direction == Character.dir_south then
return " S<EFBFBD> den " , " south "
elseif direction == Character.dir_southwest then
return " S<EFBFBD> dwesten " , " southwest "
elseif direction == Character.dir_west then
return " Westen " , " west "
elseif direction == Character.dir_northwest then
return " Nordwesten " , " northwest "
else
return false , false
end
end
--[[Distance hint, a raw expectation where the target position might be
user : start position
targetPos : target position
@ return structure with German and English distance and direction ] ] --
function M . getDistanceHint ( user , targetPos )
if not targetPos then
return false
end
local metricDistance = user : distanceMetricToPosition ( targetPos ) ;
local dir = M.GetDirection ( user.pos , targetPos ) ;
local result = { }
result.direction = { }
result.direction . de , result.direction . en = M.getTextForDirection ( dir )
result.distance = { }
result.distance . de , result.distance . en = M.getTextForDistance ( metricDistance )
return result
end
--[[Number to text
number : integer 0 - 20
languag : Player language
cardinal : false or nil : number , true : cardinals
@ return text ] ] --
function M . numberToText ( number , language , cardinal )
local usedCardinal = cardinal or false
local numberTexts = { }
numberTexts [ 0 ] = { " kein " , " no " , " kein " , " no " }
numberTexts [ 1 ] = { " ein " , " one " , " erste " , " first " }
numberTexts [ 2 ] = { " zwei " , " two " , " zweite " , " second " }
numberTexts [ 3 ] = { " drei " , " three " , " dritte " , " third " }
numberTexts [ 4 ] = { " vier " , " four " , " vierte " , " fourth " }
numberTexts [ 5 ] = { " f<EFBFBD> nf " , " five " , " f<EFBFBD> nfte " , " fivth " }
numberTexts [ 6 ] = { " sechs " , " six " , " sechste " , " sixth " }
numberTexts [ 7 ] = { " sieben " , " seven " , " siebente " , " seventh " }
numberTexts [ 8 ] = { " acht " , " eigth " , " achte " , " eighth " }
numberTexts [ 9 ] = { " neun " , " nine " , " neunte " , " ninth " }
numberTexts [ 10 ] = { " zehn " , " ten " , " zehnte " , " tenth " }
numberTexts [ 11 ] = { " elf " , " eleven " , " elfe " , " eleventh " }
numberTexts [ 12 ] = { " zw<EFBFBD> lf " , " twelf " , " zw<EFBFBD> lfte " , " twelfth " }
numberTexts [ 13 ] = { " dreizehn " , " thirteen " , " dreizehnte " , " thirteenth " }
numberTexts [ 14 ] = { " vierzehn " , " fourteen " , " vierzehnte " , " fourteenth " }
numberTexts [ 15 ] = { " f<EFBFBD> nfzehn " , " fiveteen " , " f<EFBFBD> nfzehnte " , " fiveteenth " }
numberTexts [ 16 ] = { " sechzehn " , " sixteen " , " sechzehnte " , " sixteenth " }
numberTexts [ 17 ] = { " siebzehn " , " seventeen " , " siebzehnte " , " seventeenth " }
numberTexts [ 18 ] = { " achtzehn " , " eightteen " , " achtzehnte " , " eightteenth " }
numberTexts [ 19 ] = { " neunzehn " , " ninteen " , " neunzehnte " , " ninteenth " }
numberTexts [ 20 ] = { " zwanzig " , " twenty " , " zwanzigste " , " twentieth " }
local inumber = tonumber ( number )
if M.IsNilOrEmpty ( inumber ) then
inumber = 0
end
if inumber > 20 then
if usedCardinal == true then
return tostring ( inumber ) .. " . "
else
return tostring ( inumber )
end
else
if usedCardinal == true then
return ( language == Player.german and numberTexts [ inumber ] [ 3 ] or numberTexts [ inumber ] [ 4 ] )
else
return ( language == Player.german and numberTexts [ inumber ] [ 1 ] or numberTexts [ inumber ] [ 2 ] )
end
end
end
2018-07-18 22:25:42 +02:00
--[[Position to number
number : integer
position : related position ] ] --
function M . positionToNumber ( pos )
local number = pos.x + 1000
number = number + ( pos.y + 1000 ) * 10000
number = number + ( pos.z + 100 ) * 10000000
return number
end
--[[Number to postion
number : integer
position : related position ] ] --
function M . numberToPosition ( number )
number = tonumber ( number )
if number == nil then
return position ( 0 , 0 , 0 )
end
local posz = math.floor ( number / 10000000 - 100 )
number = number - ( ( posz + 1000 ) * 100000000 )
local posy = math.floor ( number / 10000 - 1000 )
number = number - ( ( posy + 1000 ) * 10000 )
local posx = math.floor ( number - 1000 )
return position ( posx , posy , posz )
end
2021-08-24 11:30:58 +02:00
2022-06-19 18:56:58 +02:00
function M . isContainerFull ( container , containerId )
local containerSlots = 100
if containerId == Item.wickerBasket then
containerSlots = 58
end
for i = 1 , containerSlots do
local success = container : viewItemNr ( i - 1 )
if not success then --No item was found in that slot
return false
end
end
return true
end
function M . moveItemToBackpack ( user , theItem )
local backpack = user : getBackPack ( )
local backpackId = user : getItemAt ( Character.backpack ) . id
if not backpack or M.isContainerFull ( backpack , backpackId ) then -- if there is no backpack or it is full, items fall on the ground instead
local itemStats = world : getItemStatsFromId ( theItem.id )
2022-06-24 16:31:47 +02:00
user : inform ( " Da du keinen Platz im Rucksack hast, f<> llt dieser Gegenstand zu Boden: " .. itemStats.German , " Having no backpack/no space in your backpack to put them in, the pieces of your broken " .. itemStats.English .. " fall to the ground. " )
2022-06-19 18:56:58 +02:00
return false
end
world : erase ( theItem , theItem.number )
backpack : insertItem ( theItem )
return true
2021-03-28 11:11:40 +02:00
end
2021-08-24 11:30:58 +02:00
2022-08-22 12:04:49 +02:00
-- functions for storage of bitwise flags in quest IDs, see alchemy.teaching.playerInventedPotions for an example of its usage
local function questprogressToIndexedObject ( qpg )
if qpg < 0 then
return 2 ^ 31 - 1 - qpg
else
return qpg
end
end
local function indexedObjectToQuestprogress ( number )
if number < 2 ^ 31 then
return number
else
return 2 ^ 31 - 1 - number
end
end
function M . writeBitwise ( user , index , questIdStart )
local base = questIdStart + math.floor ( ( index - 1 ) / 32 )
local baseOffset = math.fmod ( index - 1 , 32 )
local current = questprogressToIndexedObject ( user : getQuestProgress ( base ) )
user : setQuestProgress ( base , indexedObjectToQuestprogress ( bit32.bor ( 2 ^ baseOffset , current ) ) )
end
function M . readBitwise ( user , index , questIdStart )
local base = questIdStart + math.floor ( ( index - 1 ) / 32 )
local baseOffset = math.fmod ( index - 1 , 32 )
local current = questprogressToIndexedObject ( user : getQuestProgress ( base ) )
if bit32.btest ( bit32.lshift ( 1 , baseOffset ) , current ) then
return true
end
return false
end
-- bitwise flag functions end
2022-12-04 15:20:49 +01:00
function M . getCharactersInRangeOfMultiplePositions ( characterType , tableOfPosRange )
local charactersToReturn = { }
for _ , posRange in pairs ( tableOfPosRange ) do
local characters
if characterType == " monsters " then
characters = world : getMonstersInRangeOf ( posRange.pos , posRange.range )
elseif characterType == " players " then
characters = world : getPlayersInRangeOf ( posRange.pos , posRange.range )
elseif characterType == " npcs " then
characters = world : getNPCSInRangeOf ( posRange.pos , posRange.range )
else
characters = world : getCharactersInRangeOf ( posRange.pos , posRange.range )
end
for _ , character in pairs ( characters ) do
local alreadyFoundCharacter = false
for _ , alreadyFound in pairs ( charactersToReturn ) do
if alreadyFound == character then
alreadyFoundCharacter = true
break
end
end
if not alreadyFoundCharacter then
table.insert ( charactersToReturn , character )
end
end
end
return charactersToReturn
end
2016-03-29 21:06:15 +02:00
return M