Steel
Software Engineer
- Reaction score
- 109
Alright, first let me say that this is a work in progress. There is probably a lot of buggy code, redundant code or just things with the code that need improvements. If you see anything you think should be re-written please let me know.
I saw someone else post a quest template for something they were working on. It didn't look very user friendly and / or customizable (No offense to the person in question) so I thought I'd lend people my experience in the system I'm building on the subject of quests.
What is this?
This is a system I threw together to easily add quests to the quest log for all players to do. I didn't like making several triggers for each quest I wanted to make. This dramatically simplifies the process.
Other Info:
Currently this system only supports the template of a "kill quest," which is one that requires you to kill X amount of Y enemies.
Example usage:
Let me break this down...The parameters you can read off the function that these are passed into:
I believe those are very self explanatory.
Requirements:vJASS, CSCache, A typical Handles System
Future Plans:
-Create templates for other types of quests
-Optimize Code
----Clean Memory Leaks
----Remove Redundant Code (Simplify function calls)
-Move away from requiring Handle Vars
-Move away from requiring CSCache
-Re-code Quest Management library
Quest Mangement Core Library:
Quest Management Library
I saw someone else post a quest template for something they were working on. It didn't look very user friendly and / or customizable (No offense to the person in question) so I thought I'd lend people my experience in the system I'm building on the subject of quests.
What is this?
This is a system I threw together to easily add quests to the quest log for all players to do. I didn't like making several triggers for each quest I wanted to make. This dramatically simplifies the process.
Other Info:
Currently this system only supports the template of a "kill quest," which is one that requires you to kill X amount of Y enemies.
Example usage:
JASS:
call CreateQuestKill(039;Hamg039;, -357, 65,true, 2, 8, "Assault!", "The Tuskarr have been roaming around recently...They are making my evocation rather difficult. Kill 3 Tuskarr Chieftain and end their disruption", "ReplaceableTextures\\CommandButtons\\BTNTuskaarBlack.blp", 039;n000039;,3, 039;afac039;, 50, 10, 5)
Let me break this down...The parameters you can read off the function that these are passed into:
JASS:
function CreateQuestKill takes integer StartNPC, real x, real y, boolean Repeatable, integer minlevel, integer maxlevel, string QuestTitle, string QuestDescription, string Icon, integer UnitToKillID,integer HowMany, integer RewardItemId, integer RewardExp, integer RewardGold, integer RewardLumber returns Quest
I believe those are very self explanatory.
Requirements:vJASS, CSCache, A typical Handles System
Future Plans:
-Create templates for other types of quests
-Optimize Code
----Clean Memory Leaks
----Remove Redundant Code (Simplify function calls)
-Move away from requiring Handle Vars
-Move away from requiring CSCache
-Re-code Quest Management library
Quest Mangement Core Library:
JASS:
library QuestManagementCore
globals
//Configurables
string expcolor = "|c00808000"
string lumbercolor = "|c00008000"
string goldcolor = "|c00FFFF00"
string itemcolor = "|c00800080"
integer array QuestStructLink
integer CurrStruct
integer QuestCount = 1
string array QuestTitles
constant integer MaxNumberOfQuests = 100
string QuestError
endglobals
struct Quest
trigger trig
string questtype
integer start
unit startu
real startx
real starty
boolean repeat
string title
string desc
integer min
integer max
integer howmany
integer countid
integer itemid
integer exp
integer gold
integer lumber
//Player Quest Variables
boolean array started[10]
boolean array finished[10]
boolean array complete[10] //Complete is different from finish. If we are finished that means we are ready for turn in
integer array count[10] //Complete means the entire quest is done
endstruct
function printerror takes player p, string str returns nothing
call DisplayTimedTextToPlayer(p, 0,0,30,str)
endfunction
function CreateQuestEvent takes trigger t returns nothing
local integer i = 0
loop
exitwhen i > 9
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SELECTED, null)
set i = i+1
endloop
endfunction
function GetStartNPCName takes Quest q returns string
local unit u
local string str
set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitTypeId(q.startu), 0,0,0)
set str = GetUnitName(u)
call RemoveUnit(u)
set u = null
return str
endfunction
function quest_click_conditions_setup takes nothing returns boolean
local trigger t = GetTriggeringTrigger()
local Quest q = GetHandleInt(t,"quest")
local integer idx = GetPlayerId(GetTriggerPlayer())+1
local unit u = Hero[idx]
local boolean b1 = (GetTriggerUnit() == q.startu)
local boolean b2 = not (q.started[idx])
local boolean b3 = not (q.finished[idx])
local boolean b4 = not (q.complete[idx])
set t = null
return (b1 and b2 and b3 and b4)
endfunction
function quest_click_actions_setup takes nothing returns nothing
local trigger t = GetTriggeringTrigger()
local Quest q = GetHandleInt(t,"quest")
local player p = GetTriggerPlayer()
local integer idx = GetPlayerId(p)+1
local unit u = Hero[idx]
local boolean b1 = (DistanceBetweenPoints(GetUnitLoc(u), GetUnitLoc(q.startu)) <= 400.00)
local boolean b2 = GetUnitLevel(u)>=q.min
local boolean b3 = GetUnitLevel(u)<=q.max
if not(b1) or not(b2) or not(b3) then
if not(b1) then
call printerror(p, "Get Closer!")
elseif not(b2) then
call printerror(p, "Return when you are a higher level")
elseif not(b3) then
call printerror(p, "You are too powerful to begin this quest")
endif
return
endif
set q.started[idx]=true
call QMDiscover(GetTriggerPlayer(),q.title)
set p = null
endfunction
function quest_conditions_kill takes nothing returns boolean
local integer idx = GetPlayerId(GetOwningPlayer(GetKillingUnit()))+1
local trigger t = GetTriggeringTrigger()
local Quest q = GetHandleInt(t,"quest")
local boolean b1 = (GetUnitTypeId(GetTriggerUnit()) == q.countid)
local boolean b2 = (q.started[idx])
local boolean b3 = not (q.finished[idx])
return b1 and b2 and b3
endfunction
function quest_actions_kill takes nothing returns nothing
local player p = GetOwningPlayer(GetKillingUnit())
local integer idx = GetPlayerId(p)+1
local trigger t = GetTriggeringTrigger()
local Quest q = GetHandleInt(t,"quest")
local unit u = CreateUnit(Player(15), q.countid, 0,0,0)
set q.count[idx]=q.count[idx]+1
call QMReqCount(p, q.title, "Kill "+I2S(q.howmany)+" "+GetUnitName(u), q.count[idx], q.howmany)
call RemoveUnit(u)
if (q.count[idx] == q.howmany) then
set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), q.countid, 0,0,0)
set q.finished[idx]=true
call QMReqComplete(p,q.title,"Kill "+I2S(q.howmany)+" "+GetUnitName(u))
call RemoveUnit(u)
call DisplayTextToPlayer(p,0,0,"Return to "+GetStartNPCName(q)+" for your reward")
endif
endfunction
function quest_reward_conditions takes nothing returns boolean
local trigger t = GetTriggeringTrigger()
local Quest q = GetHandleInt(t,"quest")
local integer idx = GetPlayerId(GetOwningPlayer(GetTriggerUnit()))+1
local boolean b1 = q.finished[idx]
local boolean b2 = IsUnitVisible(GetTriggerUnit(), GetOwningPlayer(q.startu))
local boolean b3 = not (q.complete[idx])
return b1 and b2 and b3
endfunction
function quest_reward_actions takes nothing returns nothing
local trigger t = GetTriggeringTrigger()
local Quest q = GetHandleInt(t,"quest")
local unit u = GetTriggerUnit()
local player p = GetOwningPlayer(u)
local integer idx = GetPlayerId(p)+1
local item i
//If quest is repeatable then it is never completed
if (not(q.repeat)) then
set q.complete[idx] = true
else
set q.count[idx] = 0 //Sets our count variable back to 0
set q.finished[idx] = false
call QMReqAdd(p,q.title, "Quest is repeatable")
endif
call QMReqComplete(p,q.title, "Return to "+GetUnitName(q.startu)+" for your reward")
call QMComplete(p,q.title)
//Exp Reward
if (not(q.exp==0)) then
call AddHeroXP(u,q.exp, false)
call DisplayTextToPlayer(p,0,0,"Rewarded: "+expcolor+ I2S(q.exp) + " Experience.|r")
endif
//Gold Reward
if (not(q.gold==0)) then
call SetPlayerStateBJ( p, PLAYER_STATE_RESOURCE_GOLD, ( GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) + q.gold ) )
call DisplayTextToPlayer(p,0,0,"Rewarded: "+goldcolor+ I2S(q.gold) + " Gold.|r")
endif
//Lumber Reward
if (not(q.lumber==0)) then
call SetPlayerStateBJ( p, PLAYER_STATE_RESOURCE_LUMBER, ( GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER) + q.lumber ) )
call DisplayTextToPlayer(p,0,0,"Rewarded: "+lumbercolor+ I2S(q.lumber) + " Lumber|r" )
endif
//Item Reward
if (not(q.itemid==0)) then
call UnitAddItemById(u, q.itemid )
set i = CreateItem(q.itemid,0,0)
call DisplayTextToPlayer(p, 0,0, "Rewarded: "+itemcolor+GetItemName(i)+"|r")
call RemoveItem(i)
endif
set t = null
set u = null
set p = null
set i = null
endfunction
function CreateQuestParent takes integer StartNPC, real x, real y, real face, boolean Repeatable, integer minlevel, integer maxlevel, string QuestTitle, string QuestDescription, string Icon, integer HowMany, integer UnitToKillID, integer RewardItemId, integer RewardExp, integer RewardGold, integer RewardLumber returns Quest
local Quest q = Quest.create()
local trigger t = CreateTrigger()
local unit u
//Establishes our struct information
set q.trig = t
set q.startu=CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), StartNPC, x,y,face)
set q.start = StartNPC
set q.repeat = Repeatable
set q.title = QuestTitle
set q.desc = QuestDescription
set q.min = minlevel
set q.max = maxlevel
set q.howmany = HowMany
set q.countid = UnitToKillID
set q.itemid = RewardItemId
set q.exp = RewardExp
set q.gold = RewardGold
set q.lumber = RewardLumber
call AddSpecialEffectTarget("Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl", q.startu, "overhead")
//Create Quest in QuestLog
set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), UnitToKillID, 0,0,0)
call QMAdd(QuestTitle, QuestDescription , Icon, false, false)
call QMReqAdd(null,QuestTitle, "Kill "+I2S(HowMany)+" "+GetUnitName(u))
call RemoveUnit(u)
set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), StartNPC, 0,0,0)
call QMReqAdd(null,QuestTitle, "Return to "+GetUnitName(u)+" for your reward")
call RemoveUnit(u)
call QMReqAdd(null,QuestTitle, "Quest is repeatable")
//*****************START (CLICK) EVENT************************//
call SetHandleInt(t, "quest", q)
call CreateQuestEvent(t) //Create Trigger's Events
call TriggerAddCondition( t, Condition( function quest_click_conditions_setup ) )//Create Trigger's Conditions
call TriggerAddAction( t, function quest_click_actions_setup ) //Create Trigger's Actions
//*****************FINISH (REWARD) EVENT************************//
set t = CreateTrigger()
call SetHandleInt(t, "quest", q)
call TriggerRegisterUnitInRange( t, q.startu,200.00,null )
call TriggerAddCondition( t, Condition( function quest_reward_conditions ) )
call TriggerAddAction( t, function quest_reward_actions )
return q
endfunction
function CreateQuestKill takes integer StartNPC, real x, real y, boolean Repeatable, integer minlevel, integer maxlevel, string QuestTitle, string QuestDescription, string Icon, integer UnitToKillID,integer HowMany, integer RewardItemId, integer RewardExp, integer RewardGold, integer RewardLumber returns Quest
local trigger t = CreateTrigger()
local Quest q = CreateQuestParent(StartNPC, x, y,0,Repeatable, minlevel, maxlevel, QuestTitle, QuestDescription, Icon, HowMany, UnitToKillID, RewardItemId, RewardExp, RewardGold, RewardLumber)
set q.questtype = "kill"
//*****************KILL EVENT************************//
call SetHandleInt(t, "quest", q) //Store the struct in the trigger for reference later
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition( t, Condition( function quest_conditions_kill ) )
call TriggerAddAction( t, function quest_actions_kill )
set t = null
return q
endfunction
function testabc takes nothing returns nothing
call CreateQuestKill(039;Hamg039;, -357, 65,true, 2, 8, "Assault!", "The Tuskarr have been roaming around recently...They are making my evocation rather difficult. Kill 3 Tuskarr Chieftain and end their disruption", "ReplaceableTextures\\CommandButtons\\BTNTuskaarBlack.blp", 039;n000039;,3, 039;afac039;, 50, 10, 5)
endfunction
endlibrary
Quest Management Library
JASS:
library QuestManagement
//Set Ques Finish
function print takes player p, string str returns nothing
call DisplayTextToPlayer(p,0,0, str)
endfunction
function GlobalObject takes string ref, handle obj returns nothing
call StoreInteger(CSCache(),"globals",ref,H2I(obj))
endfunction
function GetAttachedReq takes handle h, string label returns questitem
return GetAttachedInt(h, label)
return null
endfunction
function QMAdd takes string title, string desc, string icon, boolean required, boolean discovered returns nothing
local quest q = CreateQuest()
call QuestSetTitle(q, title)
call QuestSetDescription(q, desc)
call QuestSetIconPath(q, icon)
call GlobalObject(title, q)
call QuestSetRequired(q, required)
call QuestSetDiscovered(q, discovered)
if(discovered) then
call FlashQuestDialogButton()
endif
endfunction
function GQ takes string label returns quest
return GetStoredInteger(CSCache(), "globals", label)
return null
endfunction
function QMDiscover takes player p, string title returns nothing
local quest q = GQ(title)
if(q == null) then
call print(p,"Quest Manager Error: Quest " + title + " does not exist.")
return
endif
call QuestSetDiscovered(q, true)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUEST, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUEST, "|cffc0c0c0Quest Discovered - \"|cff00aadd" + title + "|cffc0c0c0\"")
call StartSound(bj_questDiscoveredSound)
call FlashQuestDialogButton()
endfunction
function QMComplete takes player p, string title returns nothing
local quest q = GQ(title)
if(q == null) then
call print(p, "Quest Manager Error: Quest " + title + " does not exist.")
return
endif
call QuestSetCompleted(q, true)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTDONE, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTDONE, "|cffc0c0c0Quest Complete - \"|cff00aadd" + title + "|cffc0c0c0\"")
call StartSound(bj_questCompletedSound)
call FlashQuestDialogButton()
endfunction
function QMFail takes player p, string title returns nothing
local quest q = GQ(title)
if(q == null) then
call print(p, "Quest Manager Error: Quest " + title + " does not exist.")
return
endif
call QuestSetFailed(q, true)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTFAILED, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTFAILED, "|cffc0c0c0Quest Failed - \"|cff00aadd" + title + "|cffc0c0c0\"")
call StartSound(bj_questFailedSound)
call FlashQuestDialogButton()
endfunction
function QMIsFailed takes player p, string title returns boolean
local quest q = GQ(title)
if(q == null) then
call print(p, "Quest Manager Error: Quest " + title + " does not exist.")
return false
endif
return IsQuestFailed(q)
endfunction
function QMIsComplete takes player p, string title returns boolean
local quest q = GQ(title)
if(q == null) then
call print(p, "Quest Manager Error: Quest " + title + " does not exist.")
return false
endif
return IsQuestCompleted(q)
endfunction
function QMExists takes string title returns boolean
local quest q = GQ(title)
return q != null
endfunction
function QMReqAdd takes player p, string title, string req returns nothing
local quest q = GQ(title)
local questitem qi
if(q == null) then
call print(p,"Quest Manager Error: Quest " + title + " does not exist.")
return
endif
set qi = QuestCreateItem(q)
call QuestItemSetDescription(qi, req)
call AttachObject(q, req, qi)
if(IsQuestDiscovered(q)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTUPDATE, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTUPDATE, "|cffc0c0c0New Quest Requirement for \"|cff00aadd" + title + "|cffc0c0c0\" - |cff00aadd" + req)
call StartSound(bj_questUpdatedSound)
call FlashQuestDialogButton()
endif
endfunction
function QMReqComplete takes player p, string title, string req returns nothing
local quest q = GQ(title)
local questitem qi
if(q == null) then
call print(p,"Quest Manager Error: Quest " + title + " does not exist.")
return
endif
set qi = GetAttachedReq(q, req)
if(qi == null) then
call print(p,"Quest Manager Error: Questitem " + req + " does not exist.")
return
endif
call QuestItemSetCompleted(qi, true)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTUPDATE, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTUPDATE, "|cffc0c0c0Quest Requirement Complete For \"|cff00aadd" + title + "|cffc0c0c0\" - |cff00aadd" + req)
call StartSound(bj_questUpdatedSound)
call FlashQuestDialogButton()
endfunction
function QMIsReqComplete takes player p, string title, string req returns boolean
local quest q = GQ(title)
local questitem qi
if(q == null) then
call print(p, "Quest Manager Error: Quest " + title + " does not exist.")
return false
endif
set qi = GetAttachedReq(q, req)
if(qi == null) then
call print(p, "Quest Manager Error: Questitem " + req + " does not exist.")
return false
endif
return IsQuestItemCompleted(qi)
endfunction
function QMReqCount takes player p, string title, string req, integer a, integer b returns nothing
local quest q = GQ(title)
local questitem qi
local string newText = req + " (" + I2S(a) + "/" + I2S(b) + ")"
if(q == null) then
call print(p,"Quest Manager Error: Quest " + title + " does not exist.")
return
endif
set qi = GetAttachedReq(q, req)
if(qi == null) then
call print(p, "Quest Manager Error: Questitem " + req + " does not exist.")
return
endif
call QuestItemSetDescription(qi, newText)
if(IsQuestDiscovered(q)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTUPDATE, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, bj_TEXT_DELAY_QUESTUPDATE, "|cffc0c0c0Quest Update - \"|cff00aadd" + title + "|cffc0c0c0\" - |cff00aadd" + newText)
call StartSound(bj_questUpdatedSound)
call FlashQuestDialogButton()
endif
set newText = null
set q = null
set qi = null
endfunction
endlibrary