• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!

QuestUtils v1.4

QuestUtils v1.4

Just a Quest System that makes life easy...

JASS:
/*
===QuestUtils v1.4
===By Mckill2009

This is an alternative to GUI Quest creation, very easy to use if you follow the instructions
written in this sample map...

API:
    struct QuestUtils extends array
        static method create takes boolean required, boolean discovered returns thistype
        method operator []= takes string title, string description returns nothing
        method operator questIcon= takes string iconPath returns nothing
        method operator completed= takes boolean b returns nothing
        method operator failed= takes boolean b returns nothing
        method operator enable= takes boolean b returns nothing
        method operator discover= takes boolean b returns nothing
        method operator completed takes nothing returns boolean
        method operator discovered takes nothing returns boolean
        method operator enabled takes nothing returns boolean
        method operator required takes nothing returns boolean
        method destroy takes nothing returns nothing
        
    struct QuestItem extends array
        static method create takes QuestUtils index, string text returns thistype
        method operator text= takes string text returns nothing
        method operator completed= takes boolean b returns nothing
        method operator completed takes nothing returns boolean
        method destroy takes nothing returns nothing
*/

library QuestUtils uses optional QuestSounds

globals
    private constant integer QUEST_LIMIT = 100
endglobals

struct QuestUtils extends array
    quest q
    
    private static thistype instance = 0
    
    static method create takes boolean required returns thistype
        local thistype this
        if instance==QUEST_LIMIT then
            debug call BJDebugMsg("QuestUtils ERROR: Maximum quest is: "+I2S(QUEST_LIMIT))        
        else
            set instance = instance + 1
            set this = instance
            set .q = CreateQuest()
            call QuestSetRequired(.q, required)   
        endif
        return this
    endmethod
    
    method operator []= takes string title, string description returns nothing
        call QuestSetTitle(.q, title)
        call QuestSetDescription(.q, description)
        call FlashQuestDialogButton()  
    endmethod
    
    method operator questIcon= takes string iconPath returns nothing
        call QuestSetIconPath(.q, iconPath) 
    endmethod  
    
    method operator completed= takes boolean b returns nothing
        call QuestSetCompleted(.q, b)            
    endmethod
    
    method operator failed= takes boolean b returns nothing
        call QuestSetFailed(.q, b)
    endmethod
    
    method operator enable= takes boolean b returns nothing
        call QuestSetEnabled(.q, b)
    endmethod
    
    method operator discover= takes boolean b returns nothing
        call QuestSetDiscovered(.q, b)
    endmethod
    
    method operator completed takes nothing returns boolean
        return IsQuestCompleted(.q)
    endmethod
    
    method operator discovered takes nothing returns boolean
        return IsQuestDiscovered(.q)
    endmethod
    
    method operator enabled takes nothing returns boolean
        return IsQuestEnabled(.q)
    endmethod
    
    method operator required takes nothing returns boolean
        return IsQuestRequired(.q)
    endmethod
    
    method destroy takes nothing returns nothing
        call DestroyQuest(.q)
        set .q = null
        set .q = instance.q  
        set instance = instance - 1
    endmethod
endstruct

struct QuestItem extends array
    questitem qi
    
    private static thistype instance = 0
    
    static method create takes QuestUtils index, string text returns thistype
        local thistype this
        set instance = instance + 1
        set this = instance
        set .qi = QuestCreateItem(index.q)
        call QuestItemSetDescription(.qi, text)
        call FlashQuestDialogButton()     
        return this
    endmethod  
    
    method operator text= takes string text returns nothing
        call QuestItemSetDescription(.qi, text)
        call FlashQuestDialogButton()
    endmethod
    
    method operator completed= takes boolean b returns nothing
        call QuestItemSetCompleted(.qi, b)
        call FlashQuestDialogButton()
    endmethod
    
    method operator completed takes nothing returns boolean
        return IsQuestItemCompleted(.qi)
    endmethod
    
    method destroy takes nothing returns nothing
        set .qi = null
        set .qi = instance.qi   
        set instance = instance - 1
    endmethod
endstruct

endlibrary



JASS:
/*
===QuestSounds v1.0
===By Mckill2009

Sounds should be in Mp3 format
*/

library QuestSounds

//sample: call PlayCompletedSound()
//! textmacro Sound takes Func, Snd
function Play$Func$Sound takes nothing returns nothing
    call StartSound(bj_quest$Snd$Sound)
endfunction
//! endtextmacro

//! runtextmacro Sound("Completed","Completed")
//! runtextmacro Sound("Discovered","Discovered")
//! runtextmacro Sound("Failed","Failed")
//! runtextmacro Sound("Hint","Hint")
//! runtextmacro Sound("ItemAcquired","ItemAcquired")
//! runtextmacro Sound("Secret","Secret")
//! runtextmacro Sound("Updated","Updated")
//! runtextmacro Sound("Warning","Warning")

//sample: call PlaySound(gg_snd_SOUNDFILENAME, false)
//if killWheDone is true, it will destroy the sound and clears the leak but
//if you use the sound again, then you should set it to false
function PlayCustomSound takes sound soundHandle, boolean killWhenDone returns nothing
    call StartSound(soundHandle)
    if killWhenDone then
        call KillSoundWhenDone(soundHandle)
    endif
endfunction

endlibrary



I'm bad at tutorials, so if you wanna add something, Ima credit you!

JASS:
scope QuestUtilsDEMO

struct QuestUtilsDEMO extends array
    
    //Globals are recommended but in this example I just use locals
    static method run takes nothing returns nothing
        local QuestUtils Q
        local QuestUtils Q1
        local QuestItem q1
        local QuestItem q2
        local QuestItem q3
        local QuestItem q4     
        set Q = Q.create(true)
        
        set Q1 = Q1.create(false)
        
        call BJDebugMsg(I2S(Q)+" Quest Created")
        call BJDebugMsg(I2S(Q1)+" Quest Created")
        
        set Q["QUEST TITLE HERE"] = "QUEST DESCRIPTION HERE"
        set Q.questIcon = "ReplaceableTextures\\CommandButtons\\BTNDivineIntervention.blp"
        
        set q1 = q1.create(Q, "I AM #1 QUEST ITEM")
        set q2 = q2.create(Q, "I AM #2 QUEST ITEM")
        set q3 = q3.create(Q, "I AM #3 QUEST ITEM")
        set q4 = q4.create(Q, "I AM #4 QUEST ITEM")
        
        set q1.text = "QUEST ITEM #1 HAS CHANGED TEXT"
        set q1.completed = true
        call q1.destroy() //questItem #1 will never be recalled
        set q1.text = "QUEST ITEM #1" //this cant be displayed anymore coz it's been destroyed
        set q4.text = "QUEST ITEM #4"
        
        set Q.enable = true
        if Q.enabled then
            call BJDebugMsg("QUEST ENABLED")
        else
            call BJDebugMsg("QUEST NOT ENABLED")
        endif
        
        set Q.completed = false
        if Q.completed then
            call BJDebugMsg("QUEST COMPLETED")
        else
            call BJDebugMsg("QUEST NOT COMPLETED")
        endif
        //call Q.destroy()
        //set Q.questIcon = "ReplaceableTextures\\CommandButtons\\BTNDivineIntervention.blp"
    endmethod
    
    static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(), 0, false, function thistype.run)
    endmethod
endstruct

endscope



  • QuestUtils DEMO GUI
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • -------- We will use a global variable for this demo created via global blocks so that we can access the code outside this trigger --------
      • -------- you may create global blocks above your map script (same as the sample) or via libraries or scopes --------
      • -------- ================== --------
      • -------- Set up the quest first, true==required, false==optional --------
      • Custom script: set sample1 = sample1.create(true)
      • -------- Inside this >> [] is the title and after this = is the description as written below --------
      • Custom script: set sample1["QUEST TITLE HERE"] = "QUEST DESCRIPTION HERE"
      • Custom script: set sample1.questIcon = "ReplaceableTextures\\CommandButtons\\BTNDivineIntervention.blp"
      • -------- Setting up the quest item, these natives are not available for GUI --------
      • -------- take note that the sample1 is your #1 indexed quest, so we will put our questItem to the #1 quest created, we already referenced it as: --------
      • -------- sample1 --------
      • Custom script: set qItem1 = qItem1.create(sample1, "I AM #1 QUEST ITEM")
      • Custom script: set qItem2 = qItem2.create(sample1, "I AM #2 QUEST ITEM")
      • Custom script: set qItem3 = qItem3.create(sample1, "I AM #3 QUEST ITEM")
      • -------- Now let's do these: --------
      • -------- Change the text to our qItem1 --------
      • -------- Destroy qItem2 --------
      • -------- Complete the qItem3 --------
      • Custom script: set qItem1.text = "QUEST ITEM #1 HAS CHANGED TEXT"
      • Custom script: call qItem2.destroy()
      • Custom script: set qItem3.completed = false
      • -------- Now let's use qtem3 outside this function --------
      • -------- say we will RUN the GUI RUN trigger --------
      • Game - Display to (All players) the text: We will run the GUI...
      • Game - Display to (All players) the text: The qItem3 is compl...
      • Wait 7.00 seconds
      • Trigger - Run GUI RUN <gen> (ignoring conditions)
  • GUI RUN
    • Events
    • Conditions
    • Actions
      • -------- See the advantages of globals in this system?, easy implementation! --------
      • -------- qItem3 will return true coz we already declared completed from the previous trigger --------
      • Custom script: if qItem3.completed then
      • Game - Display to (All players) the text: qItem3 is COMPLETED
      • Unit - Create 1 Blood Mage for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Custom script: else
      • Unit - Create 1 Paladin for Neutral Hostile at (Center of (Playable map area)) facing Default building facing degrees
      • Game - Display to (All players) the text: qItem3 is NOT COMPL...
      • Custom script: endif
      • -------- ================== --------
      • -------- if you want to complete the whole quest then --------
      • Custom script: set sample1.completed = true
      • -------- Or if you want the quest to be failed --------
      • Custom script: set sample1.failed = true




v1.4
- Name convention applied

v1.3
- Nullings applied

v1.2
- All methods replaced by operators
- Added quest limit

v1.1
- Most methods replaced by operators
- Quest Sounds created in separate snippet, as suggested by the moderator
- Most changes applied from suggestions by Magtheridon96 and Nesthaurus



- Nesthaurus and Magtheridon96 for his suggestions


Keywords:
quest, journey, mckill2009, diablo, starcraft, item, dota, utils, system
Contents

QuestUtils (Map)

Reviews
23:57, 29th Sep 2012 Magtheridon96: Approved. This is a good wrapper. Tips: - You never really need to null anything if you're going to overwrite it directly afterwards. (.q and .qi) - local thistype this set instance = instance + 1 set this =...

Moderator

M

Moderator

23:57, 29th Sep 2012
Magtheridon96: Approved. This is a good wrapper.

Tips:
- You never really need to null anything if you're going to overwrite it directly afterwards. (.q and .qi)
-
JASS:
local thistype this
set instance = instance + 1
set this = instance
->
JASS:
local thistype this = instance + 1
set instance = this

By the way, the only reason I stressed on convention is because this is solely a wrapper, and API is pretty much the only thing that can be assessed. Convention is always important especially in APIs, not because I want it to follow convention, but because everyone does. If everything follows different conventions, it's going to be really confusing for people to manage their maps and their code is going to look really strange. If everything is uniform, it's far easier.

I'm sorry I did not make all these conventions explicit, but we often take them for granted :/
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
QuestUtils is a very very bad name for that struct. Name it Quest instead... :\


With QuestUtils, that tells me that you are creating instances of Quest Utilities, which doesn't make a whole lot of sense. It appears that you are actually creating Quests, which is why the struct should be named Quest.

edit
JASS:
//! runtextmacro Sound("Completed","Completed")
//! runtextmacro Sound("Discovered","Discovered")
//! runtextmacro Sound("Failed","Failed")
//! runtextmacro Sound("Hint","Hint")
//! runtextmacro Sound("ItemAcquired","ItemAcquired")
//! runtextmacro Sound("Secret","Secret")
//! runtextmacro Sound("Updated","Updated")
//! runtextmacro Sound("Warning","Warning")

Those should probably be in a separate resource/snippet. The user may want to customize their own sounds, so you should not couple sounds with the quest objects. The quest objects should be very basic (what wc3 does) but in an OO and simpler to use format (as you have).

edit
other mehods that don't belong in this resource

JASS:
    static method hint takes string hintText, boolean forPlayer returns nothing
        if forPlayer then
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30,hintText)
        else
            call BJDebugMsg(hintText)
        endif
        call SoundHint()
    endmethod
    
    static method warning takes string warningText, boolean forPlayer returns nothing
        if forPlayer then
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30,warningText)
        else
            call BJDebugMsg(warningText)
        endif
        call SoundWarning()    
    endmethod

edit
It doesn't appear that you wrote struct instancing correctly :\. Right now, you can't destroy quests or quest items correctly, so you should probably fix your allocation/deallocation.

edit
This shouldn't be changing the text
JASS:
    method completed takes QuestUtils index, boolean changeText, string text returns nothing
        if changeText then
            call .setText(index, text)
        endif
        call QuestItemSetCompleted(.qi, true)
        call FlashQuestDialogButton()
        call SoundCompleted()
    endmethod

Also, move all of these methods to method operators. Instead of setText etc, do text=. The completed method should be changed to complete. Example -> call quest.complete(questItem). You should be using quest items rather than arbitrary quest indexes.

So API needs to be changed, allocation/deallocation needs to be fixed, and a few things need to be removed : ).
 
But if you don't null the variable, the handle will not be recycled until the variable is overwritten, so in a map with 100 quests initially, and then 2-5 quests at a time during the rest of the game, you would have about 95-98 handle leaks :<

Yes, this is an extreme case, but "set .qi = null" shouldn't hurt :<
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
Magtheridon96: It's a standard for corresponding method operators to match, so:
completed & completed=
failed & failed=

isCompleted and the rest should be changed to match their corresponding method operators to follow this unwritten, yet abided-by convention.
here we go again, what's wrong with you dude?, there's nothing wrong there!!!, you're writing BS again!, I'm not a robot to follow your s*** naming games!
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
method operators on the user end represent variables. If they have different names, then it's like you are make a variable a different named depending on what you are doing. It's extremely confusing in the API to do that. If you have 2 method operators that both operate on a variable (setter/getter), they must both be the same name. This is standard practice in c++, java, c#, etc. I don't know of a programming language where this isn't standard practice.

magtheridon96 is correct in demanding that your method operators have the same name when they operate on the same variable. By not doing that, you simply confuse the user as now the variable names are changing depending on what you are doing.

Let's say you have a variable named completed. However, when you read it, suddenly you have to use isComplete. So 1 variable with 2 names depending on what you are doing. No language does this with its regular variables, and you should not do this with method operators (variables with custom defined behavior).

vJASS itself also follows this standard. You have [] and []=.

If you don't follow this standard, you are not following vJASS convention.

If you refuse to do the above, the resource can simply be rejected for not following basic vJASS conventions inherent in the vJASS language.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
This is WC3 not c++ nor any major programming languages and standards for naming to follow those programs is NOT mentioned in the spell rules...
http://www.hiveworkshop.com/forums/faq/spellrules/#faq_spelllang

Perhaps it's time to change the rules before someone do a nasty rejection...

EDIT:
Also why he didnt mention it on the first place?, this is a short code, and he should see it...if I update this one, what's next?...remove the limit coz nobody will create more than 100 quest in a game?, that's BS...
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Well, the general conventions have been enforced even though they weren't in the rules, but they should be in the rules ;\.

for example, constants in all caps, functions as HelloWorld, methods as helloWorld, structs as HelloWorld, etc. All of this has been enforced, although those conventions aren't in the rules. GUI naming conventions have also been enforced.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
Put a link where it has been enforeced as a rule...it's not even in the 'Resource Submission Rules & Guidelines' General rules...

EDIT:
Besides a moderator can be wrong...although it doesnt hurt to change it but making suggestions one by one after the spell is updated is just like jerking around which is very very very bad for customers...

EDIT2:
Constants are NOT in caps for some of my systems, and many of those should be in constant but are not, but still they have been approved, why? coz they are not in the rules...

He may have the full decision to reject the spell or not, but I have the right to protest coz 'it's not in the rules'...
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
Magtheridon96 said:
edit
Awaiting suggested changes to follow convention.
what's next about this?...anyway I've already reported this and state my thoughts...for now Ima change it, but what's next?...

EDIT:
your last suggestion Ima follow in the next updates, sorry for my temper coz Im just quoting something that isnt in the rules but still you implemented it...
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
QuestUtils is still a terrible name for the struct.. lol

Example of my point
QuestUtils questUtils = QuestUtils.create(...)

so we're creating quest utilities now? Do those create quests or what? Or do we pass quests into them?

The struct name is extraordinarily confusing.

If it was all purely static, the struct name would be ok, but you're doing instancing there, so you're really creating quests, not quest utilities'.

edit
this isn't in the rules anywhere, but it's a followed principal among all experienced OO programmers in every language. Never create classes that have plural names, like class Lists, or class Utilities. You can do class List and class Utility, but not plural. The name should also reflect the class. QuestUtilities is both plural and it doesn't reflect the class, meaning that it breaks both of these principles : p.

Now, there are some special cases of this, like when the class specifically contains a set of item a's, then you can actually give it a plural name, that is As or something. Generally, this is done with the actual variables though, not the class itself.
 
Top