Topic readme.md

Readme, explanation & example

Introduction

This page/document should hopefully help explain how the host handles various bits and pieces relevant to plugins. It includes explanations and a fully working Gerenium Quest example which will be live (or already is) on the test server and subsequently main server. It will be updated as the live script is updated to give an idea of how to make a quest.

NOTE: In the code blocks containing definitions, x4 spaces represent a tab. This is an issue with LDoc replacing tab characters with a fixed number of spaces. It is stressed that for any definitions, tabs rather than spaces are used.

Tools

Lua Documentation
Item Generator

Files

A plugin comprises of just a single lua file and is usually coupled with a zone file.

Zone/Map File explanation

A zone/map file (they're the same thing, the terms are interchangable) defines a map or area on the server. it represents one quadrant on the map either in space or the underspace. Cythris, TYV, Starbase etc are each defined in separate zone files. In a zone file, the majority of content for a zone is defined - items, mobs, tiles, fields.

Valid definitions

Zone files can have a number of definitions for different objects ingame. Allowed definitions include:

  • #DEF_ITEM
  • #DEF_PLUGIN
  • #DEF_COMBINE
  • #DEF_MOBILE

The format for these is as followed with x4 spaces replaced with \t (tab). The initial field is case-insensitive.

 #DEF_COMBINE    FINAL_ITEM_ID    SKILL_REQUIRED    LEVEL    ITEM_REQUIRED_ID    ITEM_REQUIRED_ID    ITEM_REQUIRED_ID    ITEM_REQUIRED_ID    ITEM_REQUIRED_ID    ITEM_REQUIRED_ID    ITEM_REQUIRED_ID    ITEM_REQUIRED_ID    ITEM_REQUIRED_ID

 #DEF_PLUGIN    file    Name

Both #DEF_ITEM, #DEF_COMBINE, please use the web tools provided to generate definitions for these.

IDs

The host uses IDs to identify everything however there are a few "special" ranges. Any ID below 10000 is absolute - it will not be changed, it will stick with what it's been given. These are reserved for very common or global items such as ores - Silver, Gold etc. Any ID above 10000 inside a zone file will be modified to a calculated offset for that zone. This means that it is possible to have relative IDs in zonefiles eliminating the need to have to keep track of what item numbers do what. For example, we may have a zonefile Cythris.txt:

 #DEF_ITEM    10001    WEAP    TestWeapon    5    7    0    2    1    0    20    0    2    4    0    30    5000

It is then possible in the Lua file to find TestWeapon by using the item(id) function which handles fixing up offsets for you, e.g:

 local testweaponid = item(plugin_id, 1)

For all quests it is necessary to use IDs beginning from 10000.

Lua file

The lua file is the quest script and has to implement at a minimum, 3 functions.

Each plugin doesn't have to implement any functions although it is recommended to use the init(id) to initialize some variables and cache the plugin id

 plugin_id
 function init(id)
     plugin_id = id
 end

The server will run the init function once when the script is definition is read at startup or when the script is reloaded. It can be used for things such as adding chat to mob and creating the start callback as seen in the Gerenium example further down.

All functions called from Lua -> HS Server are in a hs namespace (e.g, hs.give_exp)

Hooks

It is now possible for the script to 'hook' certain host functions. These functions all end with a _hook and are not in the hs namespace. These are fired only when the event occurs and only if the server contains a certain hook. An example:


 function player_sell_hook(playerid, planetid, itemid, quantity)
     local planet = get_planet_details(planetid)
     create_news_message(string.format("%s sold x%d [%d] to %s", get_player_name(playerid), quantity, itemid, planet["name"])
 end

This will broadcast a message every time a player sells something to a planet.


Gerenium Quest Example

StarbaseL1.txt

 #DEF_ZONETITLE    Starbase Level 1

 #DEF_ITEM    10001    QUEST    Gerenium Ore    26    24    1    0    0    0    0    0    0    0    0    0    1
 #DEF_ITEM    10002    QUEST    Gerenium Container    1    26    1    0    0    0    0    0    0    0    0    0    1
 #DEF_ITEM    10003    QUEST    Contained Gerenium    1    10    1    0    0    0    0    0    0    0    0    0    1

 #Def_Item    10004    SYSTEM    Gerenium Plate    33    26    0    3    4    10    3    0    0    0    0    1    250
 #Def_Item    10005    SYSTEM    Gerenium Plate    34    26    0    3    4    10    3    0    0    0    0    2    250
 #Def_Item    10006    SYSTEM    Gerenium Plate    35    26    0    3    3    10    3    0    0    0    0    3    250
 #Def_Item    10007    SYSTEM    Gerenium Plate    36    26    0    3    3    10    3    0    0    0    0    4    250

 #DEF_COMBINE    10003    0    0    10001    10001    10001    10001    10001    10001    10002    0    0    0

 #Def_Plugin    gerenium.lua    Gerenium Quest

Gerenium.lua

 plugin_id = 0
 quest_level = 30

 fes_rebels = {}
 persistent = {}

 function init(id)
     plugin_id = id

     fes_rebels["defmob"] = find_mob_by_name("Fes Rebels")

     -- Set up all the chat messages for Fes Rebels
     hs.add_talk_to_mob(plugin_id, fes_rebels["defmob"],"[CLICK]","Hello there, %P, I am the famous and immobile %N. Do you need a [job]?")

     hs.add_talk_to_mob(plugin_id, fes_rebels["defmob"],"job","Great, I think I'm going to be able to help you. A slight distance away in the west lay the Scythe fleet.")
     hs.add_talk_to_mob(plugin_id, fes_rebels["defmob"],"job","They hold a rare ore called [gerenium]")

     hs.add_talk_to_mob(plugin_id, fes_rebels["defmob"],"gerenium","Yes, by combining SIX (6) gerenium in a container, it will produce a strong compound used for armor.")
     hs.add_talk_to_mob(plugin_id, fes_rebels["defmob"],"gerenium","Would you like to [start] this quest?")

     hs.add_talk_to_mob_callback(plugin_id, fes_rebels["defmob"],"start","", "start")

     --These responses require the quest to be active in order to be executed
     hs.add_talk_to_mob_callback(plugin_id, fes_rebels["defmob"],"container","Here is one container. Fabricate it (/fabricate) it with 6 Gerenium Ore to create Contained Gerenium.", "giveContainer")
     hs.add_talk_to_mob(plugin_id, fes_rebels["defmob"],"container","Once you have Contained Gerenium, come back and ask me for a [Fore],[Aft],[Port] or [sb] plate.",1)

     --allow the player to manually try to finish
     hs.add_talk_to_mob_callback(plugin_id, fes_rebels["defmob"],"finish","I hope we meet again.","finish")

     hs.add_talk_to_mob_callback(plugin_id, fes_rebels["defmob"],"fore","Let me check if you have the ore.", "handleFore")
     hs.add_talk_to_mob_callback(plugin_id, fes_rebels["defmob"],"port","Let me check if you have the ore.", "handlePort")
     hs.add_talk_to_mob_callback(plugin_id, fes_rebels["defmob"],"aft","Let me check if you have the ore.", "handleAft")
     hs.add_talk_to_mob_callback(plugin_id, fes_rebels["defmob"],"sb","Let me check if you have the ore.", "handleSb")

     -- allow the player to abort the quest
     -- callback must be executed only if player is on this quest
     hs.add_talk_to_mob_callback(plugin_id, fes_rebels["defmob"],"abort","", "abort")

     -- set_quest_summary("This quest requires farming of certain materials from the Scythe Fleet for Fes Rebels.")
     -- set_quest_summary("In return for your work, you will be rewarded with a collection of Gerenium plates with generous low-level stats.")
     -- set_quest_summary("The quest can be repeated infinitely but experience will only be rewarded for the first completion.")

     -- Save his ID to a global variable so we don't have to run a search for him again
 end

 function start(playerid, mobid)
     log("Start was called ["..playerid.."]["..mobid.."]")
     --Check if we have a record of this player starting the quest before
     if(persistent[playerid] == nil) then
         persistent[playerid] = {}
     elseif(persistent[playerid]["started"] == "y") then
         log("Firing started")
         create_chat(mobid, playerid, "You have already started this quest. You may still [abort] this quest")
         create_chat(mobid, playerid, "Travel West from Cythris Prime and enter the Scyhte system.")
         create_chat(mobid, playerid, "Destroy the Scythe infestation and collect their precious Gerenium ore")
         create_chat(mobid, playerid, "Once you have 6 (/fabricate) them with a gerenimum [container] and return to me for you reward")
     else
         log("Firing not started")
         --Set this player as started
         persistent[playerid]["started"] = "y"
         create_chat(mobid, playerid, "Good. I can provide you with a [container] if needs be. I look forward to seeing you later.")
     end

 end

 function handleFore(playerid, mobid)
     --Call handleplate with ID of a Fore gerenium plate and the prefix
     handlePlate(item(plugin_id, 4), "fore", playerid, mobid)
 end

 function handleAft(playerid, mobid)
     --Call handleplate with ID of an Aft gerenium plate and the prefix
     handlePlate(item(plugin_id, 5), "aft", playerid, mobid)
 end

 function handlePort(playerid, mobid)
     --Call handleplate with ID of a Port gerenium plate and the prefix
     handlePlate(item(plugin_id, 6), "port", playerid, mobid)
 end

 function handleSb(playerid, mobid)
     --Call handleplate with ID of a Starboard gerenium plate and the prefix
     handlePlate(item(plugin_id, 7), "sb", playerid, mobid)
 end

 --Handle plate function, will check for an ID and give an item if the player has contained gerenium
 function handlePlate(id, side, playerid, mobid)
     --check if they've got a contained gerenium
     local contained_gerenium = item(plugin_id, 3)
     if(get_player_item_count(playerid, contained_gerenium) > 0) then
         create_chat(mobid, playerid, "Here you go sir.")
         --remove a contained gerenium
         remove_item(playerid, contained_gerenium)
         --give the item an id
         give_item(playerid, id)
         if(persistent[playerid][side] ~= "y") then
             persistent[playerid][side] = "y"
             give_exp(playerid, quest_level)
         end

         -- check if the player has finished
         checkIfFinished()
     else
         --tell the user the problem
         create_chat(mobid, playerid, string.format("Unfortunately I cannot supply a %s plate without a Contained Gerenium.", side))
     end
 end

 function giveContainer(playerid, mobid)
     --Give the item a container
     hs.give_item(playerid, item(plugin_id, 2))
 end

 --Unset the parameters for each plate from the player.
 --This allows the quest to be done more than once, but experience
 --to only be given for one cycle
 local function unsetPlateParameters(playerid)
     --reset the values for this player
     persistent[playerid]["fore"] = nil
     persistent[playerid]["aft"] = nil
     persistent[playerid]["port"] = nil
     persistent[playerid]["sb"] = nil
 end

 -- Prevent players from selling the gerenium (handled host-side anyway but can't hurt
 function player_sell_hook(playerid, planetid, itemid, quantity)
     if(itemid == item(plugin_id, 2)) then
         hs.write_text_to_screen(playerid, "You cannot sell contained gerenium!")
         return 1
     end
     return 0
 end

 -- Check ID of mobs the player kills, if scythe then give an ore
 function player_kill_mob_hook(playerid, mobid, level, name, qx, qy, z)
     if(string.match(name, "Scythe") ~= nil) then
         if(math.random(0,99) < 50) then
             hs.give_item(playerid, item(plugin_id, 1))
             hs.write_text_to_screen(playerid, "You found a Gerenium Ore!", 29)
         end
     end
 end

 --function to check if the player has satisfied all of the requirements
 function checkIfFinished(playerid, mobid)
     local fore = false
     local aft = false
     local port = false
     local sb = false
     local txtadded = false

     --Check which of the plates the player has created
     if(persistent[playerid]["fore"] == "y") then
         fore = true
     end
     if(persistent[playerid]["aft"] == "y") then
         aft = true
     end
     if(persistent[playerid]["port"] == "y") then
         port = true
     end
     if(persistent[playerid]["sb"] == "y") then
         sb = true
     end

     --if they have made a complete set
     if(fore == true and aft == true and port == true and sb == true) then
         --check if the player has already finished the quest before
         local alreadyFinished = persistent[playerid]["geren_finish"]
         --if not, give them some experience and set that they have as a parameter
         if(alreadyFinished ~= "y") then
             give_exp(playerid)
             persistent[playerid]["finished"] = "y"
         end
         --create a message from Fes
         create_chat_from_mob(fes_rebels, playerid, "Well done for completing the quest. Good luck in your future endeavors.")
         --reset the status of the plates so the quest can be done again
         unsetPlateParameters()
         --deactivate the quest from the player
         set_finish()
     else --Guess player has more to do, inform them what's left to do
         local txt = "You can still collect rewards for fabricating "
         if(fore == false) then
             txt = txt .. "Fore ,"
             txtadded = true
         end
         if(aft == false) then
             txt = txt .. "Aft ,"
             txtadded = true
         end
         if(port == false) then
             txt = txt .."Port ,"
             txtadded = true
         end
         -- If we've added text, add the word "and" to make it eaiser to read
         if(txtadded == true) then
             txt = txt .. "and "
         end
         if(sb == false) then
             txt = txt .. "Starboard "
         end
         hs.create_chat(mobid, playerid, txt .. "Gerenium Plates.")
     end
 end

 function finish(playerid, mobid)
     --set the quest finished on the player, this means update() no longer runs for this player
     checkIfFinished(playerid, mobid)
 end

 function abort(playerid, mobid)
     -- basic abort response
     if(persistent[playerid] == nil or persistent[playerid]["started"] == nil) then
         hs.create_chat(mobid, playerid, "You haven't started this quest yet.")
     else
         hs.create_chat(mobid, playerid, "I'm sorry you don't want to help with the struggle.")
         hs.create_chat(mobid, playerid, "Please return to me if you change your mind.")
         -- clear plate progress
         unsetPlateParameters(playerid)
         -- clear quest started param

         persistent[playerid]["started"] = nil
         -- actually abort quest
         -- set_abort()
     end
 end
generated by LDoc 1.2