• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

JPAG - JASS Proper Application Guide

I don't think anyone checked my post since last I updated it. I am largely on the verge of just leaving global convention up to the user. Berb uses something like data_ or priv_ as prefixes, though I think this might be overkill even to require a prefix at all. As long as it's not easy to get mixed up with something it's not (not getting mixed up with a constant, or a function, etc.) I should think this is fine.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
@Shadow-Flare: Well if you write GlobalsLikeThis, on one glimpse, it's harder to distinguish whether it's a local or global +error-prone. Structures of the same naming as suggested here scopes, libaries, structures, modules, functions are placed in different locations or have other additions that makes them easy to identify such as parenthesis or library_member or struct.member. Also you might go in conflict with the names of these other things and are unable to locally address an instance of object type "Box" for example just the same "Box". I do not practise this much either but what default names do you give your variables that you do not have a special one in mind for?

@Bribe: Your link has double http but I read it now. How can a getter have the same name as your variable? When you prefix getters with the usual "Get", that's an imperative and does not belong in var name. Maybe for flag states "Is" but that can also be easily left out in var names.
 
It depends on the type of data structure, to be honest I don't always
use the same names for everything.

If we have something like "j_" prefix, that's going to be impossible to
miss as a global compared to a local that won't have it. And members
inside of a struct do not require a prefix because they have the dot.

I generally don't recommend writing the struct variable name without
a dot as it is, but I am not in a position to make that a rule because
not needing a dot is part of what makes vJass look good.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
The not needing of a dot depends how you have configured jasshelper.conf

//To disable implicit usage of members and methods, (not requiring this. ) add:
// a line containing: [noimplicitthis]

If you add this feature are you still allowed to do that ? :

JASS:
struct ...

    integer data

    method m takes integer data ...
        set this.data = data

If yes, i suppose it's fine.

Also why "j_", "g_" makes sense because it leads to "global".

Btw your link in your post is invalid and the main thread still says "use camelCase for variables" and "nothing more" (i mean it's still the same, you never say it's up to the user).
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I have not updated the thread yet because we are still discussion possibilities for globals.

If you are waiting for a consensus, you will never reach it.

The most "jass way" is "g_", but then you will have many people "wtf an other ugly prefix when we had already got rid of udg_", and the first will be myself.

Same for CamelCase or Camel_case or your Struct.way, some people will love one syntax, hate an other one, ...

For CAMEL_CASE you will have even more complains, i'm pretty sure of that.

And so far i can't in all objectivity imagine an other syntax which is not weird nor senseless.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Not sure if it should be said here or not at all, but i remember something about GUI globals "default value".
If i remember correctly, the "default value" given in the GUI variable editor is always done in a custom function called by the function main (InitGlobals or something like that).
While it makes sense for arrays, it doesn't make sense for the others.
And again if i remember correctly, all vJass initializers are done before InitGlobals, which means that it won't work as intended if you use a GUI variable in a vJass initializer.

First, the "default value" won't be already setted when you will use this variable in the vJass initializer.
Secondly, if you give a value to it in a vJass initializer it will be overwritten later by InitGlobals.

I don't really know why Vexorian did it like that (to annoy GUI users ?!), InitGlobals should be done first before vJass initializers, it's up to the user to avoid the limit op with the global variables definitions anyway.

Ofc it's not an issue if you don't use the GUI variables created by the variable editor or vJass initializers, only if you use both together.

At least i'm pretty sure it was like that before, i haven't checked with the latest jasshelper.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
I noticed a few things reading the OP.
The first is that you say you want to follow the style given by Blizzard.
Let's have a look at the first few lines of common.j:
JASS:
type agent extends handle
type event extends agent
//[…]
type unit extends widget

So for me it makes sense that i give my structs, which are new types, lowercase names. For most structs it looks nice.


My next question: how to deal with keys? Follow the manual and give 'em all uppercase names? Would make sense imo, since they're just constant integer.


As with keys, there is no guidline for interfaces and function interfaces. How come that two of the most highlevel constructs in vJass are not mentioned? I think interfaces should follow the struct convention whatever that is. And function interfaces should follow the function convention.
 
<P>Interface, key and function interface were not listed because I never use them so I didn't think to add them. Simple as that.</P><P>&nbsp;</P><P>Keys were a good idea but the execution was bad. One of the ideas in LuckyParser, which was derived from cJass, was to use definitions instead of keys because they are more dynamic.</P><P>&nbsp;</P><P>Function interfaces and interfaces in general were also good ideas but their compiled structure is aweful: mainly in that it duplicates the method with 2-3 copies, so longer methods just become giant.</P><P>&nbsp;</P><P>I see your point in that native types are lowercased, however native types do not have OOP syntax like structs do.</P>
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
<P>Interface, key and function interface were not listed because I never use them so I didn't think to add them. Simple as that.</P><P>&nbsp;</P><P>Keys were a good idea but the execution was bad. One of the ideas in LuckyParser, which was derived from cJass, was to use definitions instead of keys because they are more dynamic.</P><P>&nbsp;</P><P>Function interfaces and interfaces in general were also good ideas but their compiled structure is aweful: mainly in that it duplicates the method with 2-3 copies, so longer methods just become giant.</P><P>&nbsp;</P><P>I see your point in that native types are lowercased, however native types do not have OOP syntax like structs do.</P>

Are the guidelines now aiming to match LuckyParser/not vJass? I do not use e.g. functions either but see others doing so. Same with interfaces.
 
Well WaterKnight I haven't added those to the tutorial, doesn't mean I won't write up the guidelines for them. I have just not made the update yet for one reason or another. Seeing as how this is still pending, I can make updating the thread and its arguments a lesser priority because there are so many other things I am trying to work on.
 
Well WaterKnight I haven't added those to the tutorial, doesn't mean I won't write up the guidelines for them. I have just not made the update yet for one reason or another. Seeing as how this is still pending, I can make updating the thread and its arguments a lesser priority because there are so many other things I am trying to work on.

So I assume this will still be updated, right?

If it will be, then I'll leave it here until you have the time to make updates. The guidelines seem pretty good to me, and no one has seemed to strongly oppose them so far.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Just a little thing :

Bribe said:
variableNames and methodNames should begin with a
lowercase letter and each new word seperated by a new capitalized
letter. This is called camelCasing. variableNames, this was designed
by Blizzard. methodNames are typically formatted like this in most
mainstream languages like C and are also formatted in such a way
with most of vJass resources, it makes sense that this should also
be the standard.

Like C++ probably, but definetely not in C, since there are no methods, neither POO concepts.
Structs in C are "just" a special data storage, not a class.

Pretty good in general, it only lacks about usual conventions to make a difference between a local and a global variable.
GUI use the prefix udg_, i personnaly use This_is_a_global, this_is_a_local, but i know you hate it.
 
It seriously doesn't matter what format people use as long as it's consistent through their own resources, I see a lot of people using the same format for normal as well as constant variables which is pretty lame. I should probably just add that to this resource that if you don't like this way of doing it at least make sure that your own way of doing it makes a difference between "this and that". After all it's been proven that this should just be a formality in case people (like myself) need to defer to some standard whatever standard that may be.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
It prioritizes the globals? That doesn't even make sense why they would do that. Epic fail right?

It could refuse to compile, printing out a script error, or maybe prioritize the locals would make more sense, but they choiced this way.
The variable shadowing was fixed because we could typecast without a function call, just with variables.

We all know how blizzard choices how to fix jass bugs, it's most of time for the worst, not the best.
 
Well common.AI has a native "StartThread" (which sadly is not one of the natives that actually works), though I suppose "ExecuteFunc" could still be used though it doesn't work so well with needing string concatenation and all that.

Code compression meaning that the JASS verbosity really takes up a LOT of map file size. The compression could be better.
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
JASS:
struct Code
    static hashtable TABLE

    trigger action

    static method GetFromSelf takes code c returns thistype
        local integer id = GetHandleId(Condition(c))

        local thistype this = LoadInteger(thistype.TABLE, id, 0)

        if (this == 0) then
            set this = thistype.allocate()

            set this.action = CreateTrigger()
            call SaveInteger(thistype.TABLE, id, 0, this)

            call TriggerAddCondition(this.action, Condition(c))
        endif

        return this
    endmethod

    static method Run takes nothing returns nothing
        call TriggerEvaluate(this.action)
    endmethod

    static method onInit takes nothing returns nothing
        set thistype.TABLE = InitHashtable()
    endmethod
endstruct
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Well common.AI has a native "StartThread" (which sadly is not one of the natives that actually works), though I suppose "ExecuteFunc" could still be used though it doesn't work so well with needing string concatenation and all that.

I don't know a thing in ai but i suppose, at least i hope, that StartThread works as intented in an ai script (but maybe the name is senseless comparing to what it does).
Btw as you know string concatenation is a vJass problem, not a jass one.
I mean it's unfair to bitching about ExecuteFunc, it even doesn't crash anymore with an invalid string argument, not to mention that is also the slowest way to execute a code.
Now, i'm not a fan of using strings for functions, since it's error prone and is difficult to maintain, one of the many reason why i hate galaxy (from sc2).

Code compression meaning that the JASS verbosity really takes up a LOT of map file size. The compression could be better.

Jass without verbosity wouldn't be jass, yes it's annoying but it's also what it's making so easy to learn and use.

Plus i don't think that in a real map the script size really does matter.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Well StartThread takes a "code" argument and you know what that means.

common.ai natives are not meant to be used in a map script, only in an ai script.

Code size in a real map is quite important, a map with a couple dozen GUI spells
converted to optimized JASS can cut out 50-100KB of data.

A script optimizer seems enough, and again comparing to custom models, skins, sounds, and whatever else, it's just negligible most of time.
What i mean, is that reducing the jass verbosity is clearly not in my blizzard christmas gift wishes.
We can have jass preprocessors for that.
Fixing known bugs and implement all the NYI features, plus eventually add some new natives are definetely in my list.

Too bad i don't believe in miracles.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I dunno how to even use an AI script. Although if those natives work inside of one I think I would use AI scripts to code everything from now on.

There is 99.9 % of chance that it is a joke but meh :

I don't think so, you are even more limited than inside a map script (at least some common.j natives don't work inside it).
Sure there are some cools functions, but most of them can be reproduced in some way or in an other in a map script.

Plus, for the moment the only way to check if you haven't made any errors (typos or whatever) is to test your ai script ...
Or maybe jasscraft or such tool can do the checks for you, i dunno.

In fact if i have to make an ai script, i would use "regular" jass ...

Here is a random demo ai script that i've stolen from someone, as you can se an ai script is thread based, i don't think you can use the event approach (except probably some directly related to an ai such as your base is attacked), nor timers.

JASS:
//===========================================================================
// ElfesH 
//===========================================================================

globals 
    integer                 attackWave                 = 1 
    integer                 nextDelay                  = 0 
    integer                 awGold                     = 0 
    integer                 awWood                     = 0 
    string                  name                       = "computerElf" 
    unit                    u_heros                    = null 
endglobals 

//===========================================================================
function ReturnPlayerString takes integer i returns string 
  if i == 1 then 
  return "1" 
  elseif i == 2 then 
  return "2" 
  elseif i == 3 then 
  return "3" 
  elseif i == 4 then 
  return "4" 
  elseif i == 5 then 
  return "5" 
  elseif i == 6 then 
  return "6" 
  elseif i == 7 then 
  return "7" 
  elseif i == 8 then 
  return "8" 
  elseif i == 9 then 
  return "9" 
  elseif i == 10 then 
  return "10" 
  elseif i == 11 then 
  return "11" 
  elseif i == 12 then 
  return "12" 
  endif 
endfunction 

//===========================================================================
function InitOptions takes nothing returns nothing 
    call SetCampaignAI(  ) 
    call SetDefendPlayer( false ) 
    call SetRandomPaths( false ) 
    call SetTargetHeroes( true ) 
    call SetPeonsRepair( false ) 
    call SetHeroesFlee( true ) 
    call SetHeroesBuyItems( true ) 
    call SetUnitsFlee( true ) 
    call SetGroupsFlee( false ) 
    call SetWatchMegaTargets( false ) 
    call SetIgnoreInjured( false ) 
    call SetHeroesTakeItems( true ) 
    call SetSlowChopping( false ) 
    call SetCaptainChanges( false ) 
    call SetSmartArtillery( true ) 
endfunction 

//*************************************************************************** 
//* 
//*  Heroes 
//* 
//*************************************************************************** 

//===========================================================================
// Stores hero ID and skills 
//===========================================================================
function SetHero takes integer order, integer heroid returns nothing 
    if (order == 1) then 
        set hero_id = heroid 
        if (heroid == 'Edem') then 
            set skills1[ 1] = 'AEmb' 
            set skills1[ 2] = 'AEim' 
            set skills1[ 3] = 'AEev' 
            set skills1[ 4] = 'AEmb' 
            set skills1[ 5] = 'AEim' 
            set skills1[ 6] = 'AEme' 
            set skills1[ 7] = 'AEev' 
            set skills1[ 8] = 'AEmb' 
            set skills1[ 9] = 'AEim' 
            set skills1[10] = 'AEev' 
        endif 
    endif 
endfunction 

//===========================================================================
// Selects hero IDs for three possible heroes 
//===========================================================================
function SelectHeroes takes nothing returns nothing 
    local integer roll = GetRandomInt(1,100) 
    call SetHero( 1, 'Edem' ) 
endfunction 

//===========================================================================
// Returns the hero skill for the given hero and level 
//===========================================================================
function ChooseHeroSkill takes nothing returns integer 
    local integer curHero = GetHeroId() 
    local integer level = GetHeroLevelAI() 
    
    if (level > max_hero_level) then 
        set max_hero_level = level 
    endif 
    
    if (curHero == hero_id) then 
        return skills1[level] 
    elseif (curHero == hero_id2) then 
        return skills2[level] 
    elseif (curHero == hero_id3) then 
        return skills3[level] 
    endif 
    return 0 
endfunction 

//*************************************************************************** 
//* 
//*  Building and Harvesting 
//* 
//*************************************************************************** 

//===========================================================================
// Specifies building priorities for workers 
//===========================================================================

function BuildPriorities takes nothing returns nothing 
    call SetBuildAll( BUILD_UNIT, 1, hero_id, -1 )    
endfunction 

//===========================================================================
// Specifies harvesting priorities for workers 
//===========================================================================
function HarvestPriorities takes nothing returns nothing 
    local integer mine = TownWithMine() 
    local integer allGold = GetUnitCountDone('opeo') 
    local integer allWood = GetUnitCountDone('opeo') 
    local integer numWorkers 
    set numWorkers = 5 
    call HarvestGold( 0, numWorkers ) 
    set numWorkers = 5 
    call HarvestWood( 0, numWorkers ) 
    set numWorkers = 5 
    call HarvestGold( 1, numWorkers ) 
    set numWorkers = 4 
    call HarvestWood( 0, numWorkers ) 
endfunction 

//===========================================================================
// Determines all building and harvesting assignments for workers 
//===========================================================================
function WorkerAssignment takes nothing returns nothing 
    loop 
        
        // Harvesting 
        call ClearHarvestAI(  ) 
        call HarvestPriorities(  ) 
        
        // Building 
        call InitBuildArray(  ) 
        call BuildPriorities(  ) 
        
        call Sleep( 2 ) 
    endloop 
endfunction 

//=================================== 
function ReturnNeutralUnitById takes integer unitID returns unit 
    local group g = CreateGroup() 
   local unit u = null 
    
   call GroupEnumUnitsOfPlayer(g,Player(PLAYER_NEUTRAL_PASSIVE),null)    
    loop 
      set u = FirstOfGroup(g) 
      exitwhen u == null 
      if GetUnitTypeId(u) == unitID then 
         return u 
      endif 
      call GroupRemoveUnit(g, u) 
   endloop 
   return null 
endfunction 
//=================================== 
//==============UNITS================ 
//=================================== 
function ReturnUnitById takes integer unitID returns unit 
    local group g = CreateGroup() 
   local unit u = null 
    
   call GroupEnumUnitsOfPlayer(g,ai_player,null)    
    loop 
      set u = FirstOfGroup(g) 
      exitwhen u == null 
      if GetUnitTypeId(u) == unitID then 
         return u 
      endif 
      call GroupRemoveUnit(g, u) 
   endloop 
   return null 
endfunction 
//==================================== 
function CreateAttackGroup takes integer unitID returns group 
    local integer all 
    local integer i 
    local unit u 
    local group attack 

        set i = 0 
        set all = GetUnitCountDone(unitID) 
        loop 
        set u = ReturnUnitById(unitID) 
        if (not IsUnitInGroup(u,attack)) then 
        call GroupAddUnit(attack,u) 
        set i = i + 1 
        endif 
        exitwhen (i == all) 
        endloop 
        
        return attack 

endfunction 
//=================================== 
//==============HEROS================ 
//=================================== 
function CreateHeros takes nothing returns unit 
   local group g = CreateGroup() 
   local unit u = null 
    
   call GroupEnumUnitsOfPlayer(g,ai_player,null)    
   loop 
      set u = FirstOfGroup(g) 
      exitwhen u == null 
      if GetUnitTypeId(u) == hero_id then 
         return u 
      endif 
      call GroupRemoveUnit(g, u) 
   endloop 
   return null 
endfunction 
//=================================== 
function WaitHeros takes nothing returns nothing 
    loop 
      set u_heros = CreateHeros() 
      call Sleep(2) 
      exitwhen (u_heros != null) 
    endloop 
endfunction 
//==================================== 
function LGetHerosPercentLife takes nothing returns real 
    local real value = GetUnitState(u_heros,ConvertUnitState(0)) 
    local real maxValue = GetUnitState(u_heros,ConvertUnitState(1)) 
    return value / maxValue*100 
endfunction 
//==================================== 
function LifeNeeded takes nothing returns boolean 
    if (LGetHerosPercentLife() < 25) then 
     return true 
    elseif (LGetHerosPercentLife() > 95) then 
     return false 
    endif      
endfunction 

//=================================================================== 
function A takes nothing returns nothing 
    local group attackgroup = CreateGroup() 
    local unit heros 
    local unit fontaine = ReturnNeutralUnitById('nfoh') 
    local integer random 
    local real X 
    local real Y = 877.0 
    call WaitHeros() 
  loop    
    if (LGetHerosPercentLife() < 25 and not IsUnitInRange(u_heros,fontaine,250)) then 
       call IssuePointOrder(u_heros,"move",GetUnitX(fontaine),GetUnitY(fontaine)) 
       loop 
       exitwhen IsUnitInRange(u_heros,fontaine,250)==true 
       call Sleep(0.5) 
       endloop 
       loop        
       exitwhen LGetHerosPercentLife() == 100.0 
        if IsUnitInRange(u_heros,fontaine,250) == true then 
       call IssueImmediateOrder(u_heros,"holdposition") 
        else 
       call IssuePointOrder(u_heros,"move",GetUnitX(fontaine),GetUnitY(fontaine)) 
        endif 
       call Sleep(0.5) 
       endloop 
       set random = GetRandomInt(1,3) 
        if random == 1 then 
         set X = -672.0 
        elseif random == 2 then 
         set X = 439.0 
        elseif random == 3 then 
         set X = -567.0 
        endif 
        call IssuePointOrder(u_heros,"attack",X,Y) 
    endif 
    if (LGetHerosPercentLife() > 25) then 
      if (GetUnitCurrentOrder(u_heros) != OrderId("attack")) then 
       call IssuePointOrder(u_heros,"attack",-672.0,Y) 
      endif 
    endif 
       call Sleep(0.5) 
  endloop 
     endfunction 
//===========================================================================
// main 
//===========================================================================
function main takes nothing returns nothing 
    call InitAI(  ) 
    call SetPlayerName( ai_player,(name + "[" + ReturnPlayerString(GetAiPlayer()) + "]") ) 
    call InitOptions(  ) 
    call SelectHeroes(  ) 
    call SetHeroLevels( function ChooseHeroSkill ) 
    
    call Sleep( 0.1 ) 
    call StartThread(function WorkerAssignment) 
    call StartThread(function A) 
    call PlayGame(  ) 
endfunction
 
Last edited:
JASS:
function ReturnPlayerString takes integer i returns string 
  if i == 1 then 
  return "1" 
  elseif i == 2 then 
  return "2" 
  elseif i == 3 then 
  return "3" 
  elseif i == 4 then 
  return "4" 
  elseif i == 5 then 
  return "5" 
  elseif i == 6 then 
  return "6" 
  elseif i == 7 then 
  return "7" 
  elseif i == 8 then 
  return "8" 
  elseif i == 9 then 
  return "9" 
  elseif i == 10 then 
  return "10" 
  elseif i == 11 then 
  return "11" 
  elseif i == 12 then 
  return "12" 
  endif 
endfunction

HAHAHAHAHAHHAHHAHAHA

How funny :>
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I vaguely remember that the author said that I2S don't work in an ai script (i have no clue
if it's true or not).
I've suggested a better algorithm (see how i'm smart), but he probably didn't cared :p

Btw, i'm not off-topic, right ?
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Btw i've read on w3c.net that most of common.j natives work in an ai script, so i've edited my previous post.
But seriously if you want serious infos about ai scripts you should check about some tutorials, and/or experienced makers of ai, and not just assume my thoughts, as i know nothing about ai scripts.
 
Top