Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
Description : This a an advance quest system that is not relying on default warcraft 3 quest system. It uses game text as main display of quest details. Currently it is still in developing stage.
Main Goals : The main goals of the system
To create quests within a short time
Support all the basic functions of a quest
Enable user to customize their quest dynamically
To support more features which could be dynamically enabled/disabled depend on user needs
Features : These are the features currently supported by the Quest System. All of these features could be dynamically enable or disable depend on your need
Enable creation of custom quest category
Enable creation of quest item (usually used with custom content/custom inventory system)
Enable dynamically enable/disable a quest depend on how your map event goes
Enable dynamically assign different types of events to each of the quest, either receive/return
Enable registration event for specific when receiving or returning a quest
Enable user to generate randomize quest
Enable user to create own custom quest content
Enable user to create own custom quest reward
Enable user to create own custom quest condition
Enable user to define and categorize their quest
Enable user to define quest receive/update/return sound
Enable user to set a quest as repeatable quest
Requirements : Quest system requires several requirement which will be listed here :
Main Code : The main codes, which included Configuration, API and main system code.
Close
Configuration
API
Main System Code
Here is the configuration that the user could configure toward the QuestSystem, more configuration would be added as the system develop.
JASS:
/**************************************************************************************************/
/*****************************************CONFIGURABLE*********************************************/
/**************************************************************************************************/
globals
/***********************************************************************************************
Below constants are NON-CONFIGURABLE, it is listed here for user to know what type of quest
events exist in the system
***********************************************************************************************/
constant integer EVENT_QUEST_SELECT = 1
constant integer EVENT_QUEST_NEAR = 2
endglobals
globals
/***********************************************************************************************
- EVENT_QUEST_SELECT (receive/return quest via select the unit)
NOTE : This event requires ability with 2 levels
- EVENT_QUEST_NEAR (receive/return quest via near the unit)
***********************************************************************************************/
//Quest default receive and return event
private constant integer EVENT_QUEST_DEFAULT = EVENT_QUEST_SELECT
endglobals
globals
//Dummy unit id (selectable unit)
private constant integer DUMMY_ID = 'h000'
//Dummy unit id for PICK_UP quest type, as replacement of creating item directly
private constant integer QUEST_ITEM_ID = 'h002'
//Dummy hero id for fast viewing quest info
private constant integer QUEST_INFO_ID = 'H001'
//Maximum number of quests that allowed to be taken by player (regarless if enabled QE QuestCateogory or not)
private constant integer MAX_QUEST_PER_PLAYER = 7
//Receive effect path for quest
private constant string EFFECT_PATH_RECEIVE = "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl"
//Return effect path for quest
private constant string EFFECT_PATH_RETURN = "Objects\\RandomObject\\RandomObject.mdl"
//Effect path for talk content
private constant string EFFECT_PATH_TALK = "Objects\\RandomObject\\RandomObject.mdl"
//Effect path for goto content
private constant string EFFECT_PATH_GOTO = "Objects\\RandomObject\\RandomObject.mdl"
//The initial scale for QUEST_ITEM_ID unit
private constant real QUEST_ITEM_SCALE = 1.00
//Maximum range for the player to near the NPC before he could be able to talk to NPC and accept/return the quest(s)
private constant real MAX_SELECT_RANGE = 200.
//Minimum range for the player to near the NPC before he could automatically accept/reeturn the quest(s)
private constant real NEAR_RANGE = 200.
//Every NEAR_TIMER_TIMEOUT, a check will be performed to see if player near and NPC
//High value may lead to overhead timer processes
//Low value may lead to "delay" accept/return
private constant real NEAR_TIMER_TIMEOUT = 0.2
//Minimum range for the player unit to be able to "pick up" the item(actually its a unit)
//High value - may lead to unusual/unexpected/unwanted situation like unit too far from the item and picked it up
//It is recommended not to change the value unless you know what you're doing
//recomended value : 150 (default pick up range defined in gameplay constant)
private constant real PICK_UP_RANGE = 150.
//Coorperate with PICK_UP_RANGE
//High value - may lead to unusual/unexpected/unwanted situation like unit is within pick range but not pick up immediately
//Low value - improve accuraccy for "picking up the item", but may lead to overhead timer processes
//recomended value : 0.1
private constant real MIN_TIME_OUT = 0.1
//neutral player which doesn't use in your map
private constant player NEUTRAL = Player(15)
/*
color list for enhancing your visual when viewing the quest info, modify the color to suit your own needs if needed
A text picture for layout for easier understand which text will get which type of color :
QUEST_NAME_STRING quest name
-----------QUEST_BASIC DESRIPCTION HERE-----------------
-----------QUEST_BASIC DESRIPCTION HERE-----------------
QUEST_REQUIREMENT_STRING
- QUEST_BASIC CONTAIN GOES HERE
- QUEST_BASIC CONTAIN GOES HERE
Example output (based on default) :
Quest Name : Your quest
You quest description locates here
Quest Requirement :
- Kill 2 unit type (0/2)
- Kill 2 unit type 2 (0/2)
So, here is the list that describe which text will get which color :
quest name - white color (user may specific color by adding color code)
QUEST_NAME_STRING, QUEST_REQUIREMENT_STRING - QUEST_COLOR_GENERAL
QUEST_BASIC DESRIPCTION HERE - QUEST_COLOR_DESCRIPTION
QUEST_BASIC CONTAIN GOES HERE - 2 type of colors : For incomplete quest requirement - QUEST_COLOR_INCOMPLETE
For coplete quest requirement - QUEST_COLOR_COMPLETE
*/
constant string QUEST_COLOR_GENERAL = "|cff4da6ff" //moderate blue (77, 166, 255)
constant string QUEST_COLOR_DESCRIPTION = "|cff48d1cc" //medium turquoise (72, 209, 204)
constant string QUEST_COLOR_INCOMPLETE = "|cffff6666" //moderate red (255, 102, 102)
constant string QUEST_COLOR_COMPLETE = "|cff66ff99" //light green (102, 255, 153)
constant string QUEST_DEFAULT_COLOR = "|cffffffff" //white, default color to display quest title at dialog if
//quest title have no color code
//color code for highlight dialog's hotkey
constant string DIALOG_HOTKEY_HIGHLIGHT = "|cffffff00"
//Message that will display if a player trying to accept more quest with max quest list registered
private constant string QUEST_LIST_FULL_MESSAGE = QUEST_COLOR_GENERAL + "Your quest list is fulled."
//integer array used as hotkey for the dialog, input an ASCII character (or integer if you know what to do)
integer array DIALOG_HOTKEY
endglobals
//Init function for user to configure
private function InitEx takes nothing returns nothing
//Configure your own dialog button hotkey here (index starts with 0 and end with MAX_QUEST_PER_PLAYER)
set DIALOG_HOTKEY[0] = '1'
set DIALOG_HOTKEY[1] = '2'
set DIALOG_HOTKEY[2] = '3'
set DIALOG_HOTKEY[3] = '4'
set DIALOG_HOTKEY[4] = '5'
set DIALOG_HOTKEY[5] = '6'
set DIALOG_HOTKEY[6] = '7'
set DIALOG_HOTKEY[7] = 512 //ESC hotkey
endfunction
//A function that adjusts text display's position of y, leave it if you unsure what it is
//Currently not in use
private function GetTextDisplayY takes real NoOfLines returns real
return 0.54 - NoOfLines*0.04
endfunction
//A function that adjusts text display's position of x, leave it if you unsure what it is
private constant function GetTextDisplayX takes nothing returns real
return 0.25
endfunction
Here is the list of API, these API are basic APIs to construct a basic quest
JASS:
//used by PICK_UP content
struct RectSet extends array
static method create takes nothing returns thistype
// -create a new rect set
method addRect takes rect r returns nothing
// -add a rect to the rect set
// -item will spawn randomly within the area of the rect, each rect will only spawn one item
//
// -limitation : the number of defined rect must be >= (number of item to pick up)
endstruct
/*******************************************************************************************/
struct Quest extends array
readonly QuestEvent event
readonly QuestCondition condition
readonly QuestContent content
readonly QuestReward reward
static method create takes nothing returns thistype
// -create a new quest instance
method operator title= takes string questTitle returns nothing
// -assign quest title
method operator description= takes string questDescription returns nothing
// -assign quest description
method createEvent takes nothing returns nothing
// -create new event
// -a quest can have multiple events
// -syntax for setting event is (assuming (q) is Quest instance) :
// q.event.(QuestEvent methods)
method createCondition takes nothing returns nothing
// -create new condition
// -a quest can have multiple conditions
// -syntax for setting condition is (assuming (q) is Quest instance) :
// q.condition.(QuestCondition methods)
method createContent takes nothing returns nothing
// -create new content
// -a quest can have multiple contents
// -syntax for setting content is (assuming (q) is Quest instance) :
// q.content.(QuestContent methods)
method createReward takes nothing returns nothing
// -create new reward
// -a quest can have multiple rewards
// -syntax for setting reward is (assuming (q) is Quest instance) :
// q.reward.(QuestReward methods)
endstruct
/*******************************************************************************************/
struct QuestEvent extends array
method operator receiveUnit= takes unit receiveU returns nothing
// -assign quest receive unit
method operator returnUnit= takes unit returnU returns nothing
// -assign quest return unit
method operator receiveReturnUnit= takes unit rrUnit returns nothing
// -assign quest receive and return unit to (rrUnit)
// -use this method instead if both receive/return unit are same
// -using both above methods works too, but 1 more line needed
method operator abilityId= takes integer aId returns nothing
// -assign receive ability id
// -ability MUST contains 2 level : level 1 for receving, level 2 for returning
// -only used by _SELECT
endstruct
/*******************************************************************************************/
struct QuestCondition extends array
method operator level= takes integer rqLevel returns nothing
// -assign lv condition
method operator addQuest= takes Quest qId returns nothing
// -assign quest condition (example quest1 needed to be completed before quest2 could be received)
endstruct
/*******************************************************************************************/
struct QuestContent extends array
method registerKill takes integer unitType, integer noToKill returns nothing
// -assign killing requirement
// -request a player to kill a (noToKill) of specific unit type (unitType)
//
// -uId : unit-type id
// -noToKill : number to kill
method registerKillObtain takes integer unitType, string itemName, integer noToObtain, real chance returns nothing
// -assign kill and obtain item requirement, item is not directly created
// -request a player to kill specific unit type (unitType) and obtain (noToObtain) of (itemName) with the drop chance
// of (chance)
//
// -uId : unit-type id
// -itemName : the item's name player need to obtain
// -noToObtain : number of "item" to obtain
// -chance : chance to obtain the "item", in probability format ( 0 - 1.00 )
method registerKillObtainItem takes integer unitType, integer iId, integer noToObtain, real chance returns nothing
// -assign kill and obtain item requirement, quest item is directly created
// -request a player to kill specific unit type (unitType) and pick up (noToObtain) specific item type (iId) with the
// drop chance of (chance)
//
// -uId : unit-type id
// -iId : item's id
// -noToObtain : number of item to obtain
// -chance : chance to obtain the item, in probability format ( 0 - 1.00 )
method registerTalk takes unit u, integer aId, string description, string exDescription returns nothing
// -assign talk requirement
// -request a player to talk to a specific unit (u) by selecting unit (u) and cast (aId)
// -uses event : EVENT_QUEST_SELECT
//
// -u : unit to talk
// -aId : ability id (so that player can click the ability and complete requirement, only 1 lv is needed)
// -description : Description that will be displayed in quest info
// -exDescription : Will display at player's screen after player completed talk
method registerTalk2 takes unit u, string description, string exDescription returns nothing
// -assign talk requirement
// -request a player to talk to a specific unit (u) with the talk effect (effectPath) attached on the unit
// -uses event : EVENT_QUEST_NEAR
//
// -u : unit to talk
// -description : Description that will be displayed in quest info
// -exDescription : Will display at player's screen after player completed talk
method registerPickUp takes RectSet rs, integer itemType, integer noToPick, string description returns nothing
// -assign pick up requirement
// -request a player to pick up a number(noToPick) of specific item (itemType) which will spawn within a set of rect (rs)
// (non-repeatable spawn for each rect, max spawn is one)
// -Note : noToPick must be >= number of rects defined in rs
//
// -rs : RectSet which defined a set of rect
// -itemType : item-type id
// -noToPick : number of item to pick
// -description : Description that will be displayed in quest info
method registerGoto takes rect r, string desciption, string completeDescription returns nothing
// -assign go to requirement
// -request a player to go to a specific place (rect) to complete the requirement
//
// -r : rect which player need to enter
// -desciption : The requirement description shown in quest info
// may put something like : "Investigate this area and report back to me"
// -completeDescription : will display the completeDescription out once player went to rect r
// may put something like : "Completed investigation, report it back"
endstruct
/*******************************************************************************************/
struct QuestReward extends array
method operator gold= takes integer amount returns nothing
// -setting gold reward amount
method operator lumber= takes integer amount returns nothing
// -setting lumber reward amount
method operator experience= takes integer amount returns nothing
// -setting experience reward amount
method addItem takes integer itemId, integer amount returns nothing
// -reward (amount) of item type (itemId)
endstruct
/*******************************************************************************************/
function QuestAddUnit takes unit u returns nothing
// -add unit (u) into group which enables (u) to accept/return quest
// -one player can only add 1 unit to group (not more than 1)
// -unit can be non-hero type
// -KNOWN ISSUE:
// 1. Over high level may cause quest init function exceed op-limit
// 2. (save-load only) Currently doesn't support saved quest state, which means if loaded a completed quest
// that is required to unlock next quest, receive effect will not be displayed properly.
function IsQuestReceivedByPlayer takes player p, Quest qId returns boolean
// - return true if quest (qId) is received by player (p)
function IsQuestBeenCompleted takes player p, Quest qId returns boolean
// - return true if quest (qId) been completed by player (p)
// - been completed means once quest been completed for once, this will always
// return true
Here is the main system code which all the quest generating, processing are done through here.
JASS:
library QuestSystem initializer Init /*
*====================================Quest System by DD_LegionTN====================================*
*
* System Description :
* A fully customized quest system that using game text as main display for quests
*
*===================================================================================================*
*
* Version
* v1.0.0
*
*===================================================================================================*
*
* Main Goals :
* - To create quests within a short time
* - Support all the basic functions of a quest
* - Enable user to customize their quest dynamically
* - To support more features which could be dynamically enabled/disabled depend on user needs
*
*===================================================================================================*
*
* These are the optional features currently supported by the Quest System. All of these features
* can be dynamically enabled or disabled depend on your need
*
* Features :
* - Enable creation of custom quest category
* - Enable creation of quest item (usually used with custom content/custom inventory system)
* - Enable dynamically enable/disable a quest depend on how your map event goes
* - Enable dynamically assign different types of events to each of the quest, either receive/return
* - Enable registration event for specific when receiving or returning a quest
* - Enable user to generate randomize quest
* - Enable user to create own custom quest content
* - Enable user to create own custom quest reward
* - Enable user to create own custom quest condition
* - Enable user to define and categorize their quest
* - Enable user to define quest receive/update/return sound
* - Enable user to set a quest as repeatable quest
*
*===================================================================================================*
*
* These are the addition content added toward the original base QuestContent, and can also be enabled
* or disabled depend on your need
*
* Addition Content :
* - "GroupKill" (Enable a group of difference unit-id to be register under the same killing requirement)
* - "CustomContent" (Enable user to create own custom quest content)
*
*===================================================================================================*
*
* Requirement :
* */requires /*
*
* */Dialog /*
* - To ease the creation of dialog
* - Link : https://www.hiveworkshop.com/threads/snippet-dialog-wrapper.240003/
*
* */MultidimensionalArray /*
* - Allows better safty, better syntax and also to easier to manage data storage
* - Link : https://www.hiveworkshop.com/threads/snippet-multidimensional-array.289785/
*
* */TimerUtils /*
* - To ease the creation and data attachment for timer
* - Link : http://www.wc3c.net/showthread.php?t=101322
*
* */UnitDex /*
* - To enable the usage of array for unit related data storage
* - Link : https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
*
* */optional QuestExtension /*
* - Extension of Quest System to enable more features
* (QuestExtension should have version 1.0.0)
*
*===================================================================================================*
*
* Import Instructions :
* 1. Copy all the required libraries into your map.
* 2. Copy the main code "QuestSystem" into your map
* 3. (Optional) Copy 'h000' (DUMMY_ID), 'h002' (QUEST_DUMMY_ID) and 'H001' (QUEST_INFO_ID)
* 4. Configure DUMMY_ID, QUEST_DUMMY_ID and QUEST_INFO_ID (rest configuration are optional)
*
* If you would like to have any add-ons, continue the step, else you're done and may look into
* Basic Examples (Example 1 to Example 5) for example usage
*
* 5. Copy "QuestExtension"
* 6. Choose the add-ons you would like to add, and copy to your map (Alternative way, copy
* all of the add-ons to your map, and disable which you don't need)
*
* You're done, and may look into Basic Examples (which doesn't required add-ons), then to
* Advance Examples (Depends on what you have for optional add-ons) for example usage.
*
*===================================================================================================*/
//! novjass
API :
//used by PICK_UP content
struct RectSet extends array
static method create takes nothing returns thistype
// -create a new rect set
method addRect takes rect r returns nothing
// -add a rect to the rect set
// -item will spawn randomly within the area of the rect, each rect will only spawn one item
//
// -limitation : the number of defined rect must be >= (number of item to pick up)
endstruct
/*******************************************************************************************/
struct Quest extends array
readonly QuestEvent event
readonly QuestCondition condition
readonly QuestContent content
readonly QuestReward reward
static method create takes nothing returns thistype
// -create a new quest instance
method operator title= takes string questTitle returns nothing
// -assign quest title
method operator description= takes string questDescription returns nothing
// -assign quest description
method createEvent takes nothing returns nothing
// -create new event
// -a quest can have multiple events
// -syntax for setting event is (assuming (q) is Quest instance) :
// q.event.(QuestEvent methods)
method createCondition takes nothing returns nothing
// -create new condition
// -a quest can have multiple conditions
// -syntax for setting condition is (assuming (q) is Quest instance) :
// q.condition.(QuestCondition methods)
method createContent takes nothing returns nothing
// -create new content
// -a quest can have multiple contents
// -syntax for setting content is (assuming (q) is Quest instance) :
// q.content.(QuestContent methods)
method createReward takes nothing returns nothing
// -create new reward
// -a quest can have multiple rewards
// -syntax for setting reward is (assuming (q) is Quest instance) :
// q.reward.(QuestReward methods)
endstruct
/*******************************************************************************************/
struct QuestEvent extends array
method operator receiveUnit= takes unit receiveU returns nothing
// -assign quest receive unit
method operator returnUnit= takes unit returnU returns nothing
// -assign quest return unit
method operator receiveReturnUnit= takes unit rrUnit returns nothing
// -assign quest receive and return unit to (rrUnit)
// -use this method instead if both receive/return unit are same
// -using both above methods works too, but 1 more line needed
method operator abilityId= takes integer aId returns nothing
// -assign receive ability id
// -ability MUST contains 2 level : level 1 for receving, level 2 for returning
// -only used by _SELECT
endstruct
/*******************************************************************************************/
struct QuestCondition extends array
method operator level= takes integer rqLevel returns nothing
// -assign lv condition
method operator addQuest= takes Quest qId returns nothing
// -assign quest condition (example quest1 needed to be completed before quest2 could be received)
endstruct
/*******************************************************************************************/
struct QuestContent extends array
method registerKill takes integer unitType, integer noToKill returns nothing
// -assign killing requirement
// -request a player to kill a (noToKill) of specific unit type (unitType)
//
// -uId : unit-type id
// -noToKill : number to kill
method registerKillObtain takes integer unitType, string itemName, integer noToObtain, real chance returns nothing
// -assign kill and obtain item requirement, item is not directly created
// -request a player to kill specific unit type (unitType) and obtain (noToObtain) of (itemName) with the drop chance
// of (chance)
//
// -uId : unit-type id
// -itemName : the item's name player need to obtain
// -noToObtain : number of "item" to obtain
// -chance : chance to obtain the "item", in probability format ( 0 - 1.00 )
method registerKillObtainItem takes integer unitType, integer iId, integer noToObtain, real chance returns nothing
// -assign kill and obtain item requirement, quest item is directly created
// -request a player to kill specific unit type (unitType) and pick up (noToObtain) specific item type (iId) with the
// drop chance of (chance)
//
// -uId : unit-type id
// -iId : item's id
// -noToObtain : number of item to obtain
// -chance : chance to obtain the item, in probability format ( 0 - 1.00 )
method registerTalk takes unit u, integer aId, string description, string exDescription returns nothing
// -assign talk requirement
// -request a player to talk to a specific unit (u) by selecting unit (u) and cast (aId)
// -uses event : EVENT_QUEST_SELECT
//
// -u : unit to talk
// -aId : ability id (so that player can click the ability and complete requirement, only 1 lv is needed)
// -description : Description that will be displayed in quest info
// -exDescription : Will display at player's screen after player completed talk
method registerTalk2 takes unit u, string description, string exDescription returns nothing
// -assign talk requirement
// -request a player to talk to a specific unit (u) with the talk effect (effectPath) attached on the unit
// -uses event : EVENT_QUEST_NEAR
//
// -u : unit to talk
// -description : Description that will be displayed in quest info
// -exDescription : Will display at player's screen after player completed talk
method registerPickUp takes RectSet rs, integer itemType, integer noToPick, string description returns nothing
// -assign pick up requirement
// -request a player to pick up a number(noToPick) of specific item (itemType) which will spawn within a set of rect (rs)
// (non-repeatable spawn for each rect, max spawn is one)
// -Note : noToPick must be >= number of rects defined in rs
//
// -rs : RectSet which defined a set of rect
// -itemType : item-type id
// -noToPick : number of item to pick
// -description : Description that will be displayed in quest info
method registerGoto takes rect r, string desciption, string completeDescription returns nothing
// -assign go to requirement
// -request a player to go to a specific place (rect) to complete the requirement
//
// -r : rect which player need to enter
// -desciption : The requirement description shown in quest info
// may put something like : "Investigate this area and report back to me"
// -completeDescription : will display the completeDescription out once player went to rect r
// may put something like : "Completed investigation, report it back"
endstruct
/*******************************************************************************************/
struct QuestReward extends array
method operator gold= takes integer amount returns nothing
// -setting gold reward amount
method operator lumber= takes integer amount returns nothing
// -setting lumber reward amount
method operator experience= takes integer amount returns nothing
// -setting experience reward amount
method addItem takes integer itemId, integer amount returns nothing
// -reward (amount) of item type (itemId)
endstruct
/*******************************************************************************************/
function QuestAddUnit takes unit u returns nothing
// -add unit (u) into group which enables (u) to accept/return quest
// -one player can only add 1 unit to group (not more than 1)
// -unit can be non-hero type
// -KNOWN ISSUE:
// 1. Over high level may cause quest init function exceed op-limit
// 2. (save-load only) Currently doesn't support saved quest state, which means if loaded a completed quest
// that is required to unlock next quest, receive effect will not be displayed properly.
function IsQuestReceivedByPlayer takes player p, Quest qId returns boolean
// - return true if quest (qId) is received by player (p)
function IsQuestBeenCompleted takes player p, Quest qId returns boolean
// - return true if quest (qId) been completed by player (p)
// - been completed means once quest been completed for once, this will always
// return true
//! endnovjass
/**************************************************************************************************/
/*****************************************CONFIGURABLE*********************************************/
/**************************************************************************************************/
globals
/***********************************************************************************************
Below constants are NON-CONFIGURABLE, it is listed here for user to know what type of quest
events exist in the system
***********************************************************************************************/
constant integer EVENT_QUEST_SELECT = 1
constant integer EVENT_QUEST_NEAR = 2
endglobals
globals
/***********************************************************************************************
- EVENT_QUEST_SELECT (receive/return quest via select the unit)
NOTE : This event requires ability with 2 levels
- EVENT_QUEST_NEAR (receive/return quest via near the unit)
***********************************************************************************************/
//Quest default receive and return event
private constant integer EVENT_QUEST_DEFAULT = EVENT_QUEST_SELECT
endglobals
globals
//Dummy unit id (selectable unit)
private constant integer DUMMY_ID = 'h000'
//Dummy unit id for PICK_UP quest type, as replacement of creating item directly
private constant integer QUEST_ITEM_ID = 'h002'
//Dummy hero id for fast viewing quest info
private constant integer QUEST_INFO_ID = 'H001'
//Maximum number of quests that allowed to be taken by player (regarless if enabled QE QuestCateogory or not)
private constant integer MAX_QUEST_PER_PLAYER = 7
//Receive effect path for quest
private constant string EFFECT_PATH_RECEIVE = "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl"
//Return effect path for quest
private constant string EFFECT_PATH_RETURN = "Objects\\RandomObject\\RandomObject.mdl"
//Effect path for talk content
private constant string EFFECT_PATH_TALK = "Objects\\RandomObject\\RandomObject.mdl"
//Effect path for goto content
private constant string EFFECT_PATH_GOTO = "Objects\\RandomObject\\RandomObject.mdl"
//The initial scale for QUEST_ITEM_ID unit
private constant real QUEST_ITEM_SCALE = 1.00
//Maximum range for the player to near the NPC before he could be able to talk to NPC and accept/return the quest(s)
private constant real MAX_SELECT_RANGE = 200.
//Minimum range for the player to near the NPC before he could automatically accept/reeturn the quest(s)
private constant real NEAR_RANGE = 200.
//Every NEAR_TIMER_TIMEOUT, a check will be performed to see if player near and NPC
//High value may lead to overhead timer processes
//Low value may lead to "delay" accept/return
private constant real NEAR_TIMER_TIMEOUT = 0.2
//Minimum range for the player unit to be able to "pick up" the item(actually its a unit)
//High value - may lead to unusual/unexpected/unwanted situation like unit too far from the item and picked it up
//It is recommended not to change the value unless you know what you're doing
//recomended value : 150 (default pick up range defined in gameplay constant)
private constant real PICK_UP_RANGE = 150.
//Coorperate with PICK_UP_RANGE
//High value - may lead to unusual/unexpected/unwanted situation like unit is within pick range but not pick up immediately
//Low value - improve accuraccy for "picking up the item", but may lead to overhead timer processes
//recomended value : 0.1
private constant real MIN_TIME_OUT = 0.1
//neutral player which doesn't use in your map
private constant player NEUTRAL = Player(15)
/*
color list for enhancing your visual when viewing the quest info, modify the color to suit your own needs if needed
A text picture for layout for easier understand which text will get which type of color :
QUEST_NAME_STRING quest name
-----------QUEST_BASIC DESRIPCTION HERE-----------------
-----------QUEST_BASIC DESRIPCTION HERE-----------------
QUEST_REQUIREMENT_STRING
- QUEST_BASIC CONTAIN GOES HERE
- QUEST_BASIC CONTAIN GOES HERE
Example output (based on default) :
Quest Name : Your quest
You quest description locates here
Quest Requirement :
- Kill 2 unit type (0/2)
- Kill 2 unit type 2 (0/2)
So, here is the list that describe which text will get which color :
quest name - white color (user may specific color by adding color code)
QUEST_NAME_STRING, QUEST_REQUIREMENT_STRING - QUEST_COLOR_GENERAL
QUEST_BASIC DESRIPCTION HERE - QUEST_COLOR_DESCRIPTION
QUEST_BASIC CONTAIN GOES HERE - 2 type of colors : For incomplete quest requirement - QUEST_COLOR_INCOMPLETE
For coplete quest requirement - QUEST_COLOR_COMPLETE
*/
constant string QUEST_COLOR_GENERAL = "|cff4da6ff" //moderate blue (77, 166, 255)
constant string QUEST_COLOR_DESCRIPTION = "|cff48d1cc" //medium turquoise (72, 209, 204)
constant string QUEST_COLOR_INCOMPLETE = "|cffff6666" //moderate red (255, 102, 102)
constant string QUEST_COLOR_COMPLETE = "|cff66ff99" //light green (102, 255, 153)
constant string QUEST_DEFAULT_COLOR = "|cffffffff" //white, default color to display quest title at dialog if
//quest title have no color code
//color code for highlight dialog's hotkey
constant string DIALOG_HOTKEY_HIGHLIGHT = "|cffffff00"
//Message that will display if a player trying to accept more quest with max quest list registered
private constant string QUEST_LIST_FULL_MESSAGE = QUEST_COLOR_GENERAL + "Your quest list is fulled."
//integer array used as hotkey for the dialog, input an ASCII character (or integer if you know what to do)
integer array DIALOG_HOTKEY
endglobals
//Init function for user to configure
private function InitEx takes nothing returns nothing
//Configure your own dialog button hotkey here (index starts with 0 and end with MAX_QUEST_PER_PLAYER)
set DIALOG_HOTKEY[0] = '1'
set DIALOG_HOTKEY[1] = '2'
set DIALOG_HOTKEY[2] = '3'
set DIALOG_HOTKEY[3] = '4'
set DIALOG_HOTKEY[4] = '5'
set DIALOG_HOTKEY[5] = '6'
set DIALOG_HOTKEY[6] = '7'
set DIALOG_HOTKEY[7] = 512 //ESC hotkey
endfunction
//A function that adjusts text display's position of y, leave it if you unsure what it is
//Currently not in use
private function GetTextDisplayY takes real NoOfLines returns real
return 0.54 - NoOfLines*0.04
endfunction
//A function that adjusts text display's position of x, leave it if you unsure what it is
private constant function GetTextDisplayX takes nothing returns real
return 0.25
endfunction
/**************************************************************************************************/
/********************************BELOW ARE NON-CONFIGURABLE****************************************/
/**************************************************************************************************/
/**************************************************************************************************/
//globals init private CONSTANT or ARRAY_CONSTANT which use internally by QuestSystem only
globals
//constant used by setting quest ability level : 2 = quest ready to be completed
private constant integer COMPLETE = 2
//facing angle of ITEM_DUMMY
private constant real QUEST_ITEM_ANGLE = 278.
//Player's hero
unit array HERO
//Player's quest dummy hero (will auto create after assigned a unit to variable HERO)
unit array QUEST_INFO
//used by GetTriggerQuestId()
private Quest TriggerQuestId = 0
//used byGetTriggerPlayerId()
private integer TriggerPlayerId = 0
endglobals
/**************************************************************************************************/
/**************************************************************************************************/
private keyword QuestEvent
private keyword QuestCondition
private keyword QuestContent
private keyword QuestReward
private keyword Internal
private keyword enterRegTrigger
/**************************************************************************************************/
/**************************************************************************************************/
//External Globals
//! runtextmacro optional QEQuestCategory__Globals()
//! runtextmacro optional QEQuestSound__Globals()
//External Structs
//! runtextmacro optional QEGroupKill__UnitIdSet()
//! runtextmacro optional QEQuestCategory__QuestCategory()
//! runtextmacro optional QEQuestSound__QuestSound()
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct for defining set of rect for PICK_UP content type
*/
Internal API - RectSet :
struct RectSet extends array
method setRectList takes nothing returns nothing
- get list of stored rect
- use rectList to access rect
- last array position is set to null for exit looping purpose
method getRegisteredAmount takes nothing returns integer
- return registered amount of rect
*/
struct RectSet extends array
//==========================Globals===============================
//---------------------Creation Globals-----------------------
private static thistype instanceCount = 0
//------------------------------------------------------------
//---------------------Ordinary Globals-----------------------
private integer listCount
//------------------------------------------------------------
//----------------------Public Globals------------------------
static rect array rectList
//------------------------------------------------------------
//----------------------Struct Globals------------------------
/*
list[a] = b
a : listCount (starts from 0)
b : store rect
*/
private Rect1D list
//------------------------------------------------------------
//================================================================
//====================Creation/Destroy Methods====================
static method create takes nothing returns thistype
local thistype this = instanceCount + 1
set instanceCount = this
set .list = Rect1D.create()
set .listCount = 0
return this
endmethod
//================================================================
//=====================Data Assign Methods========================
method addRect takes rect rec returns nothing
set .list[.listCount] = rec
set .listCount = .listCount + 1
endmethod
//================================================================
//======================Accessor Methods==========================
method getRegisteredAmount takes nothing returns integer
return .listCount
endmethod
method setRectList takes nothing returns nothing
local integer i = 0
loop
exitwhen i == .listCount
set rectList[i] = .list[i]
set i = i + 1
endloop
set rectList[i] = null
endmethod
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to Item, which aims to provide :
1. Quest item data assign, included quest id, subset and player
*/
Internal API - Item :
private struct Item extends array
static method assignData takes item it, Quest qId, integer subset, player user returns nothing
- assign (qId), (subset) data to (it), which owned by (user)
static method setData takes item it returns nothing
- set all the data stored in (it)
- enable the access of getting quest id, subset and owner
static method clearData takes item it returns nothing
- clear all data related to (it)
static method getQuestId takes nothing returns Quest
- return quest id stored in item
static method getSubset takes nothing returns integer
- return subset stored in item
static method getOwner takes nothing returns player
- return owner of item
*/
private module ItemInit
private static method onInit takes nothing returns nothing
set itemData = Array2D.create()
endmethod
endmodule
private struct Item extends array
//==========================Globals===============================
//---------------------Ordinary Globals-----------------------
private static integer questId
private static integer subset
private static player owner
//------------------------------------------------------------
//----------------------Struct Globals------------------------
private static Array2D itemData
//------------------------------------------------------------
//================================================================
//=====================Data Assign Methods========================
static method assignData takes item it, Quest qId, integer subset, player user returns nothing
local Array1D a1d = itemData[GetHandleId(it)]
set a1d[1].integer = qId
set a1d[2].integer = subset
set a1d[3].player = user
endmethod
static method clearData takes item it returns nothing
call itemData[GetHandleId(it)].flush()
endmethod
//================================================================
//======================Accessor Methods==========================
static method setData takes item it returns nothing
local Array1D a1d = itemData[GetHandleId(it)]
set thistype.questId = a1d[1].integer
set thistype.subset = a1d[2].integer
set thistype.owner = a1d[3].player
endmethod
static method getQuestId takes nothing returns Quest
return thistype.questId
endmethod
static method getSubset takes nothing returns integer
return thistype.subset
endmethod
static method getOwner takes nothing returns player
return thistype.owner
endmethod
//================================================================
implement ItemInit
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to Timer, which aims to provide :
1. Running specified code once unit target to another unit
after a given period of time
Uses GetUnitId(u) as struct index
*/
Internal API - Timer :
private struct Timer extends array
method start takes unit targetUnit, real speed, real time, code c returns nothing
- start new instance for this unit who ordered to (targetUnit)
- run (c) once (time) is reached
method restart takes real time, code c returns nothing
- restart the timer with (time) running (c)
method terminate takes nothing returns nothing
- terminate the running timer for this unit
- remove unit from group as well
method getTargetUnit takes nothing returns unit
- return targeted unit
method getTargetUnitX takes nothing returns real
- return targeted unit x-axis
method getTargetUnitY takes nothing returns real
- return targeted unit y-axis
method getOrderedUnitMoveSpeed takes nothing returns real
- return ordered unit move speed
static method isUnitInGroup takes unit u returns boolean
- return true if unit is in ordered group
*/
private struct Timer extends array
//==========================Globals===============================
//---------------------Ordinary Globals-----------------------
private timer tim
private unit targetUnit
private real targetUnitX
private real targetUnitY
private real orderedUnitSpeed
private static group orderedGroup = CreateGroup()
//------------------------------------------------------------
//================================================================
//=====================Data Assign Methods========================
method start takes unit target, real speed, real time, code c returns nothing
set .targetUnitX = GetUnitX(target)
set .targetUnitY = GetUnitY(target)
set .orderedUnitSpeed = speed
set .tim = NewTimerEx(this)
set .targetUnit = target
call GroupAddUnit(thistype.orderedGroup, GetUnitById(this))
call TimerStart(.tim, time, false, c)
endmethod
method restart takes real time, code c returns nothing
call TimerStart(.tim, time, false, c)
endmethod
method terminate takes nothing returns nothing
call GroupRemoveUnit(thistype.orderedGroup, GetUnitById(this))
call ReleaseTimer(.tim)
endmethod
//================================================================
//======================Accessor Methods==========================
method getTargetUnit takes nothing returns unit
return .targetUnit
endmethod
method getTargetUnitX takes nothing returns real
return .targetUnitX
endmethod
method getTargetUnitY takes nothing returns real
return .targetUnitY
endmethod
method getOrderedUnitMoveSpeed takes nothing returns real
return .orderedUnitSpeed
endmethod
static method isUnitInGroup takes unit u returns boolean
return IsUnitInGroup(u, thistype.orderedGroup)
endmethod
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to region data storage, which aims to provide :
1. Store quest id and content subset to specific rect
*/
Internal API - Region :
private struct Region extends array
static method add takes region reg, Quest qId, integer subset returns nothing
- store (qId) and (subset) to region (reg)
static method getQuestId takes region reg returns Quest
- return stored quest id from (reg)
static method getSubset takes region reg returns integer
- return stored subset from (reg)
*/
private module RegionInit
private static method onInit takes nothing returns nothing
set regTable = Integer2D.create()
endmethod
endmodule
private struct Region extends array
//==========================Globals===============================
//----------------------Struct Globals------------------------
private static Integer2D regTable
//------------------------------------------------------------
//================================================================
//=====================Data Assign Methods========================
static method add takes region reg, Quest qId, integer subset returns nothing
local Integer1D i1d = regTable[GetHandleId(reg)]
set i1d[0] = qId
set i1d[1] = subset
endmethod
//================================================================
//======================Accessor Methods==========================
static method getQuestId takes region reg returns Quest
return regTable[GetHandleId(reg)][0]
endmethod
static method getSubset takes region reg returns integer
return regTable[GetHandleId(reg)][1]
endmethod
//================================================================
implement RegionInit
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to dialog creation, which aims to provide :
1. funtionality which required to display quest list
*/
Internal API - DialogEx :
private struct DialogEx extends array
static method create takes nothing returns thistype
method operator title= takes string text returns nothing
- set dialog title name to (text)
method addButton takes string text, integer hotkey, Quest qId returns nothing
- add a button to dialog with (text) of (hotkey) which links to (qId)
method display takes player p returns nothing
- display dislog to player (p)
method registerClickEvent takes boolexpr b returns nothing
- register click event which attached (b)
method clear takes nothing returns nothing
- clear dialog
method getClickedButtonQuestId takes nothing returns Quest
- return quest id of clicked dialog button
static method getPlayer takes nothing returns player
- return play who clicked the dialog button
*/
private module DialogExInit
private static method onInit takes nothing returns nothing
set buttonTable = Integer1D.create()
endmethod
endmodule
private struct DialogEx extends array
//==========================Globals===============================
//---------------------Creation Globals-----------------------
private static thistype instanceCount = 0
//------------------------------------------------------------
//---------------------Ordinary Globals-----------------------
private integer buttonCount
private integer currentPage
private static integer NEXT_PAGE = -1
private static integer PREV_PAGE = -2
//------------------------------------------------------------
//----------------------Struct Globals------------------------
private Dialog log
private static Integer1D buttonTable
//------------------------------------------------------------
//================================================================
//=======================Creation Method==========================
static method create takes nothing returns thistype
local thistype this = instanceCount + 1
set instanceCount = this
set .log = Dialog.create()
set .buttonCount = 0
set .currentPage = 1
return this
endmethod
//================================================================
//=====================Data Assign Methods========================
method operator title= takes string text returns nothing
set .log.title = text
endmethod
method addButton takes string text, integer hotkey, Quest qId returns nothing
set buttonTable[GetHandleId(.log.addButton(text, hotkey))] = qId
set .buttonCount = .buttonCount + 1
endmethod
method display takes player p returns nothing
call .log.display(p, true)
endmethod
method registerClickEvent takes boolexpr b returns nothing
call .log.registerClickEvent(b)
endmethod
method clear takes nothing returns nothing
call .log.clear()
endmethod
//================================================================
//======================Accessor Methods==========================
method getClickedButtonQuestId takes nothing returns Quest
return buttonTable[GetHandleId(Dialog.getClickedButton())]
endmethod
static method getPlayer takes nothing returns player
return Dialog.getPlayer()
endmethod
//================================================================
implement DialogExInit
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct aims to support struct EffectUnit, by creating effect path node for each
quest receiving/returning unit
*/
Internal API - EffectNode :
private struct EffectNode extends array
static method create takes string path, integer pathType returns thistype
- create a new EffectNode
- (path) is the effect path
- (pathType) is effect path type, either RECEIVE or RETURN
method destroy takes nothing returns nothing
method linkNext takes thistype node returns nothing
- link the next instance of this instance to (node)
method linkPrev takes thistype node returns nothing
- link the prev instance of this instance to (node)
method getNext takes nothing returns thistype
- return the next linked node
method getPrev takes nothing returns thistype
- return the prev linked node
method getPath takes nothing returns string
- return the effect path
method getType takes nothing returns integer
- return the effect path type
*/
private struct EffectNode extends array
//==========================Globals===============================
//----------------------Public Globals------------------------
readonly static integer RECEIVE = 0
readonly static integer RETURN = 1
//------------------------------------------------------------
//---------------------Creation Globals-----------------------
private static thistype instanceCount = 0
private static thistype array recycle
private static integer recycleAmount = 0
//------------------------------------------------------------
//---------------------Ordinary Globals-----------------------
private string path
private integer pathType
private thistype next
private thistype prev
//------------------------------------------------------------
//================================================================
//====================Creation/Destroy Methods====================
static method create takes string effectPath, integer pathType returns thistype
local thistype this
if recycleAmount > 0 then
set recycleAmount = recycleAmount - 1
set this = recycle[recycleAmount]
else
set this = instanceCount + 1
set instanceCount = this
endif
set .next = 0
set .prev = 0
set .path = effectPath
set .pathType = pathType
return this
endmethod
method destroy takes nothing returns nothing
set .next = 0
set .prev = 0
set recycle[recycleAmount] = this
set recycleAmount = recycleAmount + 1
endmethod
//================================================================
//=====================Data Assign Methods========================
method linkNext takes thistype node returns nothing
set .next = node
endmethod
method linkPrev takes thistype node returns nothing
set .prev = node
endmethod
//================================================================
//======================Accessor Methods==========================
method getNext takes nothing returns thistype
return .next
endmethod
method getPrev takes nothing returns thistype
return .prev
endmethod
method getPath takes nothing returns string
return .path
endmethod
method getType takes nothing returns integer
return .pathType
endmethod
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
private struct EffectUnit extends array
/*
/*
Struct aims to provide independent instance for each unit, having seperated singly linked
list for each quest receiving/returning unit.
Struct uses GetUnitId() as struct index
playerId : Seperate linked list of each unit to each player
*/
Internal API - EffectUnit :
private struct EffectUnit extends array
method increase takes integer playerId, string path, boolean isReceive returns EffectNode
- create a new EffectNode which stores (path)
- (isReceive) : true if receive effect path, else return effect path
- return newly created effect node
method decrease takes integer playerId, EffectNode en returns EffectNode
- destroy a (en) from this unit
- return the next available EffectNode
- return 0 if no more available EffectNode
method getReceiveHead takes integer playerId return EffectNode
- return first of the receive EffectNode assigned to this unit
method getReturnHead takes integer playerId returns EffectNode
- return first of the return EffectNode assigned to this unit
method getReceiveHeadPath takes integer playerId returns string
- return first of the receive effect path assigned to this unit
method getReturnHeadPath takes integer playerId returns string
- return first of the return effect path assigned to this unit
*/
//==========================Globals===============================
//--------------------Ordinary Globals------------------------
private static integer RECEIVE_HEAD = 1
private static integer RECEIVE_LAST = 2
private static integer RETURN_HEAD = 3
private static integer RETURN_LAST = 4
//------------------------------------------------------------
//----------------------Struct Globals------------------------
private Integer2D node
//------------------------------------------------------------
//================================================================
//=====================Data Assign Methods========================
method increase takes integer playerId, string path, boolean isReceive returns EffectNode
local Integer1D i1d
local integer i = 0
local EffectNode tail
local EffectNode en
local integer headType
local integer tailType
if .node == 0 then
set .node = Integer2D.create()
endif
set i1d = node[playerId]
if isReceive then
set headType = RECEIVE_HEAD
set tailType = RECEIVE_LAST
set en = EffectNode.create(path, EffectNode.RECEIVE)
else
set headType = RETURN_HEAD
set tailType = RETURN_LAST
set en = EffectNode.create(path, EffectNode.RETURN)
endif
set tail = i1d[tailType]
if i1d[headType] == 0 then
set i1d[headType] = en
else
call tail.linkNext(en)
endif
call en.linkPrev(tail)
set i1d[tailType] = en
return en
endmethod
method decrease takes integer playerId, EffectNode en returns EffectNode
local EffectNode nextEn
local EffectNode startEn
local EffectNode endEn
local EffectNode anotherStartEn
local Integer1D i1d = node[playerId]
local integer headType
local integer tailType
local boolean receiveType = en.getType() == EffectNode.RECEIVE
local boolean returnType = en.getType() == EffectNode.RETURN
local integer i = 0
if receiveType then
set headType = RECEIVE_HEAD
set tailType = RECEIVE_LAST
set anotherStartEn = i1d[RETURN_HEAD]
elseif returnType then
set headType = RETURN_HEAD
set tailType = RETURN_LAST
set anotherStartEn = i1d[RECEIVE_HEAD]
endif
set startEn = i1d[headType]
set endEn = i1d[tailType]
//case 1 : if en to be removed is head node
if en == startEn then
set nextEn = startEn.getNext()
set i1d[headType] = nextEn
//addition checking : if it is return type
if returnType then
//if return head node is empty
if i1d[headType] == 0 then
set nextEn = anotherStartEn
endif
//if return type of head node is not empty
elseif anotherStartEn != 0 then
set nextEn = anotherStartEn
endif
//case 2 : if en to be removed is last node
elseif en == endEn then
set nextEn = endEn.getPrev()
set i1d[tailType] = nextEn
call nextEn.linkNext(0)
if receiveType and anotherStartEn != 0 then
set nextEn = anotherStartEn
endif
//case 3 : en to be removed is in between list
else
call en.getPrev().linkNext(en.getNext())
call en.getNext().linkPrev(en.getPrev())
set nextEn = en.getNext()
endif
call en.destroy()
return nextEn
endmethod
//================================================================
//======================Accessor Methods==========================
method getReceiveHead takes integer playerId returns EffectNode
return node[playerId][RECEIVE_HEAD]
endmethod
method getReturnHead takes integer playerId returns EffectNode
return node[playerId][RETURN_HEAD]
endmethod
method getReceiveHeadPath takes integer playerId returns string
return EffectNode(node[playerId][RECEIVE_HEAD]).getPath()
endmethod
method getReturnHeadPath takes integer playerId returns string
return EffectNode(node[playerId][RETURN_HEAD]).getPath()
endmethod
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to effect creation, which aims to provide :
1. attach/remove effect to specific unit/point
Struct uses player id as struct index
*/
Internal API - Effect :
private struct Effect extends array
method applyUnit takes unit u, string effectPath, boolean isReceive returns EffectNode
- attach effect with path of (effectPath) to (u
- (isReceive) : true for receive quest event, false for return quest event
- return created EffectNode associated with this unit (acts like handle id of effect)
method removeUnit takes unit u, EffectNode oldEn returns nothing
- remove effect of (oldEn) attached to (u)
method applyRect takes rect r, string effectPath returns nothing
- attach effect with path of (effectPath) to (r)
method removeRect takes rect r returns nothing
- remove effect attached to (r)
*/
private struct Effect extends array
//==========================Globals===============================----------------------------------------------------
//----------------------Struct Globals------------------------
/*
effectTable[a].type
a : GetUnitId()
type : effect - effect handle
boolean - true if return effect path applied, else false
integer - currently EffectNode apply to unit
*/
private Array1D effectTable
/*
rectEffectTable[a].type
a : handle id of rect
type : effect - effect handle
integer - effect count
*/
private Array1D rectEffectTable
//------------------------------------------------------------
//================================================================
//=======================Internal Methods=========================
private method applyEffect takes unit u, integer unitId, string effectPath, boolean destroyPrev returns nothing
if destroyPrev then
call DestroyEffect(effectTable[unitId].effect)
endif
if GetLocalPlayer() != Player(this) then
set effectPath = ""
endif
set effectTable[unitId].effect = AddSpecialEffectTarget(effectPath, u, "overhead")
endmethod
//================================================================
//=====================Data Assign Methods========================
method applyUnit takes unit u, string effectPath, boolean isReceive returns EffectNode
local integer unitId = GetUnitId(u)
local EffectUnit eu = unitId
local EffectNode en
set en = eu.increase(this, effectPath, isReceive)
if effectTable == 0 then
set effectTable = Array1D.create()
endif
if effectTable[unitId].effect == null then
call .applyEffect(u, unitId, effectPath, false)
set effectTable[unitId].boolean = not isReceive
set effectTable[unitId].integer = en
elseif not effectTable[unitId].boolean and not isReceive then
call .applyEffect(u, unitId, eu.getReturnHeadPath(this), true)
set effectTable[unitId].boolean = true
set effectTable[unitId].integer = en
endif
return en
endmethod
method removeUnit takes unit u, EffectNode oldEn returns nothing
local integer unitId = GetUnitId(u)
local EffectUnit eu = unitId
local EffectNode newEn = eu.decrease(this, oldEn)
local EffectNode currentEn
if newEn == 0 then
call DestroyEffect(effectTable[unitId].effect)
call effectTable.remove(unitId)
else
set currentEn = effectTable[unitId].integer
//if newEn not belongs to either receive/return head,
//simply ignore the operations
if newEn == eu.getReceiveHead(this) then
if newEn.getPath() != currentEn.getPath() then
call .applyEffect(u, unitId, eu.getReceiveHeadPath(this), true)
endif
set effectTable[unitId].boolean = false
elseif newEn == eu.getReturnHead(this) then
if newEn.getPath() != currentEn.getPath() then
call .applyEffect(u, unitId, eu.getReturnHeadPath(this), true)
endif
endif
//if the node to be removed == current applying en
//then apply the newly using en as current applying en
if oldEn == currentEn then
set effectTable[unitId].integer = newEn
endif
endif
endmethod
method applyRect takes rect r, string effectPath returns nothing
local integer rectHandle = GetHandleId(r)
local integer count
if rectEffectTable == 0 then
set rectEffectTable = Array1D.create()
endif
set count = rectEffectTable[rectHandle].integer
if count > 0 then
set rectEffectTable[rectHandle].integer = count + 1
else
if GetLocalPlayer() != Player(this) then
set effectPath = ""
endif
set rectEffectTable[rectHandle].effect = AddSpecialEffect(effectPath, GetRectCenterX(r), GetRectCenterY(r))
set rectEffectTable[rectHandle].integer = 1
endif
endmethod
method removeRect takes rect r returns nothing
local integer rectHandle = GetHandleId(r)
local integer count = rectEffectTable[rectHandle].integer
if count > 1 then
set rectEffectTable[rectHandle].integer = count - 1
else
call DestroyEffect(rectEffectTable[rectHandle].effect)
call rectEffectTable.remove(rectHandle)
endif
endmethod
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to linking between quest, which aims to provide :
1. Linkage from required quest to requering quest (for checking available quest once player completed quest)
*/
Internal API - LinkedQuest :
private struct LinkedQuest extends array
static method add takes Quest requiredQuest, Quest toLink returns nothing
- link (requiredQuest) to (toLink)
static method setLinkedQuestList takes Quest requiredQuest returns nothing
- get list of linked quest
- use questList to access quest id
- last array position is set to 0 for exit looping purpose
*/
private struct LinkedQuest extends array
//==========================Globals===============================
//---------------------Ordinary Globals-----------------------
private integer count
//------------------------------------------------------------
//----------------------Public Globals------------------------
static Quest array questList
//------------------------------------------------------------
//----------------------Struct Globals------------------------
/*
lvList[a] = b
a : lvListCount
b : store quest id
*/
private Integer1D list
//------------------------------------------------------------
//================================================================
//=====================Data Assign Methods========================
static method add takes Quest requiredQuest, Quest toLink returns nothing
local thistype this = thistype(requiredQuest)
if .list == 0 then
set .list = Integer1D.create()
set .count = 0
endif
set .list[.count] = toLink
set .count = .count + 1
endmethod
//================================================================
//======================Accessor Methods==========================
static method setLinkedQuestList takes Quest requiredQuest returns nothing
local thistype this = thistype(requiredQuest)
local integer i = 0
if .list != 0 then
loop
set questList[i] = list[i]
set i = i + 1
exitwhen i == .count
endloop
endif
set questList[i] = 0
endmethod
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to level, which aims to provide :
1. Linkage for level to quest id (for checking available quest once player lv up)
*/
Internal API - Level :
private struct Level extends array
method linkQuest takes Quest qId returns nothing
- link (qId) to this level instance
- uses level as struct index
method getQuestId takes integer index returns Quest
- return quest instance at (index)
- (index) starts from 0
- uses level as struct index
method getMaxQuestCount takes nothing returns integer
- return registered quest amount associated to this level
- uses level as struct index
*/
private struct Level extends array
//==========================Globals===============================
//---------------------Ordinary Globals-----------------------
private integer lvListCount
//------------------------------------------------------------
//----------------------Struct Globals------------------------
/*
lvList[a] = b
a : lvListCount
b : store quest id
*/
private Integer1D lvList
//------------------------------------------------------------
//================================================================
//=====================Data Assign Methods========================
method linkQuest takes Quest qId returns nothing
if .lvList == 0 then
set .lvList = Integer1D.create()
set .lvListCount = 0
endif
set .lvList[.lvListCount] = qId
set .lvListCount = .lvListCount + 1
endmethod
//================================================================
//======================Accessor Methods==========================
method getQuestId takes integer index returns Quest
return lvList[index]
endmethod
method getMaxQuestCount takes nothing returns integer
return .lvListCount
endmethod
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to ability, which aims to provide :
1. Quest instance retrieve from ability (receive/return)
2. Ability id retrieve from Quest
3. Ability links to subset (for TALK/TALK_2 content)
*/
Internal API - Ability :
private struct Ability extends array
static method linkQuest takes integer aId, Quest qId returns nothing
- link ability (aId) to (qId)
static method talkLink takes integer aId, Quest qId, integer subset returns nothing
- link talk ability (aId) to (qId) and (subset)
static method getQuestId takes integer aId returns Quest
- return linked quest id from (aId)
static method getTalkQuestId takes integer aId returns Quest
- return talk linked quest id from (aId)
static method getTalkSubset takes integer aId returns integer
- return linked subset from (aId)
- for ability related TALK/TALK_2 content type usage
static method get takes Quest qId returns integer
- return linked ability id from (qId)
*/
private module AbilityInit
private static method onInit takes nothing returns nothing
set linkList = Integer1D.create()
set talkList = Integer2D.create()
endmethod
endmodule
private struct Ability extends array
//==========================Globals===============================
//----------------------Struct Globals------------------------
/*
list[a] = b
a : ability id
b : store quest id
AND
a : quest id
b : store ability id
*/
private static Integer1D linkList
/*
talkList[a][b] = c
a : ability id
b : 0
c : store quest id
b : 1
c : store subset
*/
private static Integer2D talkList
//------------------------------------------------------------
//================================================================
//=====================Data Assign Methods========================
static method linkQuest takes integer aId, Quest qId returns nothing
set linkList[aId] = qId
set linkList[qId] = aId
endmethod
static method talkLink takes integer aId, Quest qId, integer subset returns nothing
set talkList[aId][0] = qId
set talkList[aId][1] = subset
endmethod
//================================================================
//======================Accessor Methods==========================
static method getQuestId takes integer aId returns Quest
return linkList[aId]
endmethod
static method getTalkQuestId takes integer aId returns Quest
return talkList[aId][0]
endmethod
static method getTalkSubset takes integer aId returns integer
return talkList[aId][1]
endmethod
static method get takes Quest qId returns integer
return linkList[qId]
endmethod
//================================================================
implement AbilityInit
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to unit, which aims to provide :
1. Quest instance retrieve from unit (receive/return quest)
2. Quest instance retrieve from monster
3. Quest unit (item) data assign, included quest id, subset and player
*/
Internal API - Unit :
private struct Unit extends array
method addReceivableQuest takes Quest qId returns nothing
- register this unit to have receivable (qId)
- uses GetUnitId(u) as struct index
method addReturnableQuest takes Quest qId returns nothing
- register this unit to have returnable (qId)
- uses GetUnitId(u) as struct index
static method monsterLinkQuest takes integer uId, Quest qId returns nothing
- link unit type (unitType) with (qId)
method assignData takes Quest qId, integer subset, player user returns nothing
- assign (qId), (subset) to this unit
- ownership of the unit belongs to (user)
- uses GetUnitId(u) as struct index
method getReceivableQuest takes integer index returns Quest
- return receivable quest at (index) for this unit
- (index) starts from 0
method getReturnableQuest takes integer index returns Quest
- return returnable quest at (index) for this unit
- (index) starts from 0
/*function below is currently not in use*/
static method setMonsterQuestList takes integer unitType returns nothing
- get list of linked monster quest
- use linkQuestList to access quest id
- last array position is set to 0 for exit looping purpose
method getMaxReceivableQuest takes nothing returns integer
- return maximum receivable quest for this unit
method getMaxReturnableQuest takes nothing returns integer
- return maximum returnable quest for this unit
method getQuestId takes nothing returns Quest
- return stored quest id for this unit
- use to retrieve stored data once assignData() is called
method getSubset takes nothing returns integer
- return stored subset for this unit
- use to retrieve stored data once assignData() is called
method getOwner takes nothing returns player
- return ownership of this unit
- use to retrieve stored data once assignData() is called
method clear takes nothing returns nothing
- clear data assigned to this unit
- for clearing assignData() data
*/
private module UnitInit
private static method onInit takes nothing returns nothing
set monsterQuestList = Integer2D.create()
endmethod
endmodule
private struct Unit extends array
//==========================Globals===============================
//---------------------Ordinary Globals-----------------------
private integer receiveListCount
private integer returnListCount
//------------------------------------------------------------
//----------------------Public Globals------------------------
static Quest array linkQuestList
//------------------------------------------------------------
//----------------------Struct Globals------------------------
/*
receiveList/returnList[a] = b
a : receiveListCount/returnListCount (starts from 0)
b : store quest id
*/
private Integer1D receiveList
private Integer1D returnList
/*
unitData[a].type = b
a : 1
b : quest id (type integer)
a : 2
b : subset (type integer)
a : 3
b : user (type player)
*/
private Array1D unitData
/*
monsterQuestList[a][b] = c
a : unit type id
b : linkCount (starts from 0)
c : store quest id
*/
private static Integer2D monsterQuestList
//------------------------------------------------------------
//================================================================
//=====================Data Assign Methods========================
method addReceivableQuest takes Quest qId returns nothing
if .receiveList == 0 then
set .receiveList = Integer1D.create()
set .receiveListCount = 0
endif
set .receiveList[.receiveListCount] = qId
set .receiveListCount = .receiveListCount + 1
endmethod
method addReturnableQuest takes Quest qId returns nothing
if .returnList == 0 then
set .returnList = Integer1D.create()
set .returnListCount = 0
endif
set .returnList[.returnListCount] = qId
set .returnListCount = .returnListCount + 1
endmethod
static method monsterLinkQuest takes integer unitType, Quest qId returns nothing
local Integer1D i1d
local integer i
if monsterQuestList == 0 then
set monsterQuestList = Integer2D.create()
endif
set i1d = monsterQuestList[unitType]
set i = i1d[0] + 1
set i1d[0] = i
set i1d[i] = qId
endmethod
method assignData takes Quest qId, integer subset, player user returns nothing
if .unitData == 0 then
set .unitData = Array1D.create()
endif
set .unitData[1].integer = qId
set .unitData[2].integer = subset
set .unitData[3].player = user
endmethod
method clear takes nothing returns nothing
call .unitData.flush()
endmethod
//================================================================
//======================Accessor Methods==========================
method getReceivableQuest takes integer index returns Quest
return .receiveList[index]
endmethod
method getReturnableQuest takes integer index returns Quest
return .returnList[index]
endmethod
/*static method setMonsterQuestList takes integer unitType returns nothing
local Integer1D i1d = monsterQuestList[unitType]
local integer i = 0
local integer max = i1d[0]
loop
set linkQuestList[i] = i1d[i+1]
set i = i + 1
exitwhen i == max
endloop
set linkQuestList[i] = 0
endmethod*/
method getMaxReceivableQuest takes nothing returns integer
return .receiveListCount
endmethod
method getMaxReturnableQuest takes nothing returns integer
return .returnListCount
endmethod
method getQuestId takes nothing returns Quest
return .unitData[1].integer
endmethod
method getSubset takes nothing returns integer
return .unitData[2].integer
endmethod
method getOwner takes nothing returns player
return .unitData[3].player
endmethod
//================================================================
implement UnitInit
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct aims to support struct PlayerQuestList, by creating node for each
quest for each player
*/
Internal API - QuestListNode :
private struct QuestListNode extends array
method create takes nothing returns thistype
method destroy takes nothing returns nothing
method link takes thistype node returns nothing
- link this instance with (node)
method operator questId= takes Quest qId returns nothing
- assign (qId) to this instance
method getNext takes nothing returns thistype
- obtain next node linked to this instance
method getQuestId takes nothing returns Quest
- obtain assigned quest id from this instance
*/
private struct QuestListNode extends array
//==========================Globals===============================
//---------------------Creation Globals-----------------------
private static thistype instanceCount = 0
private static thistype array recycle
private static integer recycleAmount = 0
//------------------------------------------------------------
//---------------------Ordinary Globals-----------------------
private Quest q
private thistype next
//------------------------------------------------------------
//================================================================
//====================Creation/Destroy Methods====================
static method create takes nothing returns thistype
local thistype this
if recycleAmount > 0 then
set recycleAmount = recycleAmount - 1
set this = recycle[recycleAmount]
else
set this = instanceCount + 1
set instanceCount = this
endif
set .next = 0
return this
endmethod
method destroy takes nothing returns nothing
set .next = 0
set recycle[recycleAmount] = this
set recycleAmount = recycleAmount + 1
endmethod
//================================================================
//=====================Data Assign Methods========================
method link takes thistype node returns nothing
set .next = node
endmethod
method operator questId= takes Quest qId returns nothing
set .q = qId
endmethod
//================================================================
//======================Accessor Methods==========================
method getNext takes nothing returns thistype
return .next
endmethod
method getQuestId takes nothing returns Quest
return .q
endmethod
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to player quest list, which aims to provide :
1. Quest instance list received by each player
Struct index uses (player id + 1) as struct index of 0 is invalid
*/
Internal API - PlayerQuestList :
private struct PlayerQuestList extends array
method add takes Quest qId returns nothing
- register received quest (qId) for player p
- uses GetPlayerId(p) as struct index
method remove takes Quest qId returns nothing
- unregister received quest (qId) from player p
- uses GetPlayerId(p) as struct index
method received takes Quest qId returns boolean
- check if player p received quest (qId)
method setQuestList takes nothing returns nothing
- get list of quest from this player
- use questList to access quest id
- last array position is set to 0 for exit looping purpose
method fulled takes nothing returns boolean
- return true if player's quest list is fulled
method isEmpty takes nothing returns boolean
- return true if player's quest list is empty
*/
//! runtextmacro optional QEQuestCategory__PlayerQuestListInit()
private struct PlayerQuestList extends array
//==========================Globals===============================
//---------------------Ordinary Globals-----------------------
private integer questCount
//------------------------------------------------------------
//----------------------Public Globals------------------------
static Quest array questList
//------------------------------------------------------------
//----------------------Struct Globals------------------------
private QuestListNode head
private QuestListNode last
//------------------------------------------------------------
//--------------------External Globals------------------------
//! runtextmacro optional QEQuestCategory__PlayerQuestList__Globals()
//------------------------------------------------------------
//================================================================
//=================Data Manipulation Methods======================
method add takes Quest qId returns nothing
local QuestListNode node = QuestListNode.create()
if last == 0 then
set .head = node
set .questCount = 0
else
call .last.link(node)
endif
set node.questId = qId
set .last = node
set .questCount = .questCount + 1
endmethod
method remove takes Quest qId returns nothing
local QuestListNode node = .head
local QuestListNode temp
//if first node is the quest qId we want to remove
if node.getQuestId() == qId then
if .head == .last then
set .head = 0
set .last = 0
else
set .head = node.getNext()
endif
call node.destroy()
else
loop
set temp = node.getNext()
exitwhen temp == 0
//if next quest id is the quest id we want to remove
if temp.getQuestId() == qId then
if .last == temp then
call .last.destroy()
call node.link(0)
set .last = node
else
call node.link(temp.getNext())
call temp.destroy()
endif
exitwhen true
endif
set node = temp
endloop
endif
set .questCount = .questCount - 1
endmethod
//================================================================
//======================Accessor Methods==========================
method received takes Quest qId returns boolean
local QuestListNode node = .head
loop
if node.getQuestId() == qId then
return true
endif
set node = node.getNext()
exitwhen node == 0
endloop
return false
endmethod
method setQuestList takes nothing returns nothing
local QuestListNode node = .head
local integer i = 0
loop
set questList[i] = node.getQuestId()
set node = node.getNext()
set i = i + 1
exitwhen node == 0
endloop
set questList[i] = 0
endmethod
method fulled takes nothing returns boolean
return .questCount > MAX_QUEST_PER_PLAYER
endmethod
method isEmpty takes nothing returns boolean
return .questCount == 0
endmethod
//================================================================
//======================External Methods==========================
//! runtextmacro optional QEQuestCategory__PlayerQuestList__Member()
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct related to player quest data, which aims to provide :
1. Quest data required to process and update
2. Quest status for each quests
Struct index uses (player id + 1) : struct index of 0 is invalid
*/
Internal API - PlayerQuestData :
private struct PlayerQuestData extends array
method init takes Quest qId, player user returns nothing
- init neessasary data for (qId)
- Example PICK_UP need to get initialized for creating items
method setQuestStatus takes Quest qId, integer whichStatus returns nothing
- set (qId) status to (whichStatus)
- (whichStatus) uses globals defined in this struct, included :
1. UNTAKEN
2. IN_PROGRSS
3. FULLFILL
4. COMPLETED
method revertQuestStatus takes Quest qId returns nothing
- revert the quest status from FULLFILL to IN_PROGRESS
method showFullfillEffect takes Quest qId, Effect e, boolean showCompleteMsg returns nothing
- apply effect to return unit of (qId) to player id of (e)
- play sound (if enabled QEQuestSound)
- if (showCompleteMsg) is true, display message that quest of (qId) is complete
method isSubsetCompleted takes Quest qId, integer subset returns boolean
- return true if (subset) of (qId) is completed
method getQuestStatus takes Quest qId returns integer
- return the status of (qId)
method beenCompleted takes Quest qId returns boolean
- return true if quest been completed once by player
method isRequiredQuestCompleted takes Quest qId returns boolean
- return true if all prior required quests are in
completed status (or been completed status)
method isQuestReceivable takes Quest qId returns boolean
- return true if quest is receivable
method getFullDetails takes Quest qId, integer structIndex returns string
- return full details of (qId) for this player
- (structIndex) for accessing PlayerQuestData members
- details included :
1. quest title
2. quest description
3. quest contents
4. player quest progress
method createQuestItem takes real x, real y, Quest qId, integer subset, player user returns unit
- create quest item at (x,y) which only visible and pickable by player (user)
- quest item will stores (qId),(subset) and owner (which is user) associated to it
- return created quest item
method removeQuestItem takes unit u, Quest qId returns nothing
- remove and clear all quest item related to (qId) for unit (u)
method applyUnitEffect takes Quest qId, integer subset, unit toApply, string effectPath, boolean isReceive, Effect e returns nothing
- apply effect to (toApply) of path (effectPath) to player (e)
- (qId) is the effect stored in the quest
- (subset) is the effect stored in the (qId) of subset, set to -1 if
it is receive/return unit apply
- (isReceive) : true for receiving effect, else false
method removeUnitEffect takes Quest qId, integer subset, unit toRemove, Effect e returns nothing
- remove effect from (toRemove) of player (e)
- (qId) is the effect stored in the quest
- (subset) is the effect stored in the (qId) of subset, set to -1 if
it is receive/return unit remove
method clear takes Quest qId returns nothing
- clear all stored data of (qId) associated to the player
/*KILL*/
method updateKillAmount takes Quest qId, integer subset returns nothing
- update killing amount of this (subset)
- (subset) starts from 1
- usable for content type : KILL
method getKillAmount takes Quest qId, integer subset returns integer
- return killed amount
- (subset) starts from 1
- usable for content type : KILL
/*TALK/TALK_2*/
method updateTalk takes Quest qId, integer subset returns nothing
- set unit as talked for this (subset)
- (subset) starts from 1
- usable for content type : TALK, TALK_2
method isUnitTalked takes Quest qId, integer subset returns boolean
- return true if unit talked
- (subset) starts from 1
- usable for content type : TALK, TALK_2
/*PICK_UP/KILL_OBTAIN_ITEM*/
method updatePickAmount takes Quest qId, integer subset returns nothing
- update pick amount of this (subset)
- (subset) starts from 1
- usable for content type : PICK_UP, KILL_OBTAIN_ITEM
method decreasePickAmount takes Quest qId, integer subset returns nothing
- decrease pick amount of this (subset) by 1
- (subset) starts from 1
- usable for content type : PICK_UP, KILL_OBTAIN_ITEM
method getPickAmount takes Quest qId, integer subset returns integer
- return picked amount
- (subset) starts from 1
- usable for content type : PICK_UP, KILL_OBTAIN_ITEM
/*KILL_OBTAIN*/
method updateObtainAmount takes Quest qId, integer subset returns nothing
- update obtained amount of this (subset)
- (subset) starts from 1
- usable for content type : KILL_OBTAIN
method getObtainAmount takes Quest qId, integer subset returns integer
- return obtained amount
- (subset) starts from 1
- usable for content type : KILL_OBTAIN
/*KILL_OBTAIN_ITEM*/
method updateDropAmount takes Quest qId, integer subset returns nothing
- update drop amount of this (subset)
- (subset) starts from 1
- usable for content type : KILL_OBTAIN_ITEM
method getDropAmount takes Quest qId, integer subset returns integer
- return drop amount
- (subset) starts from 1
- usable for content type : KILL_OBTAIN_ITEM
/*GOTO*/
method updateGoto takes Quest qId, integer subset returns nothing
- set target rect as went for this (subset)
- (subset) starts from 1
- usable for content type : GOTO
method isPlaceWent takes Quest qId, integer subset returns boolean
- return true if place went
- (subset) starts from 1
- usable for content type : GOTO
/*Addition Type : GROUP_KILL (If enabled QEGroupKill)*/
method updateGroupKill takes Quest qId, integer subset returns nothing
- update group kill amount by 1
- (subset) starts from 1
- usable for content type : GROUP_KILL
method getGroupKillAmount takes Quest qId, integer subset returns integer
- return killed amount
- (subset) starts from 1
- usable for content type : GROUP_KILL
/*Addition Type : CUSTOM_CONTENT (If enabled QECustomContent*/
method updateCustomContent takes Quest qId, integer subset returns nothing
- update group kill amount by 1
- (subset) starts from 1
- usable for content type : GROUP_KILL
*/
private module PlayerQuestDataInit
private static method onInit takes nothing returns nothing
local thistype this = 1
loop
set .data = Array3D.create()
exitwhen integer(this) == bj_MAX_PLAYERS
set this = this + 1
endloop
endmethod
endmodule
private struct PlayerQuestData extends array
//==========================Globals===============================
//----------------------Public Globals------------------------
readonly static integer UNTAKEN = 0
readonly static integer IN_PROGRESS = 1
readonly static integer FULLFILL = 2
readonly static integer COMPLETED = 3
//------------------------------------------------------------
//----------------------Struct Globals------------------------
/*
storing quest data
most data will be cleared once quest is completed by player
data[a][b][c] = quest progress data
a : 0
b : quest id
c : 0 - quest progress (type integer) /*will be cleared to UNTAKEN status*/
1 - completed subset (type integer) /*will be cleared to 0*/
2 - disabled (type boolean) (used by QEDynamicEnable) /*will not be cleared*/
3 - been completed (type boolean) /*will not be cleared*/
4 - stored EffectNode (type integer) /*will be cleared*/
/*all data below will be cleared*/
a : quest id
b : subset (starts from 0)
c : depends on content type, which listed below :
/*ALL types*/
c : 0 - true if subset completed(type boolean)
c : 2 - stored EffectNode (type integer)
/*KILL*/
c : 0 - kill amount (type integer)
/*TALK/TALK_2*/
c : 1 - true if unit talked (type boolean)
/*PICK_UP*/
c : 0 - pick amount (type integer)
/*KILL_OBTAIN*/
c : 0 - obtain amount (type integer)
/*KILL_OBTAIN_ITEM*/
c : 0 - pick amount (type integer)
1 - dropped amount (type integer)
/*GOTO*/
c : 1 - true if went (type boolean)
/*GROUP_KILL (if enabled QEGroupKill)*/
c : 0 - kill amount (type integer)
/*CUSTOM_CONTENT (if enabled QECustomContent)*/
c : 0 - update aomunt (type integer)
*/
private Array3D data
//------------------------------------------------------------
//================================================================
//=======================Priority Methods=========================
//Priority methods are the methods which required by either internal
//methods or external callers. Any methods met such conditions will
//be placed here.
static method createQuestItem takes real x, real y, Quest qId, integer subset, player user returns unit
local integer transparency = 255
local real scale = QUEST_ITEM_SCALE
set bj_lastCreatedUnit = CreateUnit(NEUTRAL, QUEST_ITEM_ID, x, y, QUEST_ITEM_ANGLE)
if GetLocalPlayer() != user then
set transparency = 0
set scale = 0.001
endif
call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, transparency) //prevent other player to see it
call SetUnitScale(bj_lastCreatedUnit, scale, scale, scale) //prevent other player left click it
call Unit(GetUnitId(bj_lastCreatedUnit)).assignData(qId, subset, user)
return bj_lastCreatedUnit
endmethod
method applyUnitEffect takes Quest qId, integer subset, unit toApply, string effectPath, boolean isReceive, Effect e returns nothing
if subset == -1 then
set data[0][qId][4].integer = e.applyUnit(toApply, effectPath, isReceive)
else
set data[qId][subset][2].integer = e.applyUnit(toApply, effectPath, isReceive)
endif
endmethod
//================================================================
//=======================Internal Methods=========================
private static method initPickUpItems takes Quest qId, integer subset, player user returns nothing
local integer i = 0
local integer maxRect = QuestContent.getPickUpRectSet().getRegisteredAmount() - 1
local integer max = QuestContent.getPickUpRequiredAmount()
local integer rand
local rect rec
call QuestContent.getPickUpRectSet().setRectList()
loop
exitwhen i == max
set rand = GetRandomInt(0, maxRect)
set rec = RectSet.rectList[rand]
call thistype.createQuestItem(GetRandomReal(GetRectMinX(rec), GetRectMaxX(rec)), GetRandomReal(GetRectMinY(rec), GetRectMaxY(rec)), qId, subset, user)
if rand != maxRect then
set RectSet.rectList[rand] = RectSet.rectList[maxRect]
endif
set maxRect = maxRect - 1
set i = i + 1
endloop
set rec = null
endmethod
private method getCompletedSubset takes Array1D temp returns integer
return temp[1].integer
endmethod
private method setSubsetIncomplete takes Array1D temp, Array1D a1d returns nothing
local integer completedSubset = temp[1].integer - 1
set temp[1].integer = completedSubset //temp : data[0][qId]
set a1d[0].boolean = false //a1d : data[qId][subset]
endmethod
private method setSubsetComplete takes Array1D temp, Array1D a1d returns nothing
local integer completedSubset = temp[1].integer + 1
set temp[1].integer = completedSubset //temp : data[0][qId]
set a1d[0].boolean = true //a1d : data[qId][subset]
endmethod
//boolean b : true for quest progressing
// false for quest reverting
private method update takes Array1D a1d, Quest qId, integer subset, boolean b returns nothing
local Array1D temp = data[0][qId]
local QuestContent content = qId.getContentIndex()
local integer contentType = content.initContent(subset)
/*---------------------------------------------------------------------------*/
if contentType == QuestContent.KILL then
/**///Revert is impossible to involve kill content type
/**/if .getKillAmount(qId, subset) == QuestContent.getKillRequiredAmount() then
/**/ call .setSubsetComplete(temp, a1d)
/**/endif
/*---------------------------------------------------------------------------*/
elseif contentType == QuestContent.TALK or contentType == QuestContent.TALK_2 or /*
*/ contentType == QuestContent.GOTO then
/**///Revert is impossible to involve talk/talk2/goto content type
/**/ call .setSubsetComplete(temp, a1d)
/*---------------------------------------------------------------------------*/
elseif contentType == QuestContent.PICK_UP then
/**/if b then
/**/ if .getPickAmount(qId, subset) == QuestContent.getPickUpRequiredAmount() then
/**/ call .setSubsetComplete(temp, a1d)
/**/ endif
/**///Checking if subset if completed
/**/elseif data[qId][subset][0].boolean then
/**/ call .setSubsetIncomplete(temp, a1d)
/**/endif
/*---------------------------------------------------------------------------*/
elseif contentType == QuestContent.KILL_OBTAIN then
/**///Revert is impossible to involve kill obtain content type
/**/if .getObtainAmount(qId, subset) == QuestContent.getKillObtainRequiredAmount() then
/**/ call .setSubsetComplete(temp, a1d)
/**/endif
/*---------------------------------------------------------------------------*/
elseif contentType == QuestContent.KILL_OBTAIN_ITEM then
/**/if b then
/**/ if .getPickAmount(qId, subset) == QuestContent.getKillObtainItemRequiredAmount() then
/**/ call .setSubsetComplete(temp, a1d)
/**/ endif
/**///Checking if subset if completed
/**/elseif data[qId][subset][0].boolean then
/**/ call .setSubsetIncomplete(temp, a1d)
/**/endif
/*---------------------------------------------------------------------------*/
//! runtextmacro optional QEGroupKill__PlayerQuestData__Update()
//! runtextmacro optional QECustomContent__PlayerQuestData__Update()
endif
if b then
if .getCompletedSubset(temp) == content.getRegisteredAmount() then
set temp[0].integer = thistype.FULLFILL
endif
else
if .getQuestStatus(qId) == thistype.FULLFILL then
set temp[0].integer = thistype.IN_PROGRESS
endif
endif
endmethod
//increase integer by 1
private method updateInteger takes Quest qId, integer subset, integer index returns nothing
local Array1D a1d = data[qId][subset]
local integer i = a1d[index].integer + 1
set a1d[index].integer = i
call .update(a1d, qId, subset, true)
endmethod
//set boolean from false to true
private method updateBoolean takes Quest qId, integer subset, integer index returns nothing
local Array1D a1d = data[qId][subset]
set a1d[index].boolean = true
call .update(a1d, qId, subset, true)
endmethod
//decrease integer by 1
private method decreaseInteger takes Quest qId, integer subset, integer index returns nothing
local Array1D a1d = data[qId][subset]
local integer i = a1d[index].integer - 1
set a1d[index].integer = i
call .update(a1d, qId, subset, false)
endmethod
//================================================================
//=================Data Manipulation Methods======================
//--------------------KILL Content Type-----------------------
method updateKillAmount takes Quest qId, integer subset returns nothing
call .updateInteger(qId, subset, 0)
endmethod
//------------------------------------------------------------
//----------------TALK/TALK_2 Content Type--------------------
method updateTalk takes Quest qId, integer subset returns nothing
call .updateBoolean(qId, subset, 1)
endmethod
//------------------------------------------------------------
//------------------PICK_UP Content Type----------------------
method updatePickAmount takes Quest qId, integer subset returns nothing
call .updateInteger(qId, subset, 0)
endmethod
method decreasePickAmount takes Quest qId, integer subset returns nothing
call .decreaseInteger(qId, subset, 0)
endmethod
//------------------------------------------------------------
//----------------KILL_OBTAIN Content Type--------------------
method updateObtainAmount takes Quest qId, integer subset returns nothing
call .updateInteger(qId, subset, 0)
endmethod
//------------------------------------------------------------
//--------------KILL_OBTAIN_ITEM Content Type-----------------
method updateDropAmount takes Quest qId, integer subset returns nothing
call .updateInteger(qId, subset, 1)
endmethod
//------------------------------------------------------------
//--------------------GOTO Content Type-----------------------
method updateGoto takes Quest qId, integer subset returns nothing
call .updateBoolean(qId, subset, 1)
endmethod
//------------------------------------------------------------
//--------------------------Misc------------------------------
method setQuestStatus takes Quest qId, integer whichStatus returns nothing
local Array1D a1d = data[0][qId]
set a1d[0].integer = whichStatus
if whichStatus == thistype.COMPLETED then
set a1d[3].boolean = true
endif
endmethod
method removeUnitEffect takes Quest qId, integer subset, unit toRemove, Effect e returns nothing
local Array1D a1d
if subset == -1 then
set a1d = data[0][qId]
call e.removeUnit(toRemove, a1d[4].integer)
call a1d.remove(4)
else
set a1d = data[qId][subset]
call e.removeUnit(toRemove, a1d[2].integer)
call a1d.remove(2)
endif
endmethod
method revertQuestStatus takes Quest qId returns nothing
call .setQuestStatus(qId, thistype.IN_PROGRESS)
call .removeUnitEffect(qId, -1, qId.getEventIndex().getReturnUnit(), Effect(this-1))
endmethod
method showFullfillEffect takes Quest qId, Effect e, boolean showCompleteMsg returns nothing
local player user = Player(e)
//apply effect to return unit
static if LIBRARY_QEDynamicEffectPath then
if qId.getReturnPath() != "" then
call .applyUnitEffect(qId, -1, qId.getEventIndex().getReturnUnit(), qId.getReturnPath(), false, e)
else
call .applyUnitEffect(qId, -1, qId.getEventIndex().getReturnUnit(), EFFECT_PATH_RETURN, false, e)
endif
else
call .applyUnitEffect(qId, -1, qId.getEventIndex().getReturnUnit(), EFFECT_PATH_RETURN, false, e)
endif
//if sound enabled, play sound
//! runtextmacro optional QEQuestSound__UpdateSound()
if showCompleteMsg then
call DisplayTextToPlayer(user, 0, 0, QUEST_COLOR_GENERAL + "Quest - |r" + qId.getTitle() + QUEST_COLOR_GENERAL + " has been completed")
endif
set user = null
endmethod
method removeQuestItem takes unit u, Quest qId returns nothing
local item it
local integer i = 0
loop
set it = UnitItemInSlot(u, i)
call Item.setData(it)
if Item.getQuestId() == qId then
call Item.clearData(it)
call RemoveItem(it)
endif
set i = i + 1
exitwhen i == 5
endloop
set it = null
endmethod
method clear takes Quest qId returns nothing
local Array1D a1d = data[0][qId]
//clear quest status(index 0) and quest completed subset(index 1)
set a1d[0].integer = thistype.UNTAKEN
set a1d[1].integer = 0
//clear all subset contents
call data[qId].flush()
endmethod
//------------------------------------------------------------
//================================================================
//===================Content Init Method==========================
method init takes Quest qId, player user returns nothing
local QuestContent content = qId.getContentIndex()
local integer subset = 0
local Effect e = GetPlayerId(user)
local integer max = content.getRegisteredAmount()
local integer contentType
loop
set contentType = content.getContentType(subset)
if contentType == QuestContent.TALK or contentType == QuestContent.TALK_2 then
call content.initContent(subset)
call .applyUnitEffect(qId, subset, QuestContent.getTalkUnit(), EFFECT_PATH_TALK, false, e)
elseif contentType == QuestContent.GOTO then
call content.initContent(subset)
call e.applyRect(QuestContent.getGotoRect(), EFFECT_PATH_GOTO)
elseif contentType == QuestContent.PICK_UP then
call content.initContent(subset)
call thistype.initPickUpItems(qId, subset, user)
endif
set subset = subset + 1
exitwhen subset == max
endloop
endmethod
//================================================================
//======================Accessor Methods==========================
//--------------------KILL Content Type-----------------------
method getKillAmount takes Quest qId, integer subset returns integer
return data[qId][subset][0].integer
endmethod
//------------------------------------------------------------
//----------------TALK/TALK_2 Content Type--------------------
method isUnitTalked takes Quest qId, integer subset returns boolean
return data[qId][subset][0].boolean
endmethod
//------------------------------------------------------------
//------------------PICK_UP Content Type----------------------
method getPickAmount takes Quest qId, integer subset returns integer
return .getKillAmount(qId, subset)
endmethod
//------------------------------------------------------------
//----------------KILL_OBTAIN Content Type--------------------
method getObtainAmount takes Quest qId, integer subset returns integer
return .getKillAmount(qId, subset)
endmethod
//------------------------------------------------------------
//--------------KILL_OBTAIN_ITEM Content Type-----------------
method getDropAmount takes Quest qId, integer subset returns integer
return data[qId][subset][1].integer
endmethod
//------------------------------------------------------------
//--------------------GOTO Content Type-----------------------
method isPlaceWent takes Quest qId, integer subset returns boolean
return .isUnitTalked(qId, subset)
endmethod
//------------------------------------------------------------
//--------------------------Misc------------------------------
method isSubsetCompleted takes Quest qId, integer subset returns boolean
return data[qId][subset][0].boolean
endmethod
method getQuestStatus takes Quest qId returns integer
return data[0][qId][0].integer
endmethod
method beenCompleted takes Quest qId returns boolean
return data[0][qId][3].boolean
endmethod
method isRequiredQuestCompleted takes Quest qId returns boolean
local integer i = 0
local Quest questId
call qId.getConditionIndex().setRequiredQuestList()
loop
set questId = QuestCondition.questList[i]
exitwhen questId == 0
if not .beenCompleted(questId) then
return false
endif
set i = i + 1
endloop
return true
endmethod
method isQuestReceivable takes Quest qId returns boolean
local boolean b = true
static if LIBRARY_QECustomCondition then
local QuestCondition cond
local integer index = 0
endif
//First check if quest is enabled for the player (optional)
//! runtextmacro optional QEDynamicEnable__PlayerQuestData__Check()
//Quest is enabled for player, assign the conditions defined in QuestCondition
if b then
set b = .getQuestStatus(qId) == thistype.UNTAKEN and /*
*/ GetHeroLevel(HERO[this-1]) >= qId.getConditionIndex().getRequiredLevel() and /*
*/ .isRequiredQuestCompleted(qId)
endif
//Second check if quest been completed for the player (if not enabled QERepeatableQuest)
//If enabled, then it will check whether it is a repeatable quest
if b then
static if not LIBRARY_QERepeatableQuest then
//quest untaken, check if been completed (set to false if been completed)
if .beenCompleted(qId) then
set b = false
endif
endif
//! runtextmacro optional QERepeatableQuest__PlayerQuestData__Check()
endif
//Last check if there are custom condition defined by user (optional) and then
//return the result
static if not LIBRARY_QECustomCondition then
return b
endif
//! runtextmacro optional QECustomCondition__PlayerQuestData__Check()
endmethod
method getFullDetails takes Quest qId, integer structIndex returns string
local QuestContent content = qId.getContentIndex()
local PlayerQuestData userQuestData = structIndex
local integer subset = 0
local string s //storing overall quest details string
local string s2 //for storing quest complete/incomeplete color
local integer contentType
local integer i
local integer i2
set s = QUEST_COLOR_GENERAL + "Quest Name : |r" + qId.getTitle() + "\n"
set s = s + QUEST_COLOR_DESCRIPTION + qId.getDescription() + "\n\n"
set s = s + QUEST_COLOR_GENERAL + "Quest Requirement :|r"
loop
exitwhen subset == content.getRegisteredAmount()
set contentType = content.initContent(subset)
/*---------------------------------------------------------------------------*/
if contentType == QuestContent.KILL then
/**/set i = .getKillAmount(qId, subset)
/**/set i2 = QuestContent.getKillRequiredAmount()
/**/
/**/if i == i2 then
/**/ set s2 = QUEST_COLOR_COMPLETE
/**/else
/**/ set s2 = QUEST_COLOR_INCOMPLETE
/**/endif
/**/
/**/set s = s + s2 + "\n- Kill " + I2S(i2) + " |r" + GetObjectName(QuestContent.getKillRequiredUnitType()) + s2 + " (" + I2S(i) + "/" + I2S(i2) + ")"
/*---------------------------------------------------------------------------*/
elseif contentType == QuestContent.TALK or contentType == QuestContent.TALK_2 then
/**/if .isUnitTalked(qId, subset) then
/**/ set s2 = QUEST_COLOR_COMPLETE
/**/else
/**/ set s2 = QUEST_COLOR_INCOMPLETE
/**/endif
/**/
/**/set s = s + s2 + "\n- " + QuestContent.getTalkDescription()
/*---------------------------------------------------------------------------*/
elseif contentType == QuestContent.PICK_UP then
/**/set i = .getPickAmount(qId, subset)
/**/set i2 = QuestContent.getPickUpRequiredAmount()
/**/
/**/if i == i2 then
/**/ set s2 = QUEST_COLOR_COMPLETE
/**/else
/**/ set s2 = QUEST_COLOR_INCOMPLETE
/**/endif
/**/
/**/set s = s + s2 + "\n- " + QuestContent.getPickUpDescription() + s2 + " (" + I2S(i) + "/" + I2S(i2) + ")"
/*---------------------------------------------------------------------------*/
elseif contentType == QuestContent.KILL_OBTAIN then
/**/set i = .getObtainAmount(qId, subset)
/**/set i2 = QuestContent.getKillObtainRequiredAmount()
/**/
/**/if i == i2 then
/**/ set s2 = QUEST_COLOR_COMPLETE
/**/else
/**/ set s2 = QUEST_COLOR_INCOMPLETE
/**/endif
/**/
/**/set s = s + s2 + "\n- Kill |r" + GetObjectName(QuestContent.getKillObtainRequiredUnitType()) + s2 + " and obtain " + I2S(i2) + " |r" + QuestContent.getKillObtainItemName() + s2 + " (" + I2S(i) + "/" + I2S(i2) + ")"
/*---------------------------------------------------------------------------*/
elseif contentType == QuestContent.KILL_OBTAIN_ITEM then
/**/set i = .getPickAmount(qId, subset)
/**/set i2 = QuestContent.getKillObtainItemRequiredAmount()
/**/
/**/if i == i2 then
/**/ set s2 = QUEST_COLOR_COMPLETE
/**/else
/**/ set s2 = QUEST_COLOR_INCOMPLETE
/**/endif
/**/
/**/set s = s + s2 + "\n- Kill " + GetObjectName(QuestContent.getKillObtainItemRequiredUnitType()) + s2 + " and pick up " + I2S(i2) + " |r" + GetObjectName(QuestContent.getKillObtainItemItemTypeId()) + s2 + " (" + I2S(i) + "/" + I2S(i2) + ")"
/*---------------------------------------------------------------------------*/
elseif contentType == QuestContent.GOTO then
/**/if .isPlaceWent(qId, subset) then
/**/ set s2 = QUEST_COLOR_COMPLETE
/**/else
/**/ set s2 = QUEST_COLOR_INCOMPLETE
/**/endif
/**/
/**/set s = s + s2 + "\n- " + QuestContent.getGotoDescription()
/*---------------------------------------------------------------------------*/
//! runtextmacro optional QEGroupKill__PlayerQuestData__ViewDetails()
//! runtextmacro optional QECustomContent__PlayerQuestData__ViewDetails()
endif
set subset = subset + 1
endloop
return s
endmethod
//------------------------------------------------------------
//================================================================
//======================External Methods==========================
//! runtextmacro optional QEDynamicEnable__PlayerQuestData__Member()
//! runtextmacro optional QEGroupKill__PlayerQuestData__Member()
//! runtextmacro optional QECustomContent__PlayerQuestData__Member()
//================================================================
implement PlayerQuestDataInit
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/***************************End Of Quest Internal Data Storage*************************************/
/**************************************************************************************************/
/**************************************************************************************************/
/*
Internal API - QuestReward :
struct QuestReward extends array
method getGold takes nothing returns integer
- return the amount of gold to be rewarded
method getLumber takes nothing returns integer
- return the amount of lumber to be rewarded
method getExperience takes nothing returns integer
- return the amount of experience to be rewarded
method setItemList takes nothing returns nothing
- get list of rewarded Item and its amount
- use itemList to access item type id
- use itemAmountList to access item reward amount
- last array position (for itemList and itemAmountList)
is set to 0 for exit looping purpose
*/
struct QuestReward extends array
//==========================Globals===============================
//---------------------Creation Globals-----------------------
private static thistype instanceCount = 0
private static thistype array recycle
private static integer recycleAmount = 0
//------------------------------------------------------------
//---------------------Ordinary Globals-----------------------
private integer g //gold
private integer l //lumber
private integer e //experience
private integer itemCount
//------------------------------------------------------------
//----------------------Public Globals------------------------
static integer array itemList
static integer array itemAmountList
//------------------------------------------------------------
//----------------------Struct Globals------------------------
/*
storing item reward list
itemList[a] = b
a : itemCount
b : even number : store item type id (starts from 0)
odd number : store reward amount (starts from 1)
*/
private Integer1D itemListTb
//------------------------------------------------------------
//--------------------External Globals------------------------
//! runtextmacro optional QECustomReward__QuestReward__Globals()
//------------------------------------------------------------
//================================================================
//====================Creation/Destroy Methods====================
static method create takes nothing returns thistype
local thistype this
if recycleAmount > 0 then
set recycleAmount = recycleAmount - 1
set this = recycle[recycleAmount]
else
set this = instanceCount + 1
set instanceCount = this
endif
set .g = 0
set .l = 0
set .e = 0
set .itemCount = 0
return this
endmethod
method destroy takes nothing returns nothing
set recycle[recycleAmount] = this
set recycleAmount = recycleAmount + 1
endmethod
//================================================================
//=====================Data Assign Methods========================
method operator gold= takes integer amount returns nothing
set .g = amount
endmethod
method operator lumber= takes integer amount returns nothing
set .l = amount
endmethod
method operator experience= takes integer amount returns nothing
set .e = amount
endmethod
method addItem takes integer itemType, integer amount returns nothing
local integer i = .itemCount*2
if itemListTb == 0 then
set .itemListTb = Item1D.create()
set .itemCount = 0
endif
set .itemListTb[i] = itemType
set .itemListTb[i+1]= amount
set .itemCount = .itemCount + 1
endmethod
//================================================================
//======================Accessor Methods==========================
method getGold takes nothing returns integer
return .g
endmethod
method getLumber takes nothing returns integer
return .l
endmethod
method getExperience takes nothing returns integer
return .e
endmethod
method setItemList takes nothing returns nothing
local integer i = 0
loop
exitwhen i == .itemCount
set itemList[i] = itemListTb[i*2]
set itemAmountList[i] = itemListTb[i*2+1]
set i = i + 1
endloop
set itemList[i] = 0
set itemAmountList[i] = 0
endmethod
//================================================================
//======================External Methods==========================
//! runtextmacro optional QECustomReward__QuestReward__Member()
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
Internal API - QuestContent :
struct QuestContent extends array
readonly static integer KILL
readonly static integer TALK
readonly static integer TALK_2
readonly static integer PICK_UP
readonly static integer KILL_OBTAIN
readonly static integer KILL_OBTAIN_ITEM
readonly static integer GOTO
method initContent takes integer index returns integer
- init all data in (index) fully
- return content type at (index)
- after init, you're free to use accessor methods for
the returned content type
- (index) starts from 0
method initContentCond takes boolean b, integer index returns integer
- init all data in (index) fully if (b) is true
- return content type at (index)
- return 0 if (b) is false
- after init, you're free to use accessor methods for
the returned content type
- (index) starts from 0
method getRegisteredAmount takes nothing returns integer
- return the number of registered content
method getContentType takes integer index returns integer
- return content type at (index)
- (index) starts from 0
/*=================KILL Content type==================*/
static method getKillRequiredUnitType takes nothing returns integer
- return unit type id of required to be killed
static method getKillRequiredAmount takes nothing returns integer
- return required kill amount for unit type
/*=============TALK/TALK_2 Content Type===============*/
static method getTalkUnit takes nothing returns unit
- return unit required to talk to
static method getTalkAbilityId takes nothing returns integer
- return talk ability id
static method getTalkDescription takes nothing returns string
- return talk description shown in QuestInfo
static method getTalkExDescription takes nothing returns string
- return talk talk content shown after talked to a unit
/*==============PICK_UP Content Type==================*/
static method getPickUpRectSet takes nothing returns RectSet
- return registered RectSet instance
static method getPickUpItemTypeId takes nothing returns integer
- return required pick up item type id
static method getPickUpRequiredAmount takes nothing returns integer
- return required pick up amount
static method getPickUpDescription takes nothing returns string
- return pick up description shown in QuestInfo
/*=============KILL_OBTAIN Content Type===============*/
static method getKillObtainRequiredUnitType takes nothing returns integer
- return kill obtain required killing unit type id
static method getKillObtainItemName takes nothing returns string
- return kill obtain required item name
static method getKillObtainRequiredAmount takes nothing returns integer
- return kill obtain required obtain amount
static method getKillObtainChance takes nothing returns real
- return kill obtain chance of obtain (virtual) item
/*==========KILL_OBTAIN_ITEM Content Type=============*/
static method getKillObtainItemRequiredUnitType takes nothing returns integer
- return kill obtain item required killing unit type id
static method getKillObtainItemItemTypeId takes nothing returns string
- return kill obtain item required item type id
static method getKillObtainItemRequiredAmount takes nothing returns integer
- return kill obtain item required obtain amount
static method getKillObtainItemChance takes nothing returns real
- return kill obtain item chance of obtain item
/*==================GOTO Content Type==================*/
static method geteGotoRect takes nothing returns rect
- return required goto rect
static method getGotoDescription takes nothing returns string
- return goto description shown in QuestInfo
static method getGotoCompleteDescription takes nothing returns string
- return goto complete description shown after reaching rect
*/
struct QuestContent extends array
//==========================Globals===============================
//---------------------Creation Globals-----------------------
private static thistype instanceCount = 0
private static thistype array recycle
private static integer recycleAmount = 0
//------------------------------------------------------------
//---------------------Ordinary Globals-----------------------
private integer subset
//globals below are for accessor methods usage
private static integer int1
private static integer int2
private static integer int3
private static unit u1
private static string str1
private static string str2
private static real r1
private static rect rec1
private static RectSet rs1
//------------------------------------------------------------
//-------------------Content Type Globals---------------------
readonly static integer KILL = 1
readonly static integer TALK = 2
readonly static integer TALK_2 = 3
readonly static integer PICK_UP = 4
readonly static integer KILL_OBTAIN = 5
readonly static integer KILL_OBTAIN_ITEM = 6
readonly static integer GOTO = 7
//------------------------------------------------------------
//----------------------Struct Globals------------------------
private Quest questId
/*
data store all of the registered content
data[a][b] = c
a : subset (for each registered content will increase subset by 1) (starts from 0)
b : content's data index, depend on type of content
c : data to store
*/
private Array2D data
//------------------------------------------------------------
//--------------------External Globals------------------------
//! runtextmacro optional QEGroupKill__QuestContent__Globals()
//! runtextmacro optional QECustomContent__QuestContent__Globals()
//------------------------------------------------------------
//================================================================
//====================Creation/Destroy Methods====================
static method create takes Quest qId returns thistype
local thistype this
if recycleAmount > 0 then
set recycleAmount = recycleAmount - 1
set this = recycle[recycleAmount]
else
set this = instanceCount + 1
set instanceCount = this
endif
set .questId = qId
set .subset = 0
set .data = Array2D.create()
return this
endmethod
method destroy takes nothing returns nothing
set recycle[recycleAmount] = this
set recycleAmount = recycleAmount + 1
endmethod
//================================================================
//=====================Data Assign Methods========================
method registerKill takes integer unitType, integer noToKill returns nothing
local Array1D a1d = data[.subset]
call Unit.monsterLinkQuest(unitType, questId)
set a1d[0].integer = KILL
set a1d[1].integer = unitType
set a1d[2].integer = noToKill
set .subset = .subset + 1
endmethod
method registerTalk takes unit u, integer aId, string description, string exDescription returns nothing
local Array1D a1d = data[.subset]
set a1d[0].integer = TALK
set a1d[1].unit = u
set a1d[2].integer = aId
set a1d[3].string = description
set a1d[4].string = exDescription
call Ability.talkLink(aId, questId, .subset)
set .subset = .subset + 1
endmethod
method registerTalk2 takes unit u, string description, string exDescription returns nothing
local Array1D a1d = data[.subset]
set a1d[0].integer = TALK_2
set a1d[1].unit = u
set a1d[2].string = description
set a1d[3].string = exDescription
set .subset = .subset + 1
endmethod
method registerPickUp takes RectSet rs, integer iId, integer noToPick, string description returns nothing
local Array1D a1d = data[.subset]
set a1d[0].integer = PICK_UP
set a1d[1].integer = rs
set a1d[2].integer = iId
set a1d[3].integer = noToPick
set a1d[4].string = description
set .subset = .subset + 1
endmethod
method registerKillObtain takes integer unitType, string itemName, integer noToObtain, real chance returns nothing
local Array1D a1d = data[.subset]
call Unit.monsterLinkQuest(unitType, questId)
set a1d[0].integer = KILL_OBTAIN
set a1d[1].integer = unitType
set a1d[2].string = itemName
set a1d[3].integer = noToObtain
set a1d[4].real = chance
set .subset = .subset + 1
endmethod
method registerKillObtainItem takes integer unitType, integer itemType, integer noToObtain, real chance returns nothing
local Array1D a1d = data[.subset]
call Unit.monsterLinkQuest(unitType, questId)
set a1d[0].integer = KILL_OBTAIN_ITEM
set a1d[1].integer = unitType
set a1d[2].integer = itemType
set a1d[3].integer = noToObtain
set a1d[4].real = chance
set .subset = .subset + 1
endmethod
method registerGoto takes rect rt, string description, string completeDescription returns nothing
local region reg = CreateRegion()
local Array1D a1d = data[.subset]
set a1d[0].integer = GOTO
set a1d[1].rect = rt
set a1d[2].string = description
set a1d[3].string = completeDescription
call Region.add(reg, questId, .subset)
call RegionAddRect(reg, rt)
call TriggerRegisterEnterRegion(enterRegTrigger, reg, null)
set .subset = .subset + 1
set reg = null
endmethod
//================================================================
//===================Content Init Method==========================
method initContent takes integer index returns integer
local Array1D a1d = data[index]
local integer contentType = a1d[0].integer
if contentType == thistype.KILL then
set int1 = a1d[1].integer
set int2 = a1d[2].integer
elseif contentType == thistype.TALK then
set u1 = a1d[1].unit
set int1 = a1d[2].integer
set str1 = a1d[3].string
set str2 = a1d[4].string
elseif contentType == thistype.TALK_2 then
set u1 = a1d[1].unit
set str1 = a1d[2].string
set str2 = a1d[3].string
elseif contentType == thistype.PICK_UP then
set rs1 = a1d[1].integer
set int1 = a1d[2].integer
set int2 = a1d[3].integer
set str1 = a1d[4].string
elseif contentType == thistype.KILL_OBTAIN then
set int1 = a1d[1].integer
set str1 = a1d[2].string
set int2 = a1d[3].integer
set r1 = a1d[4].real
elseif contentType == thistype.KILL_OBTAIN_ITEM then
set int1 = a1d[1].integer
set int2 = a1d[2].integer
set int3 = a1d[3].integer
set r1 = a1d[4].real
elseif contentType == thistype.GOTO then
set rec1 = a1d[1].rect
set str1 = a1d[2].string
set str2 = a1d[3].string
//! runtextmacro optional QEGroupKill__QuestContent__Init()
//! runtextmacro optional QECustomContent__QuestContent__Init()
endif
return contentType
endmethod
method initContentCond takes boolean b, integer index returns integer
if b then
return .initContent(index)
endif
return 0
endmethod
//================================================================
//======================Accessor Methods==========================
method getRegisteredAmount takes nothing returns integer
return .subset
endmethod
method getContentType takes integer index returns integer
return data[index][0].integer
endmethod
//--------------------KILL Content Type-----------------------
static method getKillRequiredUnitType takes nothing returns integer
return int1
endmethod
static method getKillRequiredAmount takes nothing returns integer
return int2
endmethod
//------------------------------------------------------------
//----------------TALK/TALK_2 Content Type--------------------
static method getTalkUnit takes nothing returns unit
return u1
endmethod
static method getTalkAbilityId takes nothing returns integer
return int1
endmethod
static method getTalkDescription takes nothing returns string
return str1
endmethod
static method getTalkExDescription takes nothing returns string
return str2
endmethod
//------------------------------------------------------------
//------------------PICK_UP Content Type----------------------
static method getPickUpRectSet takes nothing returns RectSet
return rs1
endmethod
static method getPickUpItemTypeId takes nothing returns integer
return int1
endmethod
static method getPickUpRequiredAmount takes nothing returns integer
return int2
endmethod
static method getPickUpDescription takes nothing returns string
return str1
endmethod
//------------------------------------------------------------
//----------------KILL_OBTAIN Content Type--------------------
static method getKillObtainRequiredUnitType takes nothing returns integer
return int1
endmethod
static method getKillObtainItemName takes nothing returns string
return str1
endmethod
static method getKillObtainRequiredAmount takes nothing returns integer
return int2
endmethod
static method getKillObtainChance takes nothing returns real
return r1
endmethod
//------------------------------------------------------------
//--------------KILL_OBTAIN_ITEM Content Type-----------------
static method getKillObtainItemRequiredUnitType takes nothing returns integer
return int1
endmethod
static method getKillObtainItemItemTypeId takes nothing returns integer
return int2
endmethod
static method getKillObtainItemRequiredAmount takes nothing returns integer
return int3
endmethod
static method getKillObtainItemChance takes nothing returns real
return r1
endmethod
//------------------------------------------------------------
//--------------------GOTO Content Type-----------------------
static method getGotoRect takes nothing returns rect
return rec1
endmethod
static method getGotoDescription takes nothing returns string
return str1
endmethod
static method getGotoCompleteDescription takes nothing returns string
return str2
endmethod
//------------------------------------------------------------
//================================================================
//======================External Methods==========================
//! runtextmacro optional QEGroupKill__QuestContent__Member()
//! runtextmacro optional QECustomContent__QuestContent__Member()
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
Internal API - QuestCondition :
struct QuestCondition extends array
method getRequiredLevel takes nothing returns integer
- retrive required level
method setRequiredQuestList takes nothing returns nothing
- get list of required Quest instance
- use questList to access
- last array position is set to 0 for exit looping purpose
*/
struct QuestCondition extends array
//==========================Globals===============================
//---------------------Creation Globals-----------------------
private static thistype instanceCount = 0
private static thistype array recycle
private static integer recycleAmount = 0
//------------------------------------------------------------
//---------------------Ordinary Globals-----------------------
private integer lv
private integer rqQuestCount
//------------------------------------------------------------
//----------------------Public Globals------------------------
static integer array questList
//------------------------------------------------------------
//----------------------Struct Globals------------------------
/*
rqQuest stores the prior required quest in order to
receive this quest.
rqQuest[a] = b
a : rqQuestCount
b : store required quest id
*/
private Integer1D rqQuest
private Quest questId
//------------------------------------------------------------
//--------------------External Globals------------------------
//! runtextmacro optional QECustomCondition__QuestCondition__Globals()
//------------------------------------------------------------
//================================================================
//====================Creation/Destroy Methods====================
static method create takes Quest qId returns thistype
local thistype this
if recycleAmount > 0 then
set recycleAmount = recycleAmount - 1
set this = recycle[recycleAmount]
else
set this = instanceCount + 1
set instanceCount = this
endif
set .lv = 0
set .questId = qId
return this
endmethod
method destroy takes nothing returns nothing
set recycle[recycleAmount] = this
set recycleAmount = recycleAmount + 1
endmethod
//================================================================
//=====================Data Assign Methods========================
method operator level= takes integer rqLevel returns nothing
set .lv = rqLevel
call Level(.lv).linkQuest(questId)
endmethod
method operator addQuest= takes Quest qId returns nothing
if .rqQuest == 0 then
set .rqQuest = Integer1D.create()
set .rqQuestCount = 0
endif
set .rqQuest[.rqQuestCount] = qId
set .rqQuestCount = .rqQuestCount + 1
call LinkedQuest.add(qId, questId)
endmethod
//================================================================
//======================Accessor Methods==========================
method getRequiredLevel takes nothing returns integer
return .lv
endmethod
method setRequiredQuestList takes nothing returns nothing
local integer i = 0
loop
exitwhen i == .rqQuestCount
set questList[i] = .rqQuest[i]
set i = i + 1
endloop
set questList[i] = 0
endmethod
//================================================================
//======================External Methods==========================
//! runtextmacro optional QECustomCondition__QuestCondition__Member()
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
Internal API - QuestEvent :
struct QuestEvent extends array
method getReceiveUnit takes nothing returns unit
- retrive receive unit
method getReturnUnit takes nothing returns unit
- retrieve return unit
*/
struct QuestEvent extends array
//==========================Globals===============================
//---------------------Creation Globals-----------------------
private static thistype instanceCount = 0
private static thistype array recycle
private static integer recycleAmount = 0
//------------------------------------------------------------
//---------------------Ordinary Globals-----------------------
private integer recEvent //receive event
private integer retEvent //return event
private unit rcUnit //receive unit
private unit rtUnit //return unit
//------------------------------------------------------------
//----------------------Struct Globals------------------------
private Quest questId
//------------------------------------------------------------
//--------------------External Globals------------------------
//! runtextmacro optional QEDynamicEvent__QuestEvent__Globals()
//------------------------------------------------------------
//================================================================
//====================Creation/Destroy Methods====================
static method create takes Quest qId returns thistype
local thistype this
if recycleAmount > 0 then
set recycleAmount = recycleAmount - 1
set this = recycle[recycleAmount]
else
set this = instanceCount + 1
set instanceCount = this
endif
set .questId = qId
set .recEvent = EVENT_QUEST_DEFAULT
set .retEvent = EVENT_QUEST_DEFAULT
return this
endmethod
method destroy takes nothing returns nothing
set recycle[recycleAmount] = this
set recycleAmount = recycleAmount + 1
endmethod
//================================================================
//=======================Internal Methods=========================
private method setReceiveUnit takes unit receiveU, integer unitIndexId returns nothing
set .rcUnit = receiveU
call Unit(unitIndexId).addReceivableQuest(questId)
endmethod
private method setReturnUnit takes unit returnU, integer unitIndexId returns nothing
set .rtUnit = returnU
call Unit(unitIndexId).addReturnableQuest(questId)
endmethod
//================================================================
//=====================Data Assign Methods========================
method operator receiveUnit= takes unit receiveU returns nothing
call .setReceiveUnit(receiveU, GetUnitId(receiveU))
endmethod
method operator returnUnit= takes unit returnU returns nothing
call .setReturnUnit(returnU, GetUnitId(returnU))
endmethod
method operator receiveReturnUnit= takes unit rrUnit returns nothing
local integer unitIndexId = GetUnitId(rrUnit)
call .setReceiveUnit(rrUnit, unitIndexId)
call .setReturnUnit(rrUnit, unitIndexId)
endmethod
method operator abilityId= takes integer aId returns nothing
call Ability.linkQuest(aId, questId)
endmethod
//================================================================
//======================Accessor Methods==========================
method getReceiveUnit takes nothing returns unit
return .rcUnit
endmethod
method getReturnUnit takes nothing returns unit
return .rtUnit
endmethod
method getReceiveEvent takes nothing returns integer
return .recEvent
endmethod
method getReturnEvent takes nothing returns integer
return .retEvent
endmethod
//================================================================
//======================External Methods==========================
//! runtextmacro optional QEDynamicEvent__QuestEvent__Member()
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
Internal API - Quest :
struct Quest extends array
integer array eventList
integer array conditionList
integer array contentList
integer array rewardList
static method getQuestCount takes nothing returns integer
- return registered quest count
method getTitle takes nothing returns string
- retrieve title
method getDescription takes nothing returns string
- retrieve description
method getEventIndex takes nothing returns QuestEvent
- return choosen event index
method getConditionIndex takes nothing returns QuestCondition
- return choosen condition index
method getContentIndex takes nothing returns QuestContent
- return choosen content index
method getRewardIndex takes nothing returns QuestReward
- return choosen reward index
/*
NOT IN USE AT THE MOMENT
method setEventList takes nothing returns nothing
- get list of QuestEvent instance
- use eventList to access
- last array position is set to 0 for exit looping purpose
method setConditionList takes nothing returns nothing
- get list of QuestCondition instance
- use conditionList to access
- last array position is set to 0 for exit looping purpose
method setContentList takes nothing returns nothing
- get list of QuestContent instance
- use contentList to access
- last array position is set to 0 for exit looping purpose
method setRewardList takes nothing returns nothing
- get list of QuestReward instance
- use rewardList to access
- last array position is set to 0 for exit looping purpose
*/
*/
struct Quest extends array
//==========================Globals===============================
//---------------------Creation Globals-----------------------
private static thistype instanceCount = 0
private static thistype array recycle
private static integer recycleAmount = 0
//------------------------------------------------------------
//---------------------Ordinary Globals-----------------------
private string name
private string detail
private integer eventCount
private integer conditionCount
private integer contentCount
private integer rewardCount
private integer choosenEvent
private integer choosenCondition
private integer choosenContent
private integer choosenReward
//------------------------------------------------------------
//----------------------Public Globals------------------------
static integer array eventList
static integer array conditionList
static integer array contentList
static integer array rewardList
//------------------------------------------------------------
//----------------------Struct Globals------------------------
/*
list[a][b] = c
a : struct type id (QuestEvent, QuestCondition, QuestConten and QuestReward)
b : eventCount/conditionCount/contentCount/rewardCount (starts from 0)
c : store corresponding struct type id
*/
private Integer2D list
readonly QuestEvent event
readonly QuestCondition condition
readonly QuestContent content
readonly QuestReward reward
//------------------------------------------------------------
//--------------------External Globals------------------------
//! runtextmacro optional QEDynamicEffectPath__Quest__Globals()
//! runtextmacro optional QEQuestCateogry__Quest__Globals()
//! runtextmacro optional QEQuestSound__Quest__Globals()
//! runtextmacro optional QEReceiveReturnEvent__Quest__Globals()
//! runtextmacro optional QERepeatableQuest__Quest__Globals()
//------------------------------------------------------------
//================================================================
//====================Creation/Destroy Methods====================
static method create takes nothing returns thistype
local thistype this
if recycleAmount > 0 then
set recycleAmount = recycleAmount - 1
set this = recycle[recycleAmount]
else
set this = instanceCount + 1
set instanceCount = this
endif
set .eventCount = 0
set .conditionCount = 0
set .contentCount = 0
set .rewardCount = 0
set .choosenEvent = 0
set .choosenCondition = 0
set .choosenContent = 0
set .choosenReward = 0
static if LIBRARY_QEDynamicEffectPath then
set .receivePath = EFFECT_PATH_RECEIVE
set .returnPath = EFFECT_PATH_RETURN
endif
set .list = Integer2D.create()
return this
endmethod
//================================================================
//=====================Data Assign Methods========================
method operator title= takes string questTitle returns nothing
set .name = questTitle
endmethod
method operator description= takes string questDescription returns nothing
set .detail = questDescription
endmethod
//================================================================
//========Event/Condition/Content/Reward Creation Methods=========
method createEvent takes nothing returns nothing
set .event = QuestEvent.create(this)
if .choosenEvent == 0 then
set .choosenEvent = .event
endif
set list[QuestEvent.typeid][.eventCount] = .event
set .eventCount = .eventCount + 1
endmethod
method createCondition takes nothing returns nothing
set .condition = QuestCondition.create(this)
if .choosenCondition == 0 then
set .choosenCondition = .condition
endif
set list[QuestCondition.typeid][.conditionCount] = .condition
set .conditionCount = .conditionCount + 1
endmethod
method createContent takes nothing returns nothing
set .content = QuestContent.create(this)
if .choosenContent == 0 then
set .choosenContent = .content
endif
set list[QuestContent.typeid][.contentCount] = .content
set .contentCount = .contentCount + 1
endmethod
method createReward takes nothing returns nothing
set .reward = QuestReward.create()
if .choosenReward == 0 then
set .choosenReward = .reward
endif
set list[QuestReward.typeid][.rewardCount] = .reward
set .rewardCount = .rewardCount + 1
endmethod
//================================================================
//======================Accessor Methods==========================
static method getQuestCount takes nothing returns integer
return thistype.instanceCount
endmethod
method getTitle takes nothing returns string
return .name
endmethod
method getDescription takes nothing returns string
return .detail
endmethod
method getEventIndex takes nothing returns QuestEvent
return .choosenEvent
endmethod
method getConditionIndex takes nothing returns QuestCondition
return .choosenCondition
endmethod
method getContentIndex takes nothing returns QuestContent
return .choosenContent
endmethod
method getRewardIndex takes nothing returns QuestReward
return .choosenReward
endmethod
/*
NOT IN USE AT THE MOMENT
method setEventList takes nothing returns nothing
local Integer1D i1d = list[QuestEvent.typeid]
local integer i = 0
loop
exitwhen i == .eventCount
set eventList[i] = i1d[i]
set i = i + 1
endloop
set eventList[i] = 0
endmethod
method setConditionList takes nothing returns nothing
local Integer1D i1d = list[QuestCondition.typeid]
local integer i = 0
loop
exitwhen i == .conditionCount
set conditionList[i] = i1d[i]
set i = i + 1
endloop
set conditionList[i] = 0
endmethod
method setContentList takes nothing returns nothing
local Integer1D i1d = list[QuestContent.typeid]
local integer i = 0
loop
exitwhen i == .contentCount
set contentList[i] = i1d[i]
set i = i + 1
endloop
set contentList[i] = 0
endmethod
method setRewardList takes nothing returns nothing
local Integer1D i1d = list[QuestReward.typeid]
local integer i = 0
loop
exitwhen i == .rewardCount
set rewardList[i] = i1d[i]
set i = i + 1
endloop
set rewardList[i] = 0
endmethod
*/
//================================================================
//======================External Methods==========================
//! runtextmacro optional QEDynamicEffectPath__Quest__Member()
//! runtextmacro optional QEQuestCateogry__Quest__Member()
//! runtextmacro optional QEQuestSound__Quest__Member()
//! runtextmacro optional QEReceiveReturnEvent__Quest__Member()
//! runtextmacro optional QERandomizeQuest__Quest__Member()
//! runtextmacro optional QERepeatableQuest__Quest__Member()
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
/*
/*
Struct for reducing duplicate code and provide addition supports funtions
*/
Internal API - Internal :
private struct Internal extends array
static method clearTextMessagesForPlayer takes player user returns nothing
- clear text massages for player (user)
static method distanceBetweenCoordinates takes real x, real y, real x2, real y2 returns real
- return the distance between (x,y) and (x2,y2)
static method isUnitSlotFull takes unit u returns boolean
- return true if unit slot is fulled
static method talkUpdate takes Quest qId, player user, integer subset, PlayerQuestData userQuestData returns nothing
- set talk/TALK_2 content in (qId) with subset of (subset) as talked for (user)
- content init is not needed as prior caller function initialized talk/TALK_2 content
static acceptQuest takes Quest qId, player user, PlayerQuestData userQuestData returns nothing
- register quest (qId) to (user)
static returnQuest takes Quest qId, player user, PlayerQuestData userQuestData returns nothing
- return quest (qId) and remove (qId) from (user) quest list
static method checkQuest takes player user, PlayerQuestData userQuestData, unit npc, real npcX, real npcY, integer eventType returns nothing
- check if there present receivable/returnable quest for player (user)
- (npc) is the unit to check for quest, (npcX) and (npcY) is the coordinates of (npc)
- (eventType) is the type of event, included :
1. EVENT_QUEST_SELECT
2. EVENT_QUEST_NEAR
static method itemDropped takes item it, Quest qId, integer subset, player owner, real itemX, real itemY returns nothing
- create item unit at (itemX,itemY) with store info of (qId), (subset) and (owner).
- (it) data will be cleared and removed
*/
private struct Internal extends array
//===========================Misc=================================
static method clearTextMessagesForPlayer takes player user returns nothing
if GetLocalPlayer() == user then
call ClearTextMessages()
endif
endmethod
static method distanceBetweenCoordinates takes real x, real y, real x2, real y2 returns real
local real dx = x2 - x
local real dy = y2 - y
return SquareRoot(dx * dx + dy * dy)
endmethod
//Method for color correction for a string (in case a string contains 1 or more color code)
static method colorCorrection takes string s, string color returns string
local integer loopInt = 0
local integer endInt = StringLength(s)
local string new = ""
local string substr
loop
set substr = SubString(s, loopInt+1, loopInt+2)
if SubString(s, loopInt, loopInt+1) == "|" and (substr == "R" or substr == "r" ) then
set new = new + color
set loopInt = loopInt + 1
else
set new = new + SubString(s, loopInt, loopInt+1)
endif
set loopInt = loopInt + 1
exitwhen loopInt >= endInt
endloop
return new + color
endmethod
static method isUnitSlotFull takes unit u returns boolean
local integer i = 0
loop
if UnitItemInSlot(u, i) == null then
return false
endif
set i = i + 1
exitwhen i == bj_MAX_INVENTORY
endloop
return true
endmethod
static method talkUpdate takes Quest qId, player user, integer subset, PlayerQuestData userQuestData returns nothing
local Effect e = GetPlayerId(user)
call thistype.clearTextMessagesForPlayer(user)
call DisplayTextToPlayer(user, 0, 0.2, QuestContent.getTalkExDescription() + "\n")
call userQuestData.updateTalk(qId, subset)
call userQuestData.removeUnitEffect(qId, subset, QuestContent.getTalkUnit(), e)
if userQuestData.getQuestStatus(qId) == PlayerQuestData.FULLFILL then
call userQuestData.showFullfillEffect(qId, e, true)
endif
endmethod
//functions that filter out unwanted units where trigger will run when selecting a unit
static method onSelectFilter takes nothing returns boolean
return GetUnitTypeId(GetFilterUnit()) != DUMMY_ID
endmethod
//================================================================
//====================Check Available Quest=======================
//check available quest once player completed a quest
static method checkAvailableQuest takes Quest qId, PlayerQuestData userQuestData returns nothing
local integer index = 0
local Effect e = userQuestData - 1
//! runtextmacro optional QERepeatableQuest__PlayerQuestData__AssignEffect()
call LinkedQuest.setLinkedQuestList(qId)
loop
set qId = LinkedQuest.questList[index]
exitwhen qId == 0
if userQuestData.isQuestReceivable(qId) then
static if LIBRARY_QEDynamicEffectPath then
if qId.getReceivePath() != "" then
call userQuestData.applyUnitEffect(qId, -1, qId.getEventIndex().getReceiveUnit(), qId.getReceivePath(), true, e)
else
call userQuestData.applyUnitEffect(qId, -1, qId.getEventIndex().getReceiveUnit(), EFFECT_PATH_RECEIVE, true, e)
endif
else
call userQuestData.applyUnitEffect(qId, -1, qId.getEventIndex().getReceiveUnit(), EFFECT_PATH_RECEIVE, true, e)
endif
endif
set index = index + 1
endloop
endmethod
//================================================================
//==================Accepting/Returning Quest=====================
static method acceptQuest takes Quest qId, player user, PlayerQuestData userQuestData returns nothing
//! runtextmacro optional QEQuestCategory__acceptQuest__Check()
call userQuestData.removeUnitEffect(qId, -1, qId.getEventIndex().getReceiveUnit(), Effect(userQuestData-1))
call PlayerQuestList(userQuestData).add(qId)
call userQuestData.setQuestStatus(qId, PlayerQuestData.IN_PROGRESS)
call userQuestData.init(qId, user)
call DisplayTextToPlayer(user, 0, 0, QUEST_COLOR_GENERAL + "Quest - |r" + qId.getTitle() + QUEST_COLOR_GENERAL + " has been registered into your quest list.")
//! runtextmacro optional QEReceiveReturnEvent__OnReceiveEvent()
//! runtextmacro optional QEQuestSound__ReceiveSound()
endmethod
static method returnQuest takes Quest qId, player user, integer userPId, PlayerQuestData userQuestData returns nothing
local QuestContent content = qId.getContentIndex()
local QuestReward reward = qId.getRewardIndex()
local string s = QUEST_COLOR_GENERAL + "Quest - |r" + qId.getTitle() + QUEST_COLOR_GENERAL + " has been fully completed."
local integer i = 0
local integer i2
if reward != 0 then
set s = s + "\n\nRewards :"
if reward.getGold() != 0 then
call SetPlayerState(user, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(user, PLAYER_STATE_RESOURCE_GOLD) + reward.getGold())
set s = s + "\n- " + I2S(reward.getGold()) + " Gold"
endif
if reward.getLumber() != 0 then
//Reward lumber
call SetPlayerState(user, PLAYER_STATE_RESOURCE_LUMBER, GetPlayerState(user, PLAYER_STATE_RESOURCE_LUMBER) + reward.getLumber())
set s = s + "\n- " + I2S(reward.getLumber()) + " Lumber"
endif
if reward.getExperience() != 0 then
set s = s + "\n- " + I2S(reward.getExperience()) + " Experience"
call AddHeroXP(HERO[userPId], reward.getExperience(), true)
endif
call reward.setItemList()
loop
exitwhen QuestReward.itemList[i] == 0
set i2 = 0
loop
exitwhen i2 == QuestReward.itemAmountList[i]
set bj_lastCreatedItem = CreateItem(QuestReward.itemList[i], 0, 0)
call UnitAddItem(HERO[userPId], bj_lastCreatedItem)
set i2 = i2 + 1
endloop
set s = s + "\n- " + QUEST_COLOR_GENERAL
if QuestReward.itemAmountList[i] > 1 then
set s = s + I2S(QuestReward.itemAmountList[i]) + " "
endif
set s = s + "|r" + GetObjectName(QuestReward.itemList[i])
set i = i + 1
endloop
//! runtextmacro optional QECustomReward__PlayerQuestData__Reward()
endif
call userQuestData.removeUnitEffect(qId, -1, qId.getEventIndex().getReturnUnit(), Effect(userQuestData-1))
call PlayerQuestList(userQuestData).remove(qId)
call userQuestData.setQuestStatus(qId, PlayerQuestData.COMPLETED)
call userQuestData.removeQuestItem(HERO[userPId], qId)
call userQuestData.clear(qId)
//! runtextmacro optional QEQuestCategory__returnQuest__Action()
//! runtextmacro optional QEQuestSound__ReturnSound()
//! runtextmacro optional QEReceiveReturnEvent__OnReturnEvent()
call thistype.checkAvailableQuest(qId, userQuestData)
call thistype.clearTextMessagesForPlayer(user)
call DisplayTextToPlayer(user, 0, 0, s)
endmethod
//================================================================
//=============Checking Receivable/Returnable Quest===============
static method checkQuest takes player user, PlayerQuestData userQuestData, unit npc, real npcX, real npcY, integer eventType returns nothing
local Unit npcId = GetUnitId(npc)
local integer maxReceivableQuest = npcId.getMaxReceivableQuest()
local integer maxReturnableQuest = npcId.getMaxReturnableQuest()
local integer index = 0
local Quest questId
local QuestContent content
local integer contentType
local integer i
local boolean receivable
local boolean returnable
set bj_lastCreatedUnit = null
loop
/**/exitwhen (index >= maxReceivableQuest) and (index >= maxReturnableQuest)
/**/
/**/set questId = npcId.getReceivableQuest(index)
/**/
/**/if questId != 0 then
/**/ set receivable = (index < maxReceivableQuest) and (userQuestData.isQuestReceivable(questId)) and (questId.getEventIndex().getReceiveUnit() == npc)
/**/
/**/ if receivable then
/**/ if eventType == EVENT_QUEST_SELECT and questId.getEventIndex().getReceiveEvent() == EVENT_QUEST_SELECT then
/**/ if bj_lastCreatedUnit == null then
/**/ set bj_lastCreatedUnit = CreateUnit(user, DUMMY_ID, npcX, npcY, 0.)
/**/ call SelectUnitForPlayerSingle(bj_lastCreatedUnit, user)
/**/ endif
/**/
/**/ call UnitAddAbility(bj_lastCreatedUnit, Ability.get(questId))
/**/ elseif eventType == EVENT_QUEST_NEAR and questId.getEventIndex().getReceiveEvent() == EVENT_QUEST_NEAR then
/**/ if not PlayerQuestList(userQuestData).fulled() then
/**/ call thistype.acceptQuest(questId, user, userQuestData)
/**/ else
/**/ call Internal.clearTextMessagesForPlayer(user)
/**/ call DisplayTextToPlayer(user, 0, 0, QUEST_LIST_FULL_MESSAGE)
/**/ endif
/**/ endif
/**/ endif
/**/endif
/**/
/**/set questId = npcId.getReturnableQuest(index)
/**/
/**/if questId != 0 then
/**/ set returnable = (index < maxReturnableQuest) and (userQuestData.getQuestStatus(questId) == PlayerQuestData.FULLFILL)
/**/
/**/ if returnable then
/**/ if eventType == EVENT_QUEST_SELECT and questId.getEventIndex().getReturnEvent() == EVENT_QUEST_SELECT then
/**/ if bj_lastCreatedUnit == null then
/**/ set bj_lastCreatedUnit = CreateUnit(user, DUMMY_ID, npcX, npcY, 0.)
/**/ call SelectUnitForPlayerSingle(bj_lastCreatedUnit, user)
/**/ endif
/**/
/**/ set i = Ability.get(questId)
/**/ call UnitAddAbility(bj_lastCreatedUnit, i)
/**/ call SetUnitAbilityLevel(bj_lastCreatedUnit, i, COMPLETE)
/**/ elseif eventType == EVENT_QUEST_NEAR and questId.getEventIndex().getReturnEvent() == EVENT_QUEST_NEAR then
/**/ call thistype.returnQuest(questId, user, GetPlayerId(user), userQuestData)
/**/ endif
/**/ endif
/**/endif
/**/
/**/set index = index + 1
endloop
//Talk quest
call PlayerQuestList(GetPlayerId(user)+1).setQuestList()
set index = 0
loop
/**/set questId = PlayerQuestList.questList[index]
/**/exitwhen PlayerQuestList.questList[index] == 0
/**/set content = PlayerQuestList.questList[index].getContentIndex()
/**/set i = 0
/**/
/**/loop
/**/ set contentType = content.getContentType(i)
/**/ exitwhen contentType == 0
/**/
/**/ if contentType == QuestContent.TALK or contentType == QuestContent.TALK_2 then
/**/ call content.initContent(i)
/**/
/**/ if npc == QuestContent.getTalkUnit() and not userQuestData.isUnitTalked(questId, i) then
/**/ if contentType == QuestContent.TALK then
/**/ if eventType == EVENT_QUEST_SELECT then
/**/ if bj_lastCreatedUnit == null then
/**/ set bj_lastCreatedUnit = CreateUnit(user, DUMMY_ID, npcX, npcY, 0.)
/**/ call SelectUnitForPlayerSingle(bj_lastCreatedUnit, user)
/**/ endif
/**/
/**/ call UnitAddAbility(bj_lastCreatedUnit, QuestContent.getTalkAbilityId())
/**/ endif
/**/ else
/**/ call thistype.talkUpdate(questId, user, i, userQuestData)
/**/ endif
/**/ endif
/**/ endif
/**/
/**/ set i = i + 1
/**/endloop
/**/
/**/set index = index + 1
endloop
endmethod
//================================================================
//======================Unit Dropping Item========================
static method itemDropped takes item it, Quest qId, integer subset, player owner, real itemX, real itemY returns nothing
local integer contentType = qId.getContentIndex().getContentType(subset)
local PlayerQuestData userQuestData = GetPlayerId(owner) + 1
//! runtextmacro optional QEQuestItem__OnDrop2__LocalVar()
set bj_lastCreatedUnit = PlayerQuestData.createQuestItem(itemX, itemY, qId, subset, owner)
if contentType == QuestContent.PICK_UP or contentType == QuestContent.KILL_OBTAIN_ITEM then
//put in condition is because quest type of "CUSTOM_CONTENT" shouldn't be reverted
//automatically
if userQuestData.getQuestStatus(qId) == PlayerQuestData.FULLFILL then
call userQuestData.revertQuestStatus(qId)
endif
call userQuestData.decreasePickAmount(qId, subset)
endif
//! runtextmacro optional QEQuestItem__OnDrop2__AddInstance()
call Item.clearData(it)
call RemoveItem(it)
endmethod
//================================================================
endstruct
/**************************************************************************************************/
/**************************************************************************************************/
//Placed here cause it needed PlayerQuestData and Internal
//! runtextmacro optional QECustomContent__CustomContent()
//! runtextmacro optional QEQuestItem__QuestItem()
/**************************************************************************************************/
/**************************************************************************************************/
/***************************************End Of Structs*********************************************/
/**************************************************************************************************/
globals
//Globals used by OnNear()
private trigger nearTrigger = CreateTrigger()
private group nearGroup = CreateGroup()
//Globals used by OnSelect()
private trigger selectTrigger = CreateTrigger()
//Globals used by OnAccept()
private trigger acceptTrigger = CreateTrigger()
//Globals used by OnDeselect()
private trigger deselectTrigger = CreateTrigger()
//Globals used by OnDeath()
private trigger deathTrigger = CreateTrigger()
//Globals used by OnDrop()
//"OnDrop" is just a prefix which reffering to OnDrop function
private trigger dropTrigger = CreateTrigger()
private item onDropItem
//Globals used by OnUse()
private trigger useTrigger = CreateTrigger()
private boolean ItemOnUse = false
//Globals used by OnPick()
private trigger pickTrigger = CreateTrigger()
//Globals used by OnLevel()
private trigger levelTrigger = CreateTrigger()
//Globals used by OnOrder()
private trigger orderTrigger = CreateTrigger()
//Globals used by OnEnterReg()
private trigger enterRegTrigger = CreateTrigger()
endglobals
//==============================================================================================
//==============================================================================================
/*
OnClick function :
*/
private function OnClick takes nothing returns boolean
local player user = DialogEx.getPlayer()
local integer structIndex = GetPlayerId(user) + 1
local Quest questId = DialogEx(structIndex).getClickedButtonQuestId()
local string s
//Set the number of lines that the quest have so that quest can display at proper place
//Default number of line (assuming description and requirement take 1 line only) : 5
if questId != 0 then
set s = PlayerQuestData(structIndex).getFullDetails(questId, structIndex)
//
call DisplayTextToPlayer(user, GetTextDisplayX(), 0, s)
endif
set user = null
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
DisplayQuestList function (get replced by new one if enabled QEQuestCategory) :
*/
static if not LIBRARY_QEQuestCategory then
private function DisplayQuestList takes player user, integer userPId returns nothing
local PlayerQuestList userQuestList = userPId + 1
local DialogEx log = userPId + 1
local integer i = 0
local Quest questId
local string s
call Internal.clearTextMessagesForPlayer(user)
if userQuestList.isEmpty() then
call DisplayTextToPlayer(user, 0, 0, QUEST_COLOR_GENERAL + "Currently you don't have any registered quest inside your list")
else
call log.clear()
set log.title = "Quest List"
call userQuestList.setQuestList()
loop
set questId = PlayerQuestList.questList[i]
exitwhen questId == 0
set s = DIALOG_HOTKEY_HIGHLIGHT + I2S(i+1) + " |r"
//check if quest name is colored. If not, add QUEST_DEFAULT_COLOR
if SubString(questId.getTitle(), 0, 1) != "|" then
set s = s + QUEST_DEFAULT_COLOR
endif
set s = s + questId.getTitle()
if PlayerQuestData(userQuestList).getQuestStatus(questId) == PlayerQuestData.FULLFILL then
set s = s + " (Completed)|r"
endif
call log.addButton(s, DIALOG_HOTKEY[i], questId)
set i = i + 1
endloop
call log.addButton("Cancel", DIALOG_HOTKEY[MAX_QUEST_PER_PLAYER], 0)
call log.display(user)
endif
endfunction
else
//! runtextmacro optional QuestCategoryDisplayTitle()
endif
//==============================================================================================
//==============================================================================================
/*
OnAccept function :
*/
private function OnAccept takes nothing returns boolean
local unit triggerUnit = GetTriggerUnit()
local PlayerQuestData userQuestData
local PlayerQuestList userQuestList
local Quest questId
local player user = GetTriggerPlayer()
local integer userPId = GetPlayerId(user)
local integer subset
local integer abilityId
if GetUnitTypeId(triggerUnit) == DUMMY_ID then
set abilityId = GetSpellAbilityId()
set questId = Ability.getQuestId(abilityId)
set userQuestData = userPId + 1
//for receiving/returning quest
if questId != 0 then
/**/call SelectUnitForPlayerSingle(HERO[userPId], user)
/**/set userQuestList = userPId + 1
/**/
/**/if GetUnitAbilityLevel(triggerUnit, abilityId) == PlayerQuestData.FULLFILL then
/**/ call Internal.returnQuest(questId, user, userPId, userQuestData)
/**/elseif not userQuestList.fulled() then
/**/ call Internal.acceptQuest(questId, user, userQuestData)
/**/else
/**/ call Internal.clearTextMessagesForPlayer(user)
/**/ call DisplayTextToPlayer(user, 0, 0, QUEST_LIST_FULL_MESSAGE)
/**/endif
endif
set questId = Ability.getTalkQuestId(abilityId)
//for talk content type
if questId != 0 then
/**/call SelectUnitForPlayerSingle(HERO[userPId], user)
/**/set subset = Ability.getTalkSubset(abilityId)
/**/
/**/call questId.getContentIndex().initContent(subset)
/**/call Internal.talkUpdate(questId, user, subset, userQuestData)
endif
//! runtextmacro optional QEQuestCategory__OnAccept__Display()
endif
set user = null
set triggerUnit = null
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
OnSelect function :
*/
private function OnSelect takes nothing returns boolean
local unit selectedUnit = GetTriggerUnit()
local player user = GetTriggerPlayer()
local integer userPId = GetPlayerId(user)
local real selectedUnitX = GetUnitX(selectedUnit)
local real selectedUnitY = GetUnitY(selectedUnit)
if selectedUnit == QUEST_INFO[userPId] then
call SetUnitX(selectedUnit, GetUnitX(HERO[userPId]))
call SetUnitY(selectedUnit, GetUnitY(HERO[userPId]))
static if not LIBRARY_QEQuestCategory then
call SelectUnitForPlayerSingle(HERO[userPId], user)
call DisplayQuestList(user, userPId)
endif
elseif GetUnitTypeId(selectedUnit) == QUEST_ITEM_ID and Unit(GetUnitId(selectedUnit)).getOwner() != user then
call ClearSelectionForPlayer(user)
elseif selectedUnit != HERO[userPId] and Internal.distanceBetweenCoordinates(GetUnitX(HERO[userPId]), GetUnitY(HERO[userPId]), selectedUnitX, selectedUnitY) <= MAX_SELECT_RANGE then
call Internal.checkQuest(user, userPId+1, selectedUnit, selectedUnitX, selectedUnitY, EVENT_QUEST_SELECT)
endif
set selectedUnit = null
set user = null
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
OnDeselect function :
*/
private function OnDeselect takes nothing returns boolean
local unit deselectedUnit = GetTriggerUnit()
if GetUnitTypeId(deselectedUnit) == DUMMY_ID then
call RemoveUnit(deselectedUnit)
endif
set deselectedUnit = null
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
OnNear function :
*/
private function OnNear takes nothing returns boolean
local integer userPId = 0
local unit pickedUnit
loop
exitwhen userPId == bj_MAX_PLAYERS
if HERO[userPId] != null then
call GroupEnumUnitsInRange(nearGroup, GetUnitX(HERO[userPId]), GetUnitY(HERO[userPId]), NEAR_RANGE, null)
set pickedUnit = null
loop
set pickedUnit = FirstOfGroup(nearGroup)
exitwhen pickedUnit == null
if pickedUnit != HERO[userPId] and GetPlayerController(GetOwningPlayer(pickedUnit)) != MAP_CONTROL_USER then
call Internal.checkQuest(Player(userPId), userPId+1, pickedUnit, 0, 0, EVENT_QUEST_NEAR)
endif
call GroupRemoveUnit(nearGroup, pickedUnit)
endloop
endif
set userPId = userPId + 1
endloop
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
OnDeath function :
*/
private function OnDeath takes nothing returns boolean
local unit diedUnit = GetTriggerUnit()
local integer diedUnitType = GetUnitTypeId(diedUnit)
local player user = GetOwningPlayer(GetKillingUnit())
local integer userPId = GetPlayerId(user)
local string s = "" //string for storing any updated content
local string s2 = "" //string for storing any completed quest
local integer i = 0
local PlayerQuestData userQuestData = userPId + 1
local Quest questId
local QuestContent content
local integer contentType
local integer subset
local integer maxSubset
local integer curAmount
local integer maxAmount
call PlayerQuestList(userQuestData).setQuestList()
loop
set questId = PlayerQuestList.questList[i]
exitwhen questId == 0
set content = questId.getContentIndex()
set subset = 0
set maxSubset = content.getRegisteredAmount()
loop
exitwhen subset == maxSubset
set contentType = content.getContentType(subset)
if not userQuestData.isSubsetCompleted(questId, subset) and content.initContentCond(contentType != QuestContent.PICK_UP and contentType != QuestContent.GOTO and contentType != QuestContent.TALK and contentType != QuestContent.TALK_2, subset) != 0 then
/**/if contentType == QuestContent.KILL then
/**/ if QuestContent.getKillRequiredUnitType() == diedUnitType then
/**/ call userQuestData.updateKillAmount(questId, subset)
/**/
/**/ set s = s + questId.getTitle() + QUEST_COLOR_GENERAL + " : |r" + GetUnitName(diedUnit)
/**/ set curAmount = userQuestData.getKillAmount(questId, subset)
/**/ set maxAmount = QuestContent.getKillRequiredAmount()
/**/
/**/ if userQuestData.isSubsetCompleted(questId, subset) then
/**/ set s = s + QUEST_COLOR_COMPLETE
/**/ else
/**/ set s = s + QUEST_COLOR_INCOMPLETE
/**/ endif
/**/
/**/ set s = s + " (" + I2S(curAmount) + "/" + I2S(maxAmount) + ")|r\n"
/**/
/**/ if userQuestData.getQuestStatus(questId) == PlayerQuestData.FULLFILL then
/**/ call userQuestData.showFullfillEffect(questId, userPId, false)
/**/
/**/ set s2 = s2 + QUEST_COLOR_GENERAL + "Quest - |r" + questId.getTitle() + QUEST_COLOR_GENERAL + " has been completed|r\n"
/**/ endif
/**/ endif
/**/elseif contentType == QuestContent.KILL_OBTAIN then
/**/ if QuestContent.getKillObtainRequiredUnitType() == diedUnitType and GetRandomReal(0., 1.) <= QuestContent.getKillObtainChance() then
/**/ call userQuestData.updateObtainAmount(questId, subset)
/**/
/**/ set s = s + questId.getTitle() + QUEST_COLOR_GENERAL + " : Obtained |r" + QuestContent.getKillObtainItemName()
/**/ set curAmount = userQuestData.getObtainAmount(questId, subset)
/**/ set maxAmount = QuestContent.getKillObtainRequiredAmount()
/**/
/**/ if userQuestData.isSubsetCompleted(questId, subset) then
/**/ set s = s + QUEST_COLOR_COMPLETE
/**/ else
/**/ set s = s + QUEST_COLOR_INCOMPLETE
/**/ endif
/**/
/**/ set s = s + " (" + I2S(curAmount) + "/" + I2S(maxAmount) + ")|r\n"
/**/
/**/ if userQuestData.getQuestStatus(questId) == PlayerQuestData.FULLFILL then
/**/ call userQuestData.showFullfillEffect(questId, userPId, false)
/**/
/**/ set s2 = s2 + QUEST_COLOR_GENERAL + "Quest - |r" + questId.getTitle() + QUEST_COLOR_GENERAL + " has been completed|r\n"
/**/ endif
/**/ endif
/**/elseif contentType == QuestContent.KILL_OBTAIN_ITEM then
/**/ if QuestContent.getKillObtainItemRequiredUnitType() == diedUnitType and GetRandomReal(0., 1.) <= QuestContent.getKillObtainItemChance() then
/**/ call userQuestData.updateDropAmount(questId, subset)
/**/
/**/ call PlayerQuestData.createQuestItem(GetUnitX(diedUnit), GetUnitY(diedUnit), questId, subset, user)
/**/ endif
/**///! runtextmacro optional QEGroupKill__OnDeath__Update()
/**/endif
endif
set subset = subset + 1
endloop
set i = i + 1
endloop
if s != "" then
call Internal.clearTextMessagesForPlayer(user)
call DisplayTextToPlayer(user, 0, 0, s + "\n" + s2)
endif
set diedUnit = null
set user = null
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
OnDrop and OnDrop2 functions :
OnDrop2 exists due to GetItemX/Y will return item's previous position value, hence a
0 second timer is required
*/
private function OnDrop2 takes nothing returns nothing
local Quest questId = Item.getQuestId()
if questId != 0 and not IsItemOwned(onDropItem) then
call Internal.itemDropped(onDropItem, questId, Item.getSubset(), Item.getOwner(), GetItemX(onDropItem), GetItemY(onDropItem))
endif
endfunction
private function OnDrop takes nothing returns boolean
if not ItemOnUse then
set onDropItem = GetManipulatedItem()
call Item.setData(onDropItem)
if Item.getQuestId() != 0 then
call TimerStart(NewTimer(), 0., false, function OnDrop2)
endif
endif
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
OnUse and ResetOnUse functions :
Exists due to when using a quest item, it will trigger OnDrop, which is not intended
behaviour.
*/
private function ResetOnUse takes nothing returns nothing
set ItemOnUse = false
endfunction
private function OnUse takes nothing returns boolean
set ItemOnUse = true
call TimerStart(NewTimer(), 0, false, function ResetOnUse)
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
OnPick functions :
For detecting if a player passes quest item to another player
*/
private function OnPick takes nothing returns boolean
local item it = GetManipulatedItem()
local player owner
local Quest questId
local integer userPId
call Item.setData(it)
set questId = Item.getQuestId()
set owner = Item.getOwner()
if questId != 0 and owner != GetTriggerPlayer() then
set userPId = GetPlayerId(owner)
call Internal.itemDropped(it, questId, Item.getSubset(), owner, GetUnitX(HERO[userPId]), GetUnitY(HERO[userPId]))
endif
set it = null
set owner = null
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
PickUp (unit), OnPickTimeout and OnOrder functions :
*/
private function PickUp takes unit orderedUnit, unit targetUnit returns nothing
local player user = GetOwningPlayer(orderedUnit)
local integer userPId = GetPlayerId(user)
local PlayerQuestData userQuestData = userPId + 1
local Unit itemDummy = GetUnitId(targetUnit)
local Quest questId
local QuestContent content
local integer contentType
local integer subset
local integer pickedAmount
local integer requirePickAmount
local integer itemTypeId
local string s
local string s2
//! runtextmacro optional QEQuestItem__PickUp__LocalVar()
if not Internal.isUnitSlotFull(orderedUnit) then
set questId = itemDummy.getQuestId()
set subset = itemDummy.getSubset()
set content = questId.getContentIndex()
set contentType = content.initContent(subset)
if contentType == QuestContent.PICK_UP then
/**/call userQuestData.updatePickAmount(questId, subset)
/**/
/**/set pickedAmount = userQuestData.getPickAmount(questId, subset)
/**/set requirePickAmount = QuestContent.getPickUpRequiredAmount()
/**/set itemTypeId = QuestContent.getPickUpItemTypeId()
/**/set s = questId.getTitle() + QUEST_COLOR_GENERAL + " : |r" + GetObjectName(itemTypeId)
/**/
/**/if userQuestData.isSubsetCompleted(questId, subset) then
/**/ set s = s + QUEST_COLOR_COMPLETE
/**/else
/**/ set s = s + QUEST_COLOR_INCOMPLETE
/**/endif
/**/
/**/set s = s + " (" + I2S(pickedAmount) + "/" + I2S(requirePickAmount) + ")"
/**/
/**/call Internal.clearTextMessagesForPlayer(user)
/**/
/**/if userQuestData.getQuestStatus(questId) == PlayerQuestData.FULLFILL then
/**/ call userQuestData.showFullfillEffect(questId, userPId, false)
/**/
/**/ set s = s + "\n" + QUEST_COLOR_GENERAL + "Quest - |r" + questId.getTitle() + QUEST_COLOR_GENERAL + " has been completed|r"
/**/endif
/**/
/**/call DisplayTextToPlayer(user, 0, 0, s)
elseif contentType == QuestContent.KILL_OBTAIN_ITEM then
/**/call userQuestData.updatePickAmount(questId, subset)
/**/
/**/set pickedAmount = userQuestData.getPickAmount(questId, subset)
/**/set requirePickAmount = QuestContent.getKillObtainItemRequiredAmount()
/**/set itemTypeId = QuestContent.getKillObtainItemItemTypeId()
/**/set s = questId.getTitle() + QUEST_COLOR_GENERAL + " : |r" + GetObjectName(itemTypeId)
/**/
/**/if userQuestData.isSubsetCompleted(questId, subset) then
/**/ set s = s + QUEST_COLOR_COMPLETE
/**/else
/**/ set s = s + QUEST_COLOR_INCOMPLETE
/**/endif
/**/
/**/set s = s + " (" + I2S(pickedAmount) + "/" + I2S(requirePickAmount) + ")"
/**/
/**/call Internal.clearTextMessagesForPlayer(user)
/**/
/**/if userQuestData.getQuestStatus(questId) == PlayerQuestData.FULLFILL then
/**/ call userQuestData.showFullfillEffect(questId, userPId, false)
/**/
/**/ set s = s + "\n" + QUEST_COLOR_GENERAL + "Quest - |r" + questId.getTitle() + QUEST_COLOR_GENERAL + " has been completed|r"
/**/endif
/**/
/**/call DisplayTextToPlayer(user, 0, 0, s)
//! runtextmacro optional QEQuestItem__PickUp__AdditionType()
endif
call itemDummy.clear()
call RemoveUnit(targetUnit)
set bj_lastCreatedItem = CreateItem(itemTypeId, 0, 0)
call UnitAddItem(orderedUnit, bj_lastCreatedItem)
call Item.assignData(bj_lastCreatedItem, questId, subset, user)
//! runtextmacro optional QEQuestItem__PickUp__AddInstance()
else
call DisplayTextToPlayer(user, 0, 0, QUEST_COLOR_GENERAL + "Your inventory is fulled.|r")
endif
set user = null
endfunction
private function OnPickTimeout takes nothing returns nothing
local Timer t = GetTimerData(GetExpiredTimer())
local unit orderedUnit = GetUnitById(t)
local real distance = Internal.distanceBetweenCoordinates(GetUnitX(orderedUnit), GetUnitY(orderedUnit), t.getTargetUnitX(), t.getTargetUnitY())
local real time = (distance-PICK_UP_RANGE)/t.getOrderedUnitMoveSpeed()
if distance <= PICK_UP_RANGE or time <= MIN_TIME_OUT then
call DisableTrigger(orderTrigger)
call IssueImmediateOrder(orderedUnit, "stop")
call EnableTrigger(orderTrigger)
call PickUp(orderedUnit, t.getTargetUnit())
call t.terminate()
else
call t.restart(time, function OnPickTimeout)
endif
set orderedUnit = null
endfunction
private function OnOrder takes nothing returns boolean
local unit orderedUnit = GetTriggerUnit()
local unit targetUnit = GetOrderTargetUnit()
local Timer t = GetUnitId(orderedUnit)
local real distance
local real time
local real x
local real y
local real speed
if GetUnitTypeId(targetUnit) == QUEST_ITEM_ID then
set x = GetUnitX(targetUnit)
set y = GetUnitY(targetUnit)
call DisableTrigger(orderTrigger)
call IssuePointOrder(orderedUnit, "move", x, y)
call EnableTrigger(orderTrigger)
if not Timer.isUnitInGroup(orderedUnit) then
set distance = Internal.distanceBetweenCoordinates(GetUnitX(orderedUnit), GetUnitY(orderedUnit), x, y)
set speed = GetUnitMoveSpeed(orderedUnit)
if distance > PICK_UP_RANGE then
set time = (distance-PICK_UP_RANGE)/speed
else
set time = 0.
endif
call Timer(GetUnitId(orderedUnit)).start(targetUnit, speed, time, function OnPickTimeout)
endif
elseif Timer.isUnitInGroup(orderedUnit) then
call t.terminate()
endif
set orderedUnit = null
set targetUnit = null
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
OnEnterReg function :
*/
private function OnEnterReg takes nothing returns boolean
local region enteredReg = GetTriggeringRegion()
local player user = GetOwningPlayer(GetTriggerUnit())
local integer i = GetPlayerId(user) + 1
local PlayerQuestData userQuestData = PlayerQuestData(i)
local Quest questId = Region.getQuestId(enteredReg)
local integer subset
if questId != 0 then
if PlayerQuestList(i).received(questId) then
set subset = Region.getSubset(enteredReg)
if not userQuestData.isPlaceWent(questId, subset) then
call questId.getContentIndex().initContent(subset)
call Effect(i-1).removeRect(QuestContent.getGotoRect())
call userQuestData.updateGoto(questId, subset)
call Internal.clearTextMessagesForPlayer(user)
call DisplayTextToPlayer(user, 0, 0, questId.getTitle() + QUEST_COLOR_GENERAL + " : " + QuestContent.getGotoCompleteDescription())
if userQuestData.getQuestStatus(questId) == PlayerQuestData.FULLFILL then
call userQuestData.showFullfillEffect(questId, i-1, true)
endif
endif
endif
endif
set enteredReg = null
set user = null
return false
endfunction
//==============================================================================================
//==============================================================================================
/*
OnLevel function :
*/
private function OnLevel takes nothing returns boolean
local unit leveledUnit = GetTriggerUnit()
local player user = GetOwningPlayer(leveledUnit)
local integer userPId = GetPlayerId(user)
local integer index = 0
local PlayerQuestData userQuestData
local Quest questId
local Level lv
if leveledUnit == HERO[userPId] then
set lv = GetHeroLevel(HERO[userPId])
set userQuestData = userPId + 1
loop
exitwhen index == lv.getMaxQuestCount()
set questId = lv.getQuestId(index)
if userQuestData.isQuestReceivable(questId) then
static if LIBRARY_QEDynamicEffectPath then
if questId.getReceivePath() != "" then
call userQuestData.applyUnitEffect(questId, -1, questId.getEventIndex().getReceiveUnit(), questId.getReceivePath(), true, Effect(userPId))
else
call userQuestData.applyUnitEffect(questId, -1, questId.getEventIndex().getReceiveUnit(), EFFECT_PATH_RECEIVE, true, Effect(userPId))
endif
else
call userQuestData.applyUnitEffect(questId, -1, questId.getEventIndex().getReceiveUnit(), EFFECT_PATH_RECEIVE, true, Effect(userPId))
endif
endif
set index = index + 1
endloop
endif
set leveledUnit = null
set user = null
return false
endfunction
//==============================================================================================
//==============================================================================================
//Init functions
private function InitQuests takes integer userPId returns nothing
local Quest questId
local Level lv = GetHeroLevel(HERO[userPId])
local Effect e = userPId
local PlayerQuestData userQuestData = userPId + 1
local integer index
if integer(lv) > 1 then
set userQuestData = userPId + 1
loop
exitwhen lv == 1
set index = 0
loop
exitwhen index == lv.getMaxQuestCount()
set questId = lv.getQuestId(index)
if userQuestData.isQuestReceivable(questId) then
static if LIBRARY_QEDynamicEffectPath then
if questId.getReceivePath() != "" then
call userQuestData.applyUnitEffect(questId, -1, questId.getEventIndex().getReceiveUnit(), questId.getReceivePath(), true, e)
else
call userQuestData.applyUnitEffect(questId, -1, questId.getEventIndex().getReceiveUnit(), EFFECT_PATH_RECEIVE, true, e)
endif
else
call userQuestData.applyUnitEffect(questId, -1, questId.getEventIndex().getReceiveUnit(), EFFECT_PATH_RECEIVE, true, e)
endif
endif
set index = index + 1
endloop
set lv = lv - 1
endloop
endif
set questId = 1
loop
exitwhen integer(questId) > Quest.getQuestCount()
if questId.getConditionIndex() == 0 then
static if LIBRARY_QEDynamicEffectPath then
if questId.getReceivePath() != "" then
call userQuestData.applyUnitEffect(questId, -1, questId.getEventIndex().getReceiveUnit(), questId.getReceivePath(), true, e)
else
call userQuestData.applyUnitEffect(questId, -1, questId.getEventIndex().getReceiveUnit(), EFFECT_PATH_RECEIVE, true, e)
endif
else
call userQuestData.applyUnitEffect(questId, -1, questId.getEventIndex().getReceiveUnit(), EFFECT_PATH_RECEIVE, true, e)
endif
endif
set questId = questId + 1
endloop
endfunction
//==============================================================================================
//==============================================================================================
//API
function QuestAddUnit takes unit u returns nothing
local player user = GetOwningPlayer(u)
local integer userPId = GetPlayerId(user)
//debug call ThrowWarning(HERO[userPId] != null, "QuestSystem", "", "QuestAddUnit", 0, "Player("+I2S(userPId)+") already have registered hero!")
set HERO[userPId] = u
set QUEST_INFO[userPId] = CreateUnit(user, QUEST_INFO_ID, GetUnitX(HERO[userPId]), GetUnitY(HERO[userPId]), 0)
static if LIBRARY_QEQuestCategory then
call QuestCategory.setUp(QUEST_INFO[userPId])
endif
call InitQuests(userPId)
set user = null
endfunction
function IsQuestReceivedByPlayer takes player p, Quest qId returns boolean
return PlayerQuestList(GetPlayerId(p)+1).received(qId)
endfunction
function IsQuestBeenCompleted takes player p, Quest qId returns boolean
return PlayerQuestData(GetPlayerId(p)+1).beenCompleted(qId)
endfunction
//GetTriggerQuestId() and GetTriggerPlayerId() is only used by some extension, which nessasary
//to pass in quest id and player id for user to use
function GetTriggerQuestId takes nothing returns Quest
return TriggerQuestId
endfunction
function GetTriggerPlayerId takes nothing returns integer
return TriggerPlayerId
endfunction
//! runtextmacro optional QECustomCondition_ReceiveEffect()
//==============================================================================================
//==============================================================================================
private function Init takes nothing returns nothing
local integer i = 0
local player p
call TriggerRegisterTimerEvent(nearTrigger, NEAR_TIMER_TIMEOUT, true)
loop
set p = Player(i)
call TriggerRegisterPlayerUnitEvent(selectTrigger , p, EVENT_PLAYER_UNIT_SELECTED , Filter(function Internal.onSelectFilter))
call TriggerRegisterPlayerUnitEvent(acceptTrigger , p, EVENT_PLAYER_UNIT_SPELL_EFFECT , null)
call TriggerRegisterPlayerUnitEvent(deselectTrigger , p, EVENT_PLAYER_UNIT_DESELECTED , null)
call TriggerRegisterPlayerUnitEvent(deathTrigger , p, EVENT_PLAYER_UNIT_DEATH , null)
call TriggerRegisterPlayerUnitEvent(dropTrigger , p, EVENT_PLAYER_UNIT_DROP_ITEM , null)
call TriggerRegisterPlayerUnitEvent(useTrigger , p, EVENT_PLAYER_UNIT_USE_ITEM , null)
call TriggerRegisterPlayerUnitEvent(pickTrigger , p, EVENT_PLAYER_UNIT_PICKUP_ITEM , null)
call TriggerRegisterPlayerUnitEvent(levelTrigger , p, EVENT_PLAYER_HERO_LEVEL , null)
call TriggerRegisterPlayerUnitEvent(orderTrigger , p, EVENT_PLAYER_UNIT_ISSUED_ORDER , null)
call TriggerRegisterPlayerUnitEvent(orderTrigger , p, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER , null)
call TriggerRegisterPlayerUnitEvent(orderTrigger , p, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
//Init dialog
call DialogEx.create().registerClickEvent(Condition(function OnClick))
set i = i + 1
exitwhen i == bj_MAX_PLAYERS
endloop
loop
set p = Player(i)
call TriggerRegisterPlayerUnitEvent(deathTrigger , p, EVENT_PLAYER_UNIT_DEATH , null)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(selectTrigger , Condition(function OnSelect) )
call TriggerAddCondition(nearTrigger , Condition(function OnNear) )
call TriggerAddCondition(acceptTrigger , Condition(function OnAccept) )
call TriggerAddCondition(deselectTrigger, Condition(function OnDeselect) )
call TriggerAddCondition(deathTrigger , Condition(function OnDeath) )
call TriggerAddCondition(dropTrigger , Condition(function OnDrop) )
call TriggerAddCondition(useTrigger , Condition(function OnUse) )
call TriggerAddCondition(pickTrigger , Condition(function OnPick) )
call TriggerAddCondition(levelTrigger , Condition(function OnLevel) )
call TriggerAddCondition(enterRegTrigger, Condition(function OnEnterReg) )
call TriggerAddCondition(orderTrigger , Condition(function OnOrder) )
call InitEx()
static if not LIBRARY_QEDynamicEvent then
if EVENT_QUEST_DEFAULT == EVENT_QUEST_SELECT then
call DestroyTrigger(nearTrigger)
endif
endif
set p = null
endfunction
//==============================================================================================
endlibrary
Optional Codes :
Optional codes are the code that provide addition features toward the basic quest system. Currently, there are total 13 configurable optional triggers.
Closed
Main Optional Code
JASS:
library QuestExtension requires /*
*====================================Quest System Extension========================================*
*
* System Description :
* Quest system extension that extends the ability of original Quest System, enable more
* features to be available apart from the basic quest features.
*
*===================================================================================================*
*
* Version
* v1.0.0
*===================================================================================================*
* Configuration :
*
* For information hiding reason, I've decided to split out each part into a single trigger thread
* To enable/disable a specific feature, simply enable/disable the trigger correspond to the feature.
* The trigger name will be same as the library name as listed below
*
* Details for each features are written in each of the corresponding trigger thread
*
* 1. */optional QECustomCondition /*
*
* 2. */optional QECustomContent /*
*
* 3 */optional QECustomReward /*
*
* 4. */optional QEDynamicEffectPath /*
*
* 5. */optional QEDynamicEnable /*
*
* 6. */optional QEDynamicEvent /*
*
* 7. */optional QEGroupKill /*
*
* 8. */optional QEQuestCategory /*
*
* 9. */optional QEQuestItem /*
*
* 10. */optional QEReceiveReturnEvent /*
*
* 11. */optional QERanomdizeQuest /*
*
* 12. */optional QERepeatableQuest /*
*
* 13. */optional QEQuestSound /*
*
*===================================================================================================*/
endlibrary
Because It is too long to post all optional triggers at here, download the test map for more details info.
Change Log :
General changes :
Restructuring of whole Quest System
Reconstruction for more intuitive API
Code readability and flexibility improvement
Code optimization
Changes to quest requirements.
Effects from now on will be applied for only one time for each units and rects.
Return effect will have priority over receive effect
For quest with dynamic effects, effects are stored in order and destroy in order
From now on supports load function (loaded unit which then added into the QuestSystem will be able to receive quest properly)
From now on able to support custom inventory system (using optional feature QE QuestItem)
Temporary removed debug operations, it will be added back later
System internal changes (QuestSystem):
From now on all methods will be non-static methods (excluded Quest.create() method)
Now user will no longer be self configure HERO, instead replaced by function QuestAddUnit takes unit u returns nothing
System Internal Changes (QuestExtension) :
Removed "QE DynamicUnitAssign"
QE CustomCondition :
Removed method assignReceiveEffectPath takes player p returns nothing and replace with function AssignReceiveEffect takes Quest qId, player p returns nothing
Removed method removeReceiveEffectPath takes player p returns nothing and replace with function RemoveReceiveEffect takes Quest qId, player p returns nothing
QE CustomReward :
method addCustomText takes string rewardText returns nothing now will be limited to be registered by once
QE DynamicEffectPath :
Renamed static method setEffectPathReceive takes string s, string attachPoint returns nothing to method operator effectPathReceive= takes string s returns nothing
Renamed static method setEffectPathReturn takes string s, string attachPoint returns nothing to method operator effectPathReturn= takes string s returns nothing
QE DynamicEnable :
Changes method enable takes player p returns nothing to method enable takes nothing returns nothing
Changes method enable takes player p returns nothing to method enable takes nothing returns nothing
Added method enableEx takes player user returns nothing
Added method disableEx takes player user reeturns nothing
Changed create method in QuestCategory struct from static method register takes integer categoryId, integer aId returns nothing to static method register takes integer categoryId, integer abilityId, integer max, string name returns nothing
In addition to the 2 parameters, (max) is the number of quests allowed to register into this category; (name) is the name of the category
Removed global constants MAX_QUEST_PER_CATEGORY
Category registration no longer be limited in QE QuestCategory
QE QuestItem :
Renamed static method createItem takes integer itemId, real x, real y, player p, QuestVirtual qv returns unit to static method create takes integer itemId, real x, real y, player p, CustomContent cc returns thistype
Renamed and modified from static method unitAddItem takes unit toAdd, unit it returns nothing to method addToUnit takes unit toAdd returns boolean
Renamed and modified from static method removeItem takes nothing returns nothing to method remove takes nothing returns nothing
Removed static method removeUnit takes unit u returns nothing
Added method setSoundEx takes integer soundType, Sound s returns nothing
QE ReceiveReturnEvent :
Renamed static method registerEx takes code c, Quest questId returns nothing to method registerOnReceive takes code c returns nothing and method registerOnReturn takes code c returns nothing for both struct ReceiveEvent and struct ReturnEvent
Removed both structs and add the API to struct Quest
Quest : Added method method isCompleted takes player p returns boolean
Description : Enable user to check if a quest is completed for player p
System internal changes (Quest Extension) :
Quest : Added "RepeatableQuest" to configure whether a quest is repeatable or not
Reason : -
--------------------------------------------------------------------
Quest : Added "QuestCategory" which allows user to define own quest category
Reason : -
--------------------------------------------------------------------
Quest Condition : Added "CustomCondition" as an addition condition type which enables user to define their custom condition type for certain quests.
Reason : -
--------------------------------------------------------------------
QE QuestItem : Added method static method removeItem takes item it returns nothing and static method removeUnit takes unit u returns nothing
Description : Same function as the native function RemoveItem and RemoveUnit, but clear internal data stored inside the item/unit.
Constant changes :
Added constant string QUEST_LIST_FULL_MESSAGE
Description : Message that will display if a player tries to receive more quest than he allowed to.
System internal changes (QuestSystem):
Added "QUEST_EVENT_NEAR" event type
Reason : -
System internal changes (QuestExtension):
Quest Reward : Added "CustomReward" reward type
Reason : -
API Changes:
QuestContent - Added API : static method registerTalk2 takes unit u, string extraDescription, string extraDescription2, string effectPath
Description : The difference between registerTalk is that this uses QUEST_EVENT_NEAR, while registerTalk uses QUEST_EVENT_SELECT
Quest Content : Removed "KillObtainItemUse" content type
Reason : Function can be replaced by "VirtualContent"
--------------------------------------------------------------------
Added struct QuestItem
Description : "VirtualContent" may have "Quest Item" creation, this struct provides that the "Quest Item" could be clear properly after the completion of a quest
--------------------------------------------------------------------
Added struct ReceiveEvent and struct ReturnEvent
Description : A struct which enables user to perform custom actions if a quest is accepted or if a quest is returned back
API Changes :
Quest : Added method isReceived takes player p returns boolean
Description : Enable user to check if a player received this Quest
Bug fixes:
Any quest contents that hold the parameter string description will now have color display properly
Reason : Previously doing "text " + "|cffff0000color text|r" + " other text" will not have color display properly
Bug Fixes :
Fixed complied errors
Fixed some minor text errors
Fixed a bug where quest system failed to init quest effect when player hero is created while initialization
General changes :
System now have built in debug checking, which optionally requires ErrorMessage
Improved the examples documentation
System internal changes (QuestSystem):
Moved few APIs from QuestSystem to QuestExtension which included :
static method operator registerReceiveEvent= takes integer i returns nothing
static method operator registerReturnEvent= takes integer i returns nothing
static method operator receiveFrom= takes unit u returns nothing
static method operator returnTo= takes unit u returns nothing
For struct QuestEvent, struct QuestCondition, struct QuestContent, struct QuestReward:
static method new takes Quest q returns nothing
Reason : Addition features of QuestSystem should be moved to the QuestExtension library.
Added API : static method operator receiveReturnUnit= takes unit u returns nothing
Reason : For replacement of missing basic API
System internal changes (QuestExtension):
Split all of the addition features each into a single trigger library thread
Reason : Due to some unsolvable issues and information hiding problems, thus made such decision.
--------------------------------------------------------------------
Quest : Added API :
method enable takes player whichPlayer returns nothing
method disable takes player whichPlayer returns nothing
Reason : So that users could dynamically enable and disable quest at anytime depend on how their map event goes.
--------------------------------------------------------------------
Reason : So that users could randomize quest more easily by just calling these methods.
--------------------------------------------------------------------
Quest Content : Added "KillObtainItemUse" content type
Reason : -
General Changes :
Added "QuestExtension" library as features extension toward the original "QuestSystem" library
Which included :
QuestContent : GroupKill
System internal changes:
QuestContent : Added Goto type content
Reason : -
QuestReward : Added Item type as reward
Reason : -
Bug Fixes:
Fixed a bug that player could failed to return quest if both of the accepting unit and returning unit are not the same unit.
General changes :
Hugely increases the system overall performance
System internal changes :
All API methods are changed from non-static to static
Reason : Methods no longer requires struct reference since system mechanism changes hugely.
--------------------------------------------------------------------
API of struct constructor changed from create to newwhich included QuestEvent, QuestCondition, QuestContent and QuestReward
Reason : No longer requires return of the corresponding struct instance, replaced with a more appropriate name.
--------------------------------------------------------------------
Added Kill & Obtain type contents
Reason : -
Constants Changes :
Added constant string QUEST_DEFAULT_COLOR
Description : Default color to display quest title at dialog if quest title have no color code present
Bug fixes :
Fixed a bug that quest system failed to check the hero's level requirement
Fixed a bug that quest dialog failed to display the default quest color (white) properly starts from 2nd quest
Fixed a bug that talk effect display to all player instead of local player only
Modified that for each update of quest content will additionally display their corresponding quest name
Reason : So that player would not confused if 2 quests have more then 2 same content get updated simultaneously.
--------------------------------------------------------------------
Removed API : method operator difficulty= takes string s returns nothing
Reason : Can easily be done though by adding the difficulty into the quest description
--------------------------------------------------------------------
Removed API : method operator descriptionLines= takes integer lines returns nothing
Reason : Integrated into the system itself
Bug Fixes :
Fixed a bug that causes Quest Info failed to create properly when 2 or more HERO init simultaneously
Fixed a bug that causes Quest Info's titles failed to display properly for Player 2 and above
Fixed a bug that causes Killing Quest failed to properly increasing kill count when 2 or more quests having the same unit type to kill
System internal changes :
HERO will be changed to be manually defined by user at any function instead of limiting it in function Init()
Reason : So that user can assign the loaded hero (if player loaded a hero) to the variable at anytime instead of only bounded to assign hero when map is in init
--------------------------------------------------------------------
add dummy hero as replacement of "-quest" command
Reason : A more convenient way to view quest details
Bug fixes :
Fixed a bug that causes "Talk" requirement malfunction
Fixed a bug that randomized quest failed to properly recognize the receive unit
System initially uploaded
Credits :
ADG for MultidimensionalArray
TriggerHappy for UnitDex
PurgeandFire for Dialog
Vexorian for TimerUtils
Magtheridon96 for SoundTools
Emm-A- for a more convenient way to view Quest Info
Author's Words : Uploaded this quest system and trying to receive some feedback, currently it is still in progress, more features will be implemented in future.
Condition Types : Have 2 Conditions Types, with 1 addition type :
- Level condition
- Quest condition (requires to complete Quest1 before could receive Quest2)
Additional Feature :
- Customize your quest condition using "CustomCondition"
Contents Types : Have 6 base contents types, with 2 addition types :
- Kill (number) of (unit type)
- Talk to (unit)
- Pick up (number) of (item)
- Kill (unit type) and obtain (number) (item)
- Kill (unit type) and pick up (number) (item)
- Go to a (place)
Additional Features :
- Kill (number) of (grouped unit type)
- Customize your own quest content using "VirtualContent"
How to view quest details : To view the quest info, Press F2 (Quest Info)
In Progress update :
Current have no upcoming update
Requires Feedback :
Quest system may contains bug, hence I would like to have people report found bug, and feel free to give any suggestion regarding quest system.
I think creating a quest system is a very good idea, as making quests is quite tedious without some kind of standards ..
I think, that your system needs more visible features though.
Personally, I would prefer the quests tab over your system, but if one would agree to use your system, then, still, I think it is not nice having to access the system via "-quest".
Maybe you could use some dummy hero button to access a dialog field or something.
Also, some notice should flash up once a player has achieved some parts of a quest.
Also it would be nice to be able to have sub-quests or more than one requirement per quest.
Afterall, this is just my opinion
Are you planning on adding more quest types?
Maybe, add "go to some rect/region(s)" and "collect x of some unit or item"?
Another question, why exactly dont you use the blizzard quest tab?
It will have more features, currently i'm creating the most basic things first before coming up additional features.
What i planned so far would be :
- adding the API which enables auto ping for quest location
- adding categorized quest to Main Quest, Optional Quest, and perhaps even let user define the category themselves.
- adding "priority" option toward quest requirements (enable to control quest requirements's flows)
- probably adding "history quest" where stored player's completed quests (which I'm unsure)
- and awaiting for someone to suggest more suggestion
Personally, I would prefer the quests tab over your system, but if one would agree to use your system, then, still, I think it is not nice having to access the system via "-quest".
I pretty much agree, so far keep typing "-quest" really pretty much annoying after a long time usage, hopefully could find a more convenient way for it though.
Maybe you could use some dummy hero button to access a dialog field or something.
As quest tab is too small, limited description, and are usually reserved for game info, credits, and only able to categorized only 2 : Main / Optional
So created a custom text for more dynamic configuration option.
Updated to v0.1.5 - Change Log :
System internal changes :
HERO will be changed to be manually defined by user at any function instead of limiting it in function Init()
Reason : So that user can assign the loaded hero (if player loaded a hero) to the variable at anytime instead of only bounded to assign hero when map is in init
--------------------------------------------------------------------
add dummy hero as replacement of "-quest" command
Reason : A more convenient way to view quest details
Bug fixes :
Fixed a bug that causes "Talk" requirement malfunction
Fixed a bug that randomized quest failed to properly recognize the receive unit
I've thought of this myself at various points.
I never went all the way, but I did make some shortcuts in GUI which was way too limiting for a public release.
These days I would use the following approach:
Quest myQuest = Quest.allocate()
myQuest.name = "bla"
myQuest.desc = "do this"
//and so on
myQuest.onComplete = someFunctionToGiveRewards
myquest.cond = filtering function that returns a boolean (to check if quest is complete) this could probably be achieved with operators as well.
which would mean you only have to trigger the actual quest content while having a very clear API for the declaration and completion of a quest, which I think is quite fair.
For the sample API you provided, it does works. Setting a quest toward 2 functions (rewardFunction and conditionFunction) gonna be a mess as repeating actions would occur for each function you defined (just little change toward the reward/condition). And also it won't be able to generate randomize quests (which some people would need it)
The things i want to achieve is, to make the quest system to be as dynamic as possible. It would certainly increases the difficulty to use the system, but you would end up with really nice effect that not only bounds to quest contents type like killing, picking and talking only.
Currently the system doesn't shine enough to attract more people as there is no unique features, that why it is in the lab so that i could have a more feedback/suggestion toward the future system development.
So far i'm gonna focus on basic features like killing, picking, talking. Condition like level condition, quest complete condition. Reward would be exp, gold and lumber.
Of course there would present some maps that are using custom resources (like custom resources integrated into multiboard) which gonna need a custom function to reward the player and other several features i am planning to add in (after completing the basic quest system).
Chat system... a really nice idea to enhance the quest system. I've viewed the demo, demo is nice.
Even though, personally I'm not a fan of that chat system as :
Completely changing the overall quest accepting mechanism
Redoing the whole system gonna take huge time for me, which i'm not really going to do it
Requirements are too many
I'm try to avoiding user to import too many libraries just to use a single system
Anywhere, such a chat system can still be implemented without changing the overall mechanism, but requires a modification to suit into my system. I would consider your suggestion as future implementation
Your approach on the other hand is -too- complex.
IMO your way of doing it requires thousands (not literary) of different variables that may be required for certain quests.
As you stated yourself, not all quests are collect that, or kill this.
The main thing that making this system complex is using the game text as display instead of the war3 quest dialog.
Or if you're saying toward the API, i can't really disagree with that, over long API can definitely scares people away.
I will plan to lower the amount of API using constant booleans, which user would need to define themselves whether to enable a specific feature or not.
Modified that for each update of quest content will additionally display their corresponding quest name
Reason : So that player would not confused if 2 quests have more then 2 same content get updated simultaneously.
--------------------------------------------------------------------
Removed API : method operator difficulty= takes string s returns nothing
Reason : Can easily be done though by adding the difficulty into the quest description
--------------------------------------------------------------------
Removed API : method operator descriptionLines= takes integer lines returns nothing
Reason : Going to integrating into the system itself and users would save 1 line
Bug Fixes :
Fixed a bug that causes Quest Info failed to create properly when 2 or more HERO init simultaneously
Fixed a bug that causes Quest Info's titles failed to display properly for Player 2 and above
Fixed a bug that causes Killing Quest failed to properly increasing kill count when 2 or more quests having the same unit type to kill
A side note : Sorry for the long delay update, I've reworked the PICK_UP content type 2 times, that the main reason for the long delay as the output of PICK_UP content doesn't statify my own requirement, hopefully you guys would like the output of it.
Updated to v0.3.0 - Change Log :
General changes :
Hugely increases the system overall performance
System internal changes :
All API methods are changed from non-static to static
Reason : Methods no longer requires struct reference since system mechanism changes hugely.
--------------------------------------------------------------------
API of struct constructor changed from create to newwhich included QuestEvent, QuestCondition, QuestContent and QuestReward
Reason : No longer requires return of the corresponding struct instance, replaced with a more appropriate name.
--------------------------------------------------------------------
Added Kill & Obtain type content
Reason : -
Constants Changes :
Added constant string QUEST_DEFAULT_COLOR
Description : Default color to display quest title at dialog if quest title have no color code present
Bug fixes :
Fixed a bug that quest system failed to check the hero's level requirement
Fixed a bug that quest dialog failed to display the default quest color (white) properly starts from 2nd quest
Fixed a bug that talk effect display to all player instead of local player only
Details for code improvement :
Previously, the system is using full function calling for all data saving/loading:
JASS:
//register talk function
/**/
/**/ set table[temp][QUEST_CONTENT][this][subset].integer[0] = TALK
/**/ set table[temp][QUEST_CONTENT][this][subset].unit[1] = u
/**/ set table[temp][QUEST_CONTENT][this][subset].integer[2] = aId
/**/ set table[temp][QUEST_CONTENT][this][subset].string[3] = extraDescription
/**/ set table[temp][QUEST_CONTENT][this][subset].string[4] = extraDescription2
/**/ set table[temp][QUEST_CONTENT][this][subset].string[5] = effectPath
Now, the system is using saved table index and use it when needed :
JASS:
//register talk function
//tb is defined when calling QuestContent.create()
/**/
/**/ set tb.integer[0] = TALK
/**/ set tb.unit[1] = u
/**/ set tb.integer[2] = aId
/**/ set tb.string[3] = extraDescription
/**/ set tb.string[4] = extraDescription2
/**/ set tb.string[5] = effectPath
Such method could avoids recalling many unnecessary function calls, and thus improving the system overall performance.
By using JassPerfCounter() function (Sorry I forgot where did I got it) for rough testing with "Test" trigger (without pick&obtain quest type):
Previous performance time : ~3.8 - 4.5 milliseconds
Now performance time : ~2.0 - 2.5 milliseconds
Please be noted that API is changes that all methods are changed from "non-static" to "static"
I am not fun of your API at all, everything but one thing is static, so there is no point having structs at all, appart from one function(you could argue for the operators).
Also, what about having a quest "Kill 10 wolves" but my map has 3 different types of wolves that all qualify?(Lets say grey, black and dire wolf)
I am not fun of your API at all, everything but one thing is static, so there is no point having structs at all, appart from one function(you could argue for the operators).
Previously does requires struct reference, but since the internal changes of the system, that the reason changed from non-static to static.
The struct references is still using, struct references are stored in the system self and using it throughout all the static methods
Struct references for each Quest parts (QuestEvent, QuestCondition, QuestContent, QuestReward) are still used for generating a set of random quest event/condition/content/reward
Struct naming is pretty nice, like operators. Even without it, imagine like :
I still prefer first, as the second part RegisterKill() may be comes from other system which we probably don't know which component part it comes from.
Of course, that all of my opinion, unless there really present a better reason toward changing it, i will definitely do so.
Unfortunately that is currently not yet implemented, only specific unit type. I can implement it, if you need.
Misinterpreted your statement, that will have some difficulty, but sure, i will include it as to-do list.
Since the basic component of the quest is basically done, i can move toward to features improvement. Feel free to suggest stuff
This is where Polymorphism comes in handy
Shame it is so poorly available in JASS.
But I wonder if it should be part of the system's core or if it should be given with the examples in the map.
I personally vote for the latter.
Since you voted for a map specific. If the map is going to need it, user will need to create their own, since the quest system is 100% incompatible with it. The user will definitely request the quest system creator to include the feature (if I'm the user will definitely do so ). Perhaps i can make it as optional external library, but i will need to take care of the encapsulation, which gonna be a big deal.
static method operator registerReceiveEvent= takes integer i returns nothing
static method operator registerReturnEvent= takes integer i returns nothing
static method operator receiveFrom= takes unit u returns nothing
static method operator returnTo= takes unit u returns nothing
For struct QuestEvent, struct QuestCondition, struct QuestContent, struct QuestReward:
static method new takes Quest q returns nothing
Reason : Addition features of QuestSystem should be moved to the QuestExtension library.
Added API : static method operator receiveReturnUnit= takes unit u returns nothing
Reason : For replacement of missing basic API
System internal changes (QuestExtension):
Split all of the addition features each into a single trigger library thread
Reason : Due to some unsolvable issues and information hiding problems, thus made such decision.
--------------------------------------------------------------------
Quest : Added API :
method enable takes player whichPlayer returns nothing
method disable takes player whichPlayer returns nothing
Reason : So that users could dynamically enable and disable quest at anytime depend on how their map event goes.
--------------------------------------------------------------------
Reason : So that users could randomize quest more easily by just calling these methods.
--------------------------------------------------------------------
Quest Content : Added "KillObtainItemUse" content type
Reason : -
This update is mainly focus on system self safety check, reducing the number of API that user need to use for defining a basic quest and improves the quest examples documentation. I hope the amount of basic APIs is user-friendly enough. I would like some opinion from you guys though.
Quest Content : Removed "KillObtainItemUse" content type
Reason : Function can be replaced by "VirtualContent"
--------------------------------------------------------------------
Added struct QuestItem
Description : "VirtualContent" may have "Quest Item" creation, this struct provides that the "Quest Item" could be clear properly after the completion of a quest
--------------------------------------------------------------------
Added struct ReceiveEvent and struct ReturnEvent
Description : A struct which enables user to perform custom actions if a quest is accepted or if a quest is returned back
API Changes :
Quest : Added method isReceived takes player p returns boolean
Description : Enable user to check if a player received this Quest
Bug fixes:
Any quest contents that hold the parameter string description will now have color display properly
Reason : Previously doing "text " + "|cffff0000color text|r" + " other text" will not have color display properly
Since no responses, i assume all working fine ._. Anywhere, started from 0.6.0 version, user is now enable to customize their own quest content using "VirtualContent", 2 examples which uses "VirtualContent" is included inside the map.
Quest Content is done, hopefully. From now on i will focus on the other parts
Quest Reward : Added "CustomReward" reward type
Reason : -
API Changes:
QuestContent - Added API : static method registerTalk2 takes unit u, string extraDescription, string extraDescription2, string effectPath
Description : The difference between registerTalk is that this uses QUEST_EVENT_NEAR, while registerTalk uses QUEST_EVENT_SELECT
After a long time for waiting, an update for QuestSystem to v0.7.0 is here now! Originally I've planned to skip v0.7.0 and directly get to v0.8.0, but facing real hard problems while implementing "quest content precedence". I might decide not to implement it, probably depend on the requirement of you guys, or my own conscious.
Quest : Added method method isCompleted takes player p returns boolean
Description : Enable user to check if a quest is completed for player p
System internal changes (Quest Extension) :
Quest : Added "RepeatableQuest" to configure whether a quest is repeatable or not
Reason : -
--------------------------------------------------------------------
Quest : Added "QuestCategory" which allows user to define own quest category
Reason : -
--------------------------------------------------------------------
Quest Condition : Added "CustomCondition" as an addition condition type which enables user to define their custom condition type for certain quests.
Reason : -
--------------------------------------------------------------------
QE QuestItem : Added method static method removeItem takes item it returns nothing and static method removeUnit takes unit u returns nothing
Description : Same function as the native function RemoveItem and RemoveUnit, but clear internal data stored inside the item/unit.
Constant changes :
Added constant string QUEST_LIST_FULL_MESSAGE
Description : Message that will display if a player tries to receive more quest than he allowed to.
Slightly delayed the update time due to celebration of Chinese New Year. This update continue includes the missing basic features which I forgot to implement in earlier version. Next update will focus on addition features.
#1 i am puzzled as to how a function that would appear to modify the parameters of 1 particular quest could possibly be static and take no Quest parameter or the like.
#2 i feel that something of the like would serve better:
JASS:
/**
got a chat command handling library that makes heavy use of this, it uses triggers underneath i think but it makes for very clean API-s if used well.
ref: http://www.wc3c.net/vexorian/jasshelpermanual.html#funcinterf
the QuestCondition interface allows you to easily handle different conditions.
If you have any common conditions you can simply ship tghem with the system. and if somebody wants to add their own "custom" conditions then they can write them themself.
It is important that a Quest is not too tightly coupled with Questconditions for that makes is very hard to extend by 3rd parties ( you would need to modify the code thus risk breaking it.. thus necessitating that hack of QE lib..)
*/
function interface QuestCondition takes Quest q, player p returns boolean
/**
also consider.. or the like, really the moment you begin to treat a function pointer like any other variable, your code will become so much simpler.
*/
function interface QuestReward takes Quest q, player p returns nothing
struct QuestConditions
private QuestCondition array[MAX_COMPLETE_CONDITIONS] // or use Table if you dont like fixed size..
private method add takes QuestCondition returns nothing
/**
*
*/
private method checkConditions takes nothing returns boolean
endstruct
struct Quest
private QuestConditions questCompletionConditions
private QuestConditions questBeginConditions
static if LIBRARY_QuestCustomData
/*optional include, provided by user of library, can be anything the user wants to store.*/
private QuestCustomData questCustomData
endif
...
private static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.questCompletionConditions = QuestConditions.create()
set this.questBeginConditions= QuestConditions.create()
...
return this
endmethod
endstruct
usage like:
JASS:
function KillsSinceQuestBegunGTE30 takes Quest q returns boolean ....
set q = Quest.create()
...
call q.questCompletionConditions.add(QuestCondition.KillsSinceQuestBegunGTE30)
Vjass is more than just jass2 with structs, it is far more powerful and helps you write a lot better code.. if you just used it...
Same is with C and C++, C++ is not just C with classes even tho some people can never get past that particular stage..
Of course given that some things in jass2 are more expensive than other there might be reasons to create special cases for things, however these should NOT be visible to the user, ever. if you do shady shit then keep ur skeletons locked in your closet, and make sure the user will never be able to tell the difference... except maybe better perf.
JASS:
method operator questConditionList= takes integer whichList returns nothing
method operator questContentList= takes integer whichList returns nothing
method operator questRewardList= takes integer whichList returns nothing
// why not instead:
method operator questConditionList= takes QuestConditions questConditionList returns nothing
method operator questContentList= takes QuestContent questContent returns nothing
method operator questRewardList= takes QuestRewards questRewards returns nothing
// i object to taking a raw integer as a parameter when really its a pointer to a struct
also interacting with the W3 built in quest display would be nice...
really this is not something hard(the easiest bit i think actually, just boilerplate.. why else you use libs if not to reduce boilerplate?!), at least there should be an optional include you can have that makes it work if wanted..
I can hardly reply you within a short time, your sentences requires me to read over 20 times in order to almost understand them... And please refrain from double posting...
#1 i am puzzled as to how a function that would appear to modify the parameters of 1 particular quest could possibly be static and take no Quest parameter or the like.
Function interface is the thing I haven't learn yet, because I couldn't understand it when trying to learn vjass. Now I do understand, but I hope I could know when this can help me code better and understandable code. According to your example, every quest condition user gonna need to create new function block then could add in?
Of course given that some things in jass2 are more expensive than other there might be reasons to create special cases for things, however these should NOT be visible to the user, ever.
Its been a long time since the last time I've update this quest system, and now I've like to introduce version v1.0.0 of QuestSystem which I've working on several weeks, there is huge changes to API compare to last version (v0.8.1), as well as several new functions to accommodate custom inventory system.
General changes :
Restructuring of whole Quest System
Reconstruction for more intuitive API
Code readability and flexibility improvement
Code optimization
Changes to quest requirements.
Effects from now on will be applied for only one time for each units and rects.
Return effect will have priority over receive effect
For quest with dynamic effects, effects are stored in order and destroy in order
From now on supports load function (loaded unit which then added into the QuestSystem will be able to receive quest properly)
From now on able to support custom inventory system (using optional feature QE QuestItem)
Temporary removed debug operations, it will be added back later
System internal changes (QuestSystem):
From now on all methods will be non-static methods (excluded Quest.create() method)
Now user will no longer be self configure HERO, instead replaced by function QuestAddUnit takes unit u returns nothing
System Internal Changes (QuestExtension) :
Removed "QE DynamicUnitAssign"
QE CustomCondition :
Removed method assignReceiveEffectPath takes player p returns nothing and replace with function AssignReceiveEffect takes Quest qId, player p returns nothing
Removed method removeReceiveEffectPath takes player p returns nothing and replace with function RemoveReceiveEffect takes Quest qId, player p returns nothing
QE CustomReward :
method addCustomText takes string rewardText returns nothing now will be limited to be registered by once
QE DynamicEffectPath :
Renamed static method setEffectPathReceive takes string s, string attachPoint returns nothing to method operator effectPathReceive= takes string s returns nothing
Renamed static method setEffectPathReturn takes string s, string attachPoint returns nothing to method operator effectPathReturn= takes string s returns nothing
QE DynamicEnable :
Changes method enable takes player p returns nothing to method enable takes nothing returns nothing
Changes method enable takes player p returns nothing to method enable takes nothing returns nothing
Added method enableEx takes player user returns nothing
Added method disableEx takes player user reeturns nothing
Changed create method in QuestCategory struct from static method register takes integer categoryId, integer aId returns nothing to static method register takes integer categoryId, integer abilityId, integer max, string name returns nothing
In addition to the 2 parameters, (max) is the number of quests allowed to register into this category; (name) is the name of the category
Removed global constants MAX_QUEST_PER_CATEGORY
Category registration no longer be limited in QE QuestCategory
QE QuestItem :
Renamed static method createItem takes integer itemId, real x, real y, player p, QuestVirtual qv returns unit to static method create takes integer itemId, real x, real y, player p, CustomContent cc returns thistype
Renamed and modified from static method unitAddItem takes unit toAdd, unit it returns nothing to method addToUnit takes unit toAdd returns boolean
Renamed and modified from static method removeItem takes nothing returns nothing to method remove takes nothing returns nothing
Removed static method removeUnit takes unit u returns nothing
Added method setSoundEx takes integer soundType, Sound s returns nothing
QE ReceiveReturnEvent :
Renamed static method registerEx takes code c, Quest questId returns nothing to method registerOnReceive takes code c returns nothing and method registerOnReturn takes code c returns nothing for both struct ReceiveEvent and struct ReturnEvent
Removed both structs and add the API to struct Quest
method createEvent takes nothing returns nothing
// -create new event
// -a quest can have multiple events
// -syntax for setting event is (assuming (q) is Quest instance) :
// q.event.(QuestEvent methods)
but then even if I register 100 events into a quest, I can only access one by the readonly variable event in the struct Quest, so there is no point registering more than one is there.
Maybe you could return the created event from the function, which would allow me to either write it down somewhere else and then modify it, or at least do in-place access to it.
Or maybe I misunderstood what is going on in this(I skimmed through the code, and it is fucking enormous to be honest )
but then even if I register 100 events into a quest, I can only access one by the readonly variable event in the struct Quest, so there is no point registering more than one is there.
It also pointless to do so as you (assuming you clearly know what you want) only need one creation if rest are left as unused.
The main reason it exists is to compatible with "QE RandomizeQuest" so that user doesn't need to change the registration method completely. Also mainly for randomizeQuest usage, where you could assign the current quest event/condition/content/reward instance from one of them.
Maybe you could return the created event from the function, which would allow me to either write it down somewhere else and then modify it, or at least do in-place access to it.
Talking about half game modifying the quest, I temporary left it aside as so far I don't think anyone will need it, but I can make it easily return any of the quest components struct index for mid game modification, yet currently I'm unsure if such modification will produce unwanted bug or not.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.