From 3e669831a05c36a38519b6f22cb1ed3c11837f2f Mon Sep 17 00:00:00 2001 From: Thorbjørn Lindeijer Date: Sat, 29 May 2010 22:11:12 +0200 Subject: Copied basic Lua libs back from tmwserv-data and added dummy data The dummy data is currently mostly empty data though, so still nothing to see as far as the example content is concerned. Reviewed-by: Bertram --- scripts/lua/libmana-constants.lua | 103 +++++++++ scripts/lua/libmana.lua | 448 ++++++++++++++++++++++++++++++++++++++ scripts/lua/npclib.lua | 103 +++++++++ 3 files changed, 654 insertions(+) create mode 100644 scripts/lua/libmana-constants.lua create mode 100644 scripts/lua/libmana.lua create mode 100644 scripts/lua/npclib.lua (limited to 'scripts') diff --git a/scripts/lua/libmana-constants.lua b/scripts/lua/libmana-constants.lua new file mode 100644 index 00000000..227b93fb --- /dev/null +++ b/scripts/lua/libmana-constants.lua @@ -0,0 +1,103 @@ +------------------------------------------------------------- +-- Mana Support Library Constants -- +-- -- +-- Some useful numeric values for use by other scripts. -- +-- -- +---------------------------------------------------------------------------------- +-- Copyright 2008 The Mana World Development Team -- +-- -- +-- This file is part of The Mana Server. -- +-- -- +-- The Mana Server is free software; you can redistribute it and/or modify it -- +-- under the terms of the GNU General Public License as published by the Free -- +-- Software Foundation; either version 2 of the License, or any later version. -- +---------------------------------------------------------------------------------- + +DAMAGE_PHYSICAL = 0 +DAMAGE_MAGICAL = 1 +DAMAGE_OTHER = 2 + +ELEMENT_NEUTRAL = 0 +ELEMENT_FIRE = 1 +ELEMENT_WATER = 2 +ELEMENT_EARTH = 3 +ELEMENT_AIR = 4 +ELEMENT_LIGHTNING = 5 +ELEMENT_METAL = 6 +ELEMENT_WOOD = 7 +ELEMENT_ICE = 8 + +ATTR_PHY_ATK_MIN = 0 +ATTR_PHY_ATK_DELTA = 1 +ATTR_MAG_ATK = 2 +ATTR_PHY_RES = 3 +ATTR_MAG_RES = 4 +ATTR_EVADE = 5 +ATTR_HIT = 6 +ATTR_HP = 7 +ATTR_HP_REGEN = 8 +ELEM_NEUTRAL = 9 +ELEM_FIRE = 10 +ELEM_WATER = 11 +ELEM_EARTH = 12 +ELEM_AIR = 13 +ELEM_SACRED = 14 +ELEM_DEATH = 15 +ATTR_STRENGTH = 16 +ATTR_AGILITY = 17 +ATTR_DEXTERITY = 18 +ATTR_VITALITY = 19 +ATTR_INTELLIGENCE = 20 +ATTR_WILLPOWER = 21 +SKILL_WEAPON_NONE = 100 +SKILL_WEAPON_KNIFE = 101 +SKILL_WEAPON_SWORD = 102 +SKILL_WEAPON_POLEARM = 103 +SKILL_WEAPON_STAFF = 104 +SKILL_WEAPON_WHIP = 105 +SKILL_WEAPON_BOW = 106 +SKILL_WEAPON_SHOOTING = 107 +SKILL_WEAPON_MACE = 108 +SKILL_WEAPON_AXE = 109 +SKILL_WEAPON_THROWN = 110 + +TILESIZE = 32 +HOURS = 3600 +MINUTES = 60 + +TYPE_ITEM = 0; +TYPE_ACTOR = 1; +TYPE_NPC = 2; +TYPE_MONSTER = 3; +TYPE_CHARACTER = 4; +TYPE_EFFECT = 5; +TYPE_OTHER = 6; + +ACTION_STAND = 0; +ACTION_WALK = 1; +ACTION_ATTACK = 2; +ACTION_SIT = 3; +ACTION_DEAD = 4; +ACTION_HURT = 5; + +DIRECTION_DEFAULT = 0; +DIRECTION_UP = 1; +DIRECTION_DOWN = 2; +DIRECTION_LEFT = 3; +DIRECTION_RIGHT = 4; +DIRECTION_INVALID = 5; + +EMOTE_DISGUST = 10000; +EMOTE_SURPRISE = 10001; +EMOTE_HAPPY = 10002; +EMOTE_SAD = 10003; +EMOTE_EVIL = 10004; +EMOTE_WINK = 10005; +EMOTE_ANGEL = 10006; +EMOTE_BLUSH = 10007; +EMOTE_TONGUE = 10008; +EMOTE_GRIN = 10009; +EMOTE_UPSET = 10010; +EMOTE_PERTURBED = 10011; +EMOTE_SPEECH = 10012; +EMOTE_BLAH = 10013; diff --git a/scripts/lua/libmana.lua b/scripts/lua/libmana.lua new file mode 100644 index 00000000..8ea5168b --- /dev/null +++ b/scripts/lua/libmana.lua @@ -0,0 +1,448 @@ +------------------------------------------------------------- +-- Mana Support Library -- +-- -- +-- Functions which are called by the game engine and -- +-- helper functions useful for writing other scripts. -- +-- -- +---------------------------------------------------------------------------------- +-- Copyright 2008 The Mana World Development Team -- +-- -- +-- This file is part of The Mana Server. -- +-- -- +-- The Mana Server is free software; you can redistribute it and/or modify it -- +-- under the terms of the GNU General Public License as published by the Free -- +-- Software Foundation; either version 2 of the License, or any later version. -- +---------------------------------------------------------------------------------- + +require "scripts/lua/libmana-constants" + + +-- Table that associates to each NPC pointer the handler function that is +-- called when a player starts talking to an NPC. +local npc_talk_functs = {} +local npc_update_functs = {} + +-- Table that associates to each Character pointer its state with respect to +-- NPCs (only one at a time). A state is an array with four fields: +-- . 1: pointer to the NPC the player is currently talking to. +-- . 2: coroutine running the NPC handler. +-- . 3: next event the NPC expects from the server. +-- (1 = npc_next, 2 = npc_choose, 3 = quest_reply, 4 = 1+3) +-- . 4: countdown (in minutes) before the state is deleted. +-- . 5: name of the expected quest variable. (optional) +local states = {} + +-- Array containing the function registered by atinit. +local init_fun = {} + +-- Tick timer used during update to clean obsolete states. +local timer + +-- Creates an NPC and associates the given handler. +-- Note: Cannot be called until map initialization has started. +function create_npc(name, id, x, y, talkfunct, updatefunct) + local npc = mana.npc_create(name, id, x, y) + if talkfunct then npc_talk_functs[npc] = talkfunct end + if updatefunct then npc_update_functs[npc] = updatefunct end + return npc +end + +-- Waits for the player to acknowledge the previous message, if any. +function do_wait() + coroutine.yield(0) +end + +-- Sends an npc message to a player. +-- Note: Does not wait for the player to acknowledge the message. +function do_message(npc, ch, msg) + -- Wait for the arrival of a pending acknowledgment, if any. + coroutine.yield(0) + mana.npc_message(npc, ch, msg) + -- An acknowledgment is pending, but do not wait for its arrival. + coroutine.yield(1) +end + +-- Sends an NPC question to a player and waits for its answer. +function do_choice(npc, ch, ...) + -- Wait for the arrival of a pending acknowledgment, if any. + coroutine.yield(0) + mana.npc_choice(npc, ch, ...) + -- Wait for player choice. + return coroutine.yield(2) +end + +-- Sends an NPC integer ask to a player and waits for its answer. +function do_ask_integer(npc, ch, min_num, max_num, ...) + -- Wait for the arrival of a pending acknowledgment, if any. + coroutine.yield(0) + mana.npc_ask_integer(npc, ch, min_num, max_num, ...) + -- Wait for player answer. + return coroutine.yield(2) +end + +-- Sends an NPC string ask to a player and waits for its answer. +function do_ask_string(npc, ch) + -- Wait for the arrival of a pending acknowledgment, if any. + coroutine.yield(0) + mana.npc_ask_string(npc, ch) + -- Wait for player answer. + return coroutine.yield(2) +end + +-- Sends an NPC request to send letter to a player and waits for them to +-- send the letter. +function do_post(npc, ch) + coroutine.yield(0) + mana.npc_post(npc, ch) + return coroutine.yield(1) +end + +-- Gets the value of a quest variable. +-- Calling this function while an acknowledment is pending is desirable, so +-- that lag cannot be perceived by the player. +function get_quest_var(ch, name) + -- Query the server and return immediatly if a value is available. + local value = mana.chr_get_quest(ch, name) + if value then return value end + -- Wait for database reply. + return coroutine.yield(3, name) +end + +-- Gets the post for a user. +function getpost(ch) + mana.chr_get_post(ch) + return coroutine.yield(3) +end + +-- Processes as much of an NPC handler as possible. +local function process_npc(w, ...) + local co = w[2] + local pending = (w[3] == 4) + local first = true + while true do + local b, v, u + if first then + -- First time, resume with the arguments the coroutine was waiting for. + b, v, u = coroutine.resume(co, ...) + first = false + else + -- Otherwise, simply resume. + b, v, u = coroutine.resume(co) + end + + if not b then print("LUA error: ", v)end + + if not b or not v then + -- Either there was an error, or the handler just finished its work. + return + elseif v == 2 then + -- The coroutine needs a user choice from the server, so wait for it. + w[3] = 2 + break + elseif v == 3 then + -- The coroutine needs the value of a quest variable from the server. + w[5] = u + if pending then + -- The coroutine has also sent a message to the user, so do not + -- forget about it, as it would flood the user with new messages. + w[3] = 4 + else + w[3] = 3 + end + break + elseif pending then + -- The coroutine is about to interact with the user. But the previous + -- action has not been acknowledged by the user yet, so wait for it. + w[3] = 1 + break + elseif v == 1 then + -- A message has just been sent. But the coroutine can keep going in case + -- there is still some work to do while waiting for user acknowledgment. + pending = true + end + end + -- Restore the countdown, as there was some activity. + w[4] = 5 + return true +end + +-- Called by the game whenever a player starts talking to an NPC. +-- Creates a coroutine based on the registered NPC handler. +function npc_start(npc, ch) + states[ch] = nil + local h = npc_talk_functs[npc] + if not h then return end + local w = { npc, coroutine.create(h) } + if process_npc(w, npc, ch) then + states[ch] = w + if not timer then + timer = 600 + end + end + -- coroutine.resume(w) + -- do_npc_close(npc, ch) +end + +function do_npc_close(npc, ch) + mana.npc_end(npc, ch) +end + +-- Called by the game whenever a player keeps talking to an NPC. +-- Checks that the NPC expects it, and processes the respective coroutine. +function npc_next(npc, ch) + local w = states[ch] + if w then + local w3 = w[3] + if w3 == 4 then + w[3] = 3 + return + elseif w3 == 1 and process_npc(w) then + return + end + end + states[ch] = nil +end + +-- Called by the game whenever a player selects a particular reply. +-- Checks that the NPC expects it, and processes the respective coroutine. +function npc_choose(npc, ch, u) + local w = states[ch] + if not (w and w[1] == npc and w[3] == 2 and process_npc(w, u)) then + states[ch] = nil + end +end + +function npc_integer(npc, ch, u) + local w = states[ch] + if not (w and w[1] == npc and w[3] == 2 and process_npc(w, u)) then + states[ch] = nil + end +end + +function npc_string(npc, ch, u) + local w = states[ch] + if not (w and w[1] == npc and w[3] == 2 and process_npc(w, u)) then + states[ch] = nil + end +end + +-- Called by the game when a player sends a letter. +function npc_post(npc, ch, sender, letter) + local w = states[ch] + if not (w and w[1] == npc and w[3] == 1 and process_npc(w, sender, letter)) then + states[ch] = nil + end +end + +-- Called by the game whenever the value of a quest variable is known. +-- Checks that the NPC expects it, and processes the respective coroutine. +-- Note: the check for NPC correctness is missing, but it should never matter. +function quest_reply(ch, name, value) + local w = states[ch] + if w then + local w3 = w[3] + if (w3 == 3 or w3 == 4) and w[5] == name then + w[5] = nil + if process_npc(w, value) then + return + end + end + end + states[ch] = nil +end + +function post_reply(ch, sender, letter) + local w = states[ch] + if w then + local w3 = w[3] + if (w3 == 3 or w3 == 4) then + if process_npc(w, sender, letter) then + return + end + end + end + states[ch] = nil +end + +-- Called by the game every tick for each NPC. +function npc_update(npc) + local h = npc_update_functs[npc]; + if h then h(npc) end; +end + +-- Called by the game every tick. +-- Checks for scheduled function calls +-- Cleans obsolete connections. +function update() + -- check the scheduler + check_schedule() + + -- Run every minute only, in order not to overload the server. + if not timer then return end + timer = timer - 1 + if timer ~= 0 then return end + -- Free connections that have been inactive for 3-4 minutes. + for k, w in pairs(states) do + local t = w[4] - 1 + if t == 0 then + states[k] = nil + else + w[4] = t + end + end + -- Restart timer if there are still some pending states. + if next(states) then + timer = 600 + else + timer = nil + end +end + +-- Registers a function so that is is executed during map initialization. +function atinit(f) + init_fun[#init_fun + 1] = f +end + +-- Called by the game for creating NPCs embedded into maps. +-- Delays the creation until map initialization is performed. +-- Note: Assumes that the "npc_handler" global field contains the NPC handler. +function create_npc_delayed(name, id, x, y) + -- Bind the name to a local variable first, as it will be reused. + local h = npc_handler + atinit(function() create_npc(name, id, x, y, h, nil) end) + npc_handler = nil +end + +-- Called during map initialization. +-- Executes all the functions registered by atinit. +function initialize() + for i,f in ipairs(init_fun) do + f() + end + init_fun = nil +end + + +-- SCHEDULER + +-- Table of scheduled jobs. A job is an array with 3 elements: +-- 0: the UNIX timestamp when it is executed +-- 1: the function which is executed +-- 2: nil when it is a one-time job. Repetition interval is seconds when it is +-- a repeated job. +local scheduler_jobs = {} + +-- compare function used to sort the scheduler_jobs table. +-- the jobs which come first are at the end of the table. +local function job_cmp(job1, job2) + return (job1[0] > job2[0]) +end + +-- checks for jobs which have to be executed, executes them and reschedules +-- them when they are repeated jobs. +function check_schedule() + if #scheduler_jobs==0 then return end + while os.time() > scheduler_jobs[#scheduler_jobs][0] do + -- retreive the job and remove it from the schedule + job = scheduler_jobs[#scheduler_jobs] + table.remove(scheduler_jobs) + -- reschedule the job when it is a repeated job + if job[2] then + schedule_every(job[2], job[1]) + end + -- execute the job + job[1]() + end +end + +-- schedules a function call to be executed once in n seconds +function schedule_in(seconds, funct) + local job = {} + job[0] = os.time() + seconds + job[1] = funct + job[2] = nil + table.insert(scheduler_jobs, job) + table.sort(scheduler_jobs, job_cmp) +end + +-- schedules a function call to be executed at regular intervals of n seconds +function schedule_every(seconds, funct) + local job = {} + job[0] = os.time() + seconds + job[1] = funct + job[2] = seconds + table.insert(scheduler_jobs, job) + table.sort(scheduler_jobs, job_cmp) +end + + +-- DEATH NOTIFICATIONS +local ondeath_functs = {} +local onremove_functs = {} + +-- requests the gameserver to notify the script engine when the being +-- dies and adds a script function to be executed in this case. +function on_death(being, funct) + if ondeath_functs[being] == nil then + ondeath_functs[being] = {} + end + table.insert(ondeath_functs[being], funct) + mana.being_register(being) +end + +-- requests the gameserver to notify the script engine when the being +-- dies and adds a script function to be executed in this case. +function on_remove(being, funct) + if onremove_functs[being] == nil then + onremove_functs[being] = {} + end + table.insert(onremove_functs[being], funct) + mana.being_register(being) +end + +-- called by the engine when a registred being dies. +function death_notification(being) + if type(ondeath_functs[being]) == "table" then + for i,funct in pairs(ondeath_functs[being]) do + funct() + end + ondeath_functs[being] = nil + end +end + +-- called by the engine when a registred being is removed. +function remove_notification(being) + if type(onremove_functs[being]) == "table" then + for i,funct in pairs(onremove_functs[being]) do + funct() + end + onremove_functs[being] = nil + ondeath_functs[being] = nil + end +end + + +-- Below are some convenience methods added to the engine API + +mana.chr_money_change = function(ch, amount) + return mana.chr_inv_change(ch, 0, amount) +end + +mana.chr_money = function(ch) + return mana.chr_inv_count(ch, 0) +end + + + +function cast(ch, arg) + if arg == 1 then + mana.being_say(ch, "Kaaame...Haaame... HAAAAAA!") + end + if arg == 2 then + mana.being_say(ch, "HAA-DOKEN!") + end + if arg == 3 then + mana.being_say(ch, "Sonic BOOM") + end + +end diff --git a/scripts/lua/npclib.lua b/scripts/lua/npclib.lua new file mode 100644 index 00000000..9edfcbd9 --- /dev/null +++ b/scripts/lua/npclib.lua @@ -0,0 +1,103 @@ +---------------------------------------------------------- +-- Library for commonly used NPC scripts -- +-- -- +-- -- +-- Any NPC update function or talk function which could -- +-- be used for NPCs on more than one map should be -- +-- placed here. -- +-- -- +---------------------------------------------------------------------------------- +-- Copyright 2008 The Mana World Development Team -- +-- -- +-- This file is part of The Mana Server. -- +-- -- +-- The Mana Server is free software; you can redistribute it and/or modify it -- +-- under the terms of the GNU General Public License as published by the Free -- +-- Software Foundation; either version 2 of the License, or any later version. -- +---------------------------------------------------------------------------------- + +module("npclib", package.seeall); + + +-- Update function walkaround_small +-- makes the NPC walk around in a 64x64 pixel square around its start location. +-- Useful for NPCs which are supposed to stay on a specific spot but +-- move a bit from time to time. + +local wasmall_timer = {} +local wasmall_startx = {} +local wasmall_starty = {} + +function walkaround_small(npc) + if not wasmall_timer[npc] then + wasmall_timer[npc] = 1 + wasmall_startx[npc] = mana.posX(npc) + wasmall_starty[npc] = mana.posY(npc) + end + + wasmall_timer[npc] = wasmall_timer[npc] + 1 + + if wasmall_timer[npc] == 100 then + wasmall_timer[npc] = math.random(1, 10) + local x = math.random(-32, 32) + wasmall_startx[npc] + local y = math.random(-32, 32) + wasmall_starty[npc] + mana.being_walk(npc, x, y, 2) + end +end + + +-- Update function walkaround_wide +-- makes the NPC walk around in a 256x256 pixel square around its start +-- location. Useful for NPCs which are supposed to be found near a specific +-- location but not nailed to the floor. + +local wawide_timer = {} +local wawide_startx = {} +local wawide_starty = {} + +function walkaround_wide(npc) + if not wawide_timer[npc] then + wawide_timer[npc] = 1 + wawide_startx[npc] = mana.posX(npc) + wawide_starty[npc] = mana.posY(npc) + end + + wawide_timer[npc] = wawide_timer[npc] + 1 + + if wawide_timer[npc] == 50 then + wawide_timer[npc] = math.random(1, 10) + local x = math.random(-128, 128) + wawide_startx[npc] + local y = math.random(-128, 128) + wawide_starty[npc] + mana.being_walk(npc, x, y, 2) + end +end + + +-- Update function walkaround_map +-- makes the NPC wander around the whole map. Useful when the players are +-- supposed to search a bit for the NPC. + +local wam_timer = {} + +function walkaround_map(npc) + if not wam_timer[npc] then + wam_timer[npc] = 1 + end + + wam_timer[npc] = wam_timer[npc] + 1 + + if wam_timer[npc] == 50 then + wam_timer[npc] = math.random(1, 10) + local x = math.random(-128, 128) + mana.posX(npc) + local y = math.random(-128, 128) + mana.posY(npc) + mana.being_walk(npc, x, y, 2) + end +end + +-- Allows passage of more information to an NPC's talk function +function talk(f, ...) + local a = {...} + return function(npc, ch) + f(npc, ch, a) + end +end -- cgit v1.2.3-60-g2f50