• 🏆 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!

[JASS] [Jass/Triger] 'skilladd

Status
Not open for further replies.
Level 13
Joined
Jul 26, 2008
Messages
1,009
I'm working on a map to compete with SotDRP, and have made heavy, heavy progress. I'm now in need of a much better 'skilladd system, as my own causes far too heavy of lag and has numerous issues.

This is the trigger I'm using:

JASS:
//This is where it actually adds the skill, levels it, etc.
function AddDefend takes nothing returns nothing
    if IsUnitSelected(GetEnumUnit(), udg_TEMP_Player) then 
        call UnitAddAbilityBJ( 'Adef', GetEnumUnit() )
    endif
endfunction
function RemoveDefend takes nothing returns nothing
    if IsUnitSelected(GetEnumUnit(), udg_TEMP_Player) then
        call UnitRemoveAbilityBJ( 'Adef', GetEnumUnit() )
    endif
endfunction
function LevelDefend takes nothing returns nothing
    if IsUnitSelected(GetEnumUnit(), udg_TEMP_Player) then 
        call IncUnitAbilityLevelSwapped( 'Adef', GetEnumUnit() )
    endif
endfunction
function DelevelDefend takes nothing returns nothing
    if IsUnitSelected(GetEnumUnit(), udg_TEMP_Player) then
        call DecUnitAbilityLevelSwapped( 'Adef', GetEnumUnit() )
    endif
endfunction
//This recognizes the string you entered and decides what desired result you want done.
function DoSkillAddHU takes integer id returns boolean

    local group grp

    local player pl
    local force fp
    local string str
    local string text

    local integer utype

    local integer i1
    local integer i2
    local integer array ratios



    set pl = ConvertedPlayer(id)

    if not(IsPlayerInForce(pl, udg_Force[2])) then
//********************RETURN
            return false //not a command
    endif

    set fp = bj_FORCE_PLAYER[id-1]
    set str = StringCase(GetEventPlayerChatString(), false)
    

   if (str == "adddefend" or str == "'defend") then
        set udg_TEMP_Player = pl
        set grp = GetUnitsInRectAll(GetPlayableMapRect())
        call ForGroupBJ( grp, function AddDefend )
        call DestroyGroup(grp)
        call DebugCommand(str,id)
    elseif (str == "@defend" or str == "removedefend") then
        set udg_TEMP_Player = pl
        set grp = GetUnitsInRectAll(GetPlayableMapRect())
        call ForGroupBJ( grp, function RemoveDefend )
        call DestroyGroup(grp)
        call DebugCommand(str,id)
    elseif (str == "+defend" or str == "lvldefend") then
        set udg_TEMP_Player = pl
        set grp = GetUnitsInRectAll(GetPlayableMapRect())
        call ForGroupBJ( grp, function LevelDefend )
        call DestroyGroup(grp)
        call DebugCommand(str,id)
    elseif (str == "=defend" or str == "deleveldefend") then
        set udg_TEMP_Player = pl
        set grp = GetUnitsInRectAll(GetPlayableMapRect())
        call ForGroupBJ( grp, function DelevelDefend )
        call DestroyGroup(grp)
        call DebugCommand(str,id)
        endif

//********************RETURN
    return true //is a command


function ChatEntered takes nothing returns nothing
    local integer id
    set id = GetConvertedPlayerId(GetTriggerPlayer())

    //call DisplayTextToForce( GetPlayersAll(), I2S(id) )
    call DoChatSystem(id)

endfunction

endfunction
 function DoChatSystem takes integer id returns nothing

    local group grp
    local player pl
    local force fp
    local integer chatnum
    local string text
    local string str
    local integer i
    set pl = ConvertedPlayer(id)
    set fp = bj_FORCE_PLAYER[id-1]
    set str = GetEventPlayerChatString()
    //There are several elseifs inbetween that utelize the local Xs and set Xs you see above.
    //Hides the string from DM chat
    elseif GetBooleanAnd(SubStringBJ(str,1,1) == "'" or SubStringBJ(str,1,1) == "+"or SubStringBJ(str,1,1) == "@"or SubStringBJ(str,1,1) == "=", IsPlayerInForce(pl, udg_Force[2])) then
            //Get rid of that pesky space people like to add
        if (SubStringBJ(str,2,2) == " ") then
            set text = SubStringBJ(str,3,StringLength(str))
        else
            set text = SubStringBJ(str,2,StringLength(str))
        endif
    if DoSkillAddHU(id) then
endif
I know it can be improved greatly. Right now it's over 8000 lines long alone, far too many for a playable map. It ties into a chat system, just as a fair warning.

I'll give you any additional info you need and talk to you one on one about it. You'll be heavily credited, and your name will be well known in the RP community.

As well, if you wish to jump on the project, which I'll link at the end here, I'd greatly appreciate it. I'm in need of one or two other systems as well.

http://www.hiveworkshop.com/forums/f256/vuens-d-d-te-huge-improvement-97224/
 
Last edited:

Rui

Rui

Level 41
Joined
Jan 7, 2005
Messages
7,550
Bad, bad, bad! I don't think you have a clue of what you're doing. If you know rawcodes, it'll be much easier.

JASS:
function t_NoCrash takes integer int returns boolean
 return int!='Arav'//'Arav' is the Storm Crow ability -- abilities such as 
//Crow Form,
//Bear Form and Metamorphosis (among others), will most likely 
//crash the game once used, so make sure you add them all here
//example: return int!='Arav' and int!='Abrf'
endfunction

function Trig_t_Actions takes nothing returns nothing
local group g=CreateGroup()
local integer i=S2I(SubString(GetEventPlayerChatString(),1,4))
local unit u
call GroupEnumUnitsSelected(g,GetTriggerPlayer(),null)
loop
 set u=FirstOfGroup(g)
 exitwhen u==null
  if t_NoCrash(i) then
   call UnitAddAbility(u,i)
  endif
 call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
set g=null
set u=null
endfunction
//===========================================================================
function InitTrig_t takes nothing returns nothing
 set gg_trg_t=CreateTrigger()
 call TriggerRegisterPlayerChatEvent(gg_trg_t,Player(0),"'",false)
 call TriggerAddAction(gg_trg_t,function Trig_t_Actions)
endfunction


EDIT: I was in a hurry, I'll tell you some things which I forgot. First of all it was not 1,5 but 1,4.
Also, you'll have to edit all the abilities in order to put the rawcodes in the ability names. Downside is, you have to add a few abilities to the condition I posted. Although you may always not reveal the rawcode of such abilities, people can easily go to the editor and find the rawcodes themselves.
Maybe there's some ability field you can refer to or ask for in JASS, that would be great as the anti-crash condition can be a little long, and long conditions are not something you want in a loop either.

I haven't tested if this works or not, do tell me, please.
~Thread Moved to Triggers & Scripts
 
Last edited:
Level 40
Joined
Dec 14, 2005
Messages
10,532
This is a case in which a hash table makes the most sense.

Uses vJass/JassNewGenPack/whatever you want to call it.

[jass=Our hash table]library GC initializer init
globals
public gamecache hash
endglobals
private function init takes nothing returns nothing
call FlushGameCache(InitGameCache("hash.w3v"))
set hash = InitGameCache("hash.w3v")
endfunction
endlibrary[/code]

[jass=Our trigger (should be in its own trigger category)]scope OnChat
private function Actions takes nothing returns nothing
local string s = //ability string
local unit u //u is the unit to add the spell to. In a loop, just have it be set constantly
if HaveStoredInteger(GC_hash,"SpellRawcodes",s) then
call UnitAddAbility(u,GetStoredInteger(GC_hash,"SpellRawcodes",s))
endif
//more stuff?
endfunction
public function InitTrig takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i > //MAX PLAYER ID
call TriggerRegisterPlayerChatEvent(t,Player(i),"'skilladd ",false)
set i = i + 1
endloop
call TriggerAddAction(t,function Actions)
endfunction
endscope[/code]

[jass=Set up the hash table (should be in its own trigger category): this can be rather expensive speed-wise, so you might want to thread bits of it with ExecuteFunc to avoid the op limit]scope SetupSpells
private function Actions takes nothing returns nothing
call StoreInteger(GC_hash,"SpellRawcodes","flamestrike",'RAWC')
//and so on
endfunction
public function InitTrig takes nothing returns nothing
//go somehow
endfunction
endscope[/code]
 
Last edited:
Level 13
Joined
Jul 26, 2008
Messages
1,009
Okay, due to a massive migraine I haven't been able to test these both out. I've been able to add the Hash Tables though to try them, but have had Jass issues.

Since two of the functions conflict as they're already declared, that being Actions and InitTrigger, I assumed I just had to rename them to something fitting based on the name of the function. I saw that the Hash tables attach a name to the rawcode of the spell, and then ties that into SpellRawcodes. I'm not sure if SpellRawcodes is a string though, as it appears to be. And if so, how does it work?

Or is that simply where all the information about which rawcode is tied to which string is stored?

So do I put the conditions for the spell triggering where it says //go somehow? Cause if so I may have to duplicate the chat function that hides the chat, then put in a string condition for it to activate. How would I properly tie that in to, so that they'd simply need to type 'flamestrike or 'customspell? Would I just put a condition like "'" as the string input or 'flamestrike and repete that for each spell? (I'd imagine it's more like the former.)

I'm also worried the hash table works one unit at a time, or does it also work with groups? And there's a conflict as "unit u" is already set in the Mapname Trigger at the very top. Though perhaps that just means I don't have to loop it? Or that looping it would cause issues and I should rename it?

I haven't tested it out in game yet as I haven't completely tied it together, a bit unsure of how to. But I am understanding more and more of it as I study it.

As for the code Rui provided, it's a lot different. I'm curious if one method would be faster and less prone to crash than another? And I've definently noticed Metamorph and Bear and Raven form crashes the game and causes issues in general.

I don't know why I'd have to edit all the abilities to put the rawcodes in the ability names? Don't I just hit ctrl+d to see the rawcodes? Sorry if this sounds dumb, I'm just a bit confused. And how do I put the rawcode in the ability names? Just like: FlamestrikeANfs?

I'm actually pretty confused at what I'd have to do to make all that code work properly with the instructions you've given me.

I do see that the t_NoCrash prevents you from adding those skills that you wouldn't want added. I'm confused as to how it adds the rawcode of the spell properly into the function so that it knows what spell to add? I'm still studying the code to see how it works.

One note is that the 'skilladd has a condition: IsPlayerInForce(pl, udg_Force[2]) Which is the DM force, the only ones allowed to add skills. I don't think either methods take that into account, though I may be able to input it somewhere in them. (Not sure where though)

I imagine I also just copy, paste, and edit pieces for the sake of leveling up, deleveling, and removing a spell.

I understand more JASS now though, still I'm not sure how to set up either method yet.

I tried implementing PurplePoot's hash tables, but I was clueless on how to thread them all together to make them work, and what additional information I would have to input as well. I have no problems doing repeitious work like inputting and assigning RAWCodes, which is how I got the original 'skilladd I posted to work. But I found out that that system would make a map unplayable. I'm trying to get whatever help I can on something more appropriate.

As well, for the benefit of any who help me, I've added the beginning piece of the chat hide function to the code so it's more sensible.

Edit: I just tried putting Rui's trigger into the map. No matter how I seem to input the function or 3 lines within the function, it gives me the error:
Undeclared variable gg_trg_t

What's causing the problem? To my eyes the function seems set up properly when compared to like functions.

There's another function similar to that like this:
JASS:
function InitTrig_Chat takes nothing returns nothing

    local integer ip

    set gg_trg_Chat = CreateTrigger(  )
    call TriggerAddAction( gg_trg_Chat, function ChatEntered )

    //Add Event: Any Player types Any Chat Message
    set ip = 1
    loop
        exitwhen ip > 12
        call TriggerRegisterPlayerChatEvent( gg_trg_Chat, ConvertedPlayer(ip), "", false )
        set ip = ip + 1
    endloop

Perhaps that function could be threaded into this one?
 
Last edited:
Level 40
Joined
Dec 14, 2005
Messages
10,532
Since two of the functions conflict as they're already declared, that being Actions and InitTrigger, I assumed I just had to rename them to something fitting based on the name of the function. I saw that the Hash tables attach a name to the rawcode of the spell, and then ties that into SpellRawcodes. I'm not sure if SpellRawcodes is a string though, as it appears to be. And if so, how does it work?
Imagine the rawcode having two identifiers (IE: the table is 2d), a column and a row. The spell name would be the row, and "SpellRawcode" the column (called Category).

So do I put the conditions for the spell triggering where it says //go somehow? Cause if so I may have to duplicate the chat function that hides the chat, then put in a string condition for it to activate. How would I properly tie that in to, so that they'd simply need to type 'flamestrike or 'customspell? Would I just put a condition like "'" as the string input or 'flamestrike and repete that for each spell? (I'd imagine it's more like the former.)
The former about the spell input (if the gamecache (IE hash table in the way we're using it) doesn't have the value, it won't do anything, so don't worry about garbage strings being checked). As for conditions for adding, add them on to the HaveStoredInteger line via 'and'.

I'm also worried the hash table works one unit at a time, or does it also work with groups? And there's a conflict as "unit u" is already set in the Mapname Trigger at the very top. Though perhaps that just means I don't have to loop it? Or that looping it would cause issues and I should rename it?
You would just loop it for multiple units (enclose the whole if-block in a loop). As for the name, feel free to rename it, but naming a global 'u' is a pretty stupid thing to do.

As for the code Rui provided, it's a lot different. I'm curious if one method would be faster and less prone to crash than another? And I've definently noticed Metamorph and Bear and Raven form crashes the game and causes issues in general.
My method is way faster and easier to write. Gamecache is pretty slow normally, but when you consider the magnitude (how many abilities you have), it's far better than an O(n) search. Imagine gamecache (IE a hash table) as an array which takes two string parameters rather than one integer parameter; it's a lot easier and more efficient to say myArr[45] (or in this case, the equivalent of myArr["SpellRawcodes",s], if the syntax was this rather than wc3's function implementation) rather than having 45+ if-statements, each returning its own value.

I do see that the t_NoCrash prevents you from adding those skills that you wouldn't want added. I'm confused as to how it adds the rawcode of the spell properly into the function so that it knows what spell to add? I'm still studying the code to see how it works.
My system avoids the whole problem; you just don't add those spells to the table, and for all the game knows, they don't exist.

I don't know why I'd have to edit all the abilities to put the rawcodes in the ability names? Don't I just hit ctrl+d to see the rawcodes? Sorry if this sounds dumb, I'm just a bit confused. And how do I put the rawcode in the ability names? Just like: FlamestrikeANfs?
You put it as the next parameter. "SpellRawcodes" goes there all the time. "flamestrike" would be replaced with your ability's name, in lower case (I'd suggest using s = StringCase(GetSubString(...),false) so that case isn't taken into account). 'RAWC' would be replaced with your rawcode (Say, 'ANfs').

One note is that the 'skilladd has a condition: IsPlayerInForce(pl, udg_Force[2]) Which is the DM force, the only ones allowed to add skills. I don't think either methods take that into account, though I may be able to input it somewhere in them. (Not sure where though)
If the DMs are constant, just only register the event for them in the first place. If they aren't, then just add a condition.

I imagine I also just copy, paste, and edit pieces for the sake of leveling up, deleveling, and removing a spell.
For leveling up the spell, you'd have the if-statement check if the ability level was > 0. If so, instead of adding the ability, it would increase its level by one in an elseif.

There's another function similar to that like this:
Why are you using i=1 to 12 with ConvertedPlayer(i)? Use i=0 to 11 with Player(i).

I tried implementing PurplePoot's hash tables, but I was clueless on how to thread them all together to make them work, and what additional information I would have to input as well.
The triggers are independent. One of them is just the variable itself, one of them is setting the variable (the //go somehow line means you should have some way there of running it at map init), and one of them is reading from that variable.

I don't think I missed anything, but I guess we'll see.
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
I'm actually pretty excited about attempting to implement this into my map. I'm pretty sure I understand your explanation here Purple. The only thing I know about arrays is that they're 3D tables, so all 2D tables are just tables.

I've been going through JASS spells and seeing if I couldn't learn anything more about JASS, try my hand at Spell Making and whatnot. I'm trying to get in all my custom spells before I put in this 'spelladd system, to save myself the trouble of having to add more spells and perhaps to learn more about JASS and custom text scripting.

It shouldn't be long before I try this out. Do you think it'd be faster/better than anything a Trigger Skilladd system could churn out? And is there any way you could fix the text wrap on the posts?
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
I'm actually pretty excited about attempting to implement this into my map. I'm pretty sure I understand your explanation here Purple. The only thing I know about arrays is that they're 3D tables, so all 2D tables are just tables.
Arrays are 1D, not 3D.

It shouldn't be long before I try this out. Do you think it'd be faster/better than anything a Trigger Skilladd system could churn out?
Seeing as GUI is just badly compiled Jass with an interface overlaid, yes, it's better than anything you can do in GUI (Well, theoretically you could implement the same thing in GUI, but again, it would be lame and far slower to write).

And is there any way you could fix the text wrap on the posts?
Fix what text wrap?
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Awesome, thanks! I'll be entering this in soon, but I suffered a bit of a setback when my map critted and my back up was 3 days old. Just lost a bunch of spells and some object editor changes, nothing too major. Just an injury to my pride and loss of my time.

I guess I still don't understand arrays as well. So, 1D? They're just lines pretty much?

As for the text wrap... See the image provided below. Perhaps it's just my settings? But I didn't change them...

Why are you using i=1 to 12 with ConvertedPlayer(i)? Use i=0 to 11 with Player(i).

Because the converted players are what you input when making some changes. Players see player 1 = red. Map Makers see player 0 = Red.

so you might want to thread bits of it with ExecuteFunc to avoid the op limit:

I know the OP Limit, as I run into it all too often with the 'skillladd function I had first started with. But how would I thread that in properly to prevent the OP limit? I'll have to look through some JASS spells to find out how ExecFunc works. And last I remember it was around 220+ lines of code that the OP limit kicked in. Will I have about the same limit with this? Or was it the number of actions going off that caused it?

By the way, where's a good place for the hash table:
JASS:
Library GC initinitializer init
ETC ETC ETC
I could fit it in the chat and map initilization most sensibly. I currently have it in Map Initilization.

Also, I'm trying to figure out a way to make:
JASS:
    public function InitTrig takes nothing returns nothing
        //go somehow
    endfunction
Start up on map initilization. I was thinking of just moving it to the map initilization trigger instead of having it sit in it's own trigger. Would that work?
 

Attachments

  • Textwrap.PNG
    Textwrap.PNG
    113.8 KB · Views: 161
Last edited:
Level 40
Joined
Dec 14, 2005
Messages
10,532
I guess I still don't understand arrays as well. So, 1D? They're just lines pretty much?
Yes. They're just one index. 3d would be 3 indexes. Think of a table with only one row.

As for the text wrap... See the image provided below. Perhaps it's just my settings? But I didn't change them...
Seeing as it doesn't happen on my browser, I can't fix it, since I don't know what's causing it.

Because the converted players are what you input when making some changes. Players see player 1 = red. Map Makers see player 0 = Red.
But that has nothing to do with the user side, and you can also use Player(i+1).

I know the OP Limit, as I run into it all too often with the 'skillladd function I had first started with. But how would I thread that in properly to prevent the OP limit? I'll have to look through some JASS spells to find out how ExecFunc works. And last I remember it was around 220+ lines of code that the OP limit kicked in. Will I have about the same limit with this? Or was it the number of actions going off that caused it?
It has to do with operations, not lines of code, and it's far more than 220 that does it. More on the order of thousands of long lines, or tens of thousands of lines of the sort like set i = i + 1. As for why this circumvents it, the op limit is thread specific, and ExecuteFunc threads.

Start up on map initilization. I was thinking of just moving it to the map initilization trigger instead of having it sit in it's own trigger. Would that work?
Yeah, but I suggest threading it. Ex call ExecuteFunc(SCOPE_PRIVATE+"Actions")
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
I gave it another go to add it, but still couldnt figure out how to put it in properly. So I only got two options, have someone place it in the map for me and I do all the rawcode and repetitious work, or I figure out a lot more JASS and put it in myself. If it comes to the latter, I'll have to do that last as there's a lot more easier things I can input before I go head on into that.

I can manipulate JASS and do some minor functions, but nothing too big. Creating JASS enhanced spells using Vexorian's spell factory will help, I'm sure, as well as reading some Blade.dk tutorials on JASS. I just am excited about a skilladd system and don't want to see it have to come in at such a late point.

Guess we'll see what happens, I'm working a bit of a one man army on a map here. Taken me a good month and a half already to get as far as I have, with the normal handful of set backs.

Purple Poot, if you've got the time and want, I could provide you with the map, and you could place it in and I could complete it. I, and the VD&D community, would be most grateful, but you're also a moderator and a person, so I imagine you're quiet busy. Either way your answer is understandable and I wouldnt give you a time limit.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
This should make it a bit easier, tell me if you still don't get it:

JASS:
struct Hash
    string id
    static gamecache hashgc
    static method onInit takes nothing returns nothing
        call FlushGameCache(InitGameCache("hashgc.w3v"))
        set Hash.hashgc = InitGameCache("hashgc.w3v")
    endmethod
    static method create takes nothing returns Hash
        local Hash dat = Hash.allocate()
        set dat.id = "Hashes:" + I2S(dat)
        return dat
    endmethod
    method operator []= takes string ind, integer val returns nothing
        call StoreInteger(this.hashgc,this.id,ind,val)
    endmethod
    method operator [] takes string ind returns integer
        return GetStoredInteger(this.hashgc,this.id,ind)
    endmethod
endstruct

Put that somewhere in your map (now you don't need the GC library).

Now, you can do this (note that you only need the global def one place for each).

JASS:
//this is, for example, the setup
scope SetupSkills initializer Actions
    globals
        Hash skillhash
    endglobals
    private function Actions takes nothing returns nothing
        set skillhash = Hash.create()
        set skillhash["flamestrike"] = 'ANfs'
        //...
    endfunction
endscope

Then, the learner:

JASS:
scope OnChat
    private function Actions takes nothing returns nothing
        local string s = //ability string
        local unit u //u is the unit to add the spell to. In a loop, just have it be set constantly
        call UnitAddAbility(u,skillhash[s])
        //more stuff?
    endfunction
    public function InitTrig takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        loop
             exitwhen i > //MAX PLAYER ID
             call TriggerRegisterPlayerChatEvent(t,Player(i),"'skilladd ",false)
             set i = i + 1
        endloop
        call TriggerAddAction(t,function Actions)
    endfunction
endscope
 
Last edited:
Level 13
Joined
Jul 26, 2008
Messages
1,009
I'll take a look at it first thing in the morning and see if I can get it figured out properly.

EDIT: Alright, I'm setting it up, and I was curious. Instead of using:
JASS:
local unit u

Would using the old way of determing what unit to modify be easier and more compatible?:
JASS:
local group grp
,
JASS:
set grp

JASS:
 set grp = GetUnitsInRectAll(GetPlayableMapRect())

And if so, how would I properly put that in?

The map runs on a system that determines what unit to modify based on what unit is currently selected by the player.

Also:
JASS:
        local string s = "'"

I did that with the learner. Is that' the correct way to set that up? Actually, I don't think it is. Hmm. What do you mean by //ability string?

BTW I've taken pictures of how I currently have it set up, so you can get a better picture of what is and isn't done right. I'll try to go through a brief run down of what I think the code does and what code I have no idea on if that would also help give you and idea of what I'm having issues with.

Right now, I'm getting a weird compile error.

Missing Endscope:

call SetPlayerTechResearched( Player(0), 'Remg', 1 )

In the trigger starting as

//***************************************************************************
//*
//* Upgrades
//*
//***************************************************************************

function InitUpgrades_Player0 takes nothing returns nothing
 

Attachments

  • HashSetup.tga
    1.7 MB · Views: 60
  • Learner.tga
    1 MB · Views: 72
  • RawCodes.tga
    792.3 KB · Views: 90
  • Triggers.tga
    293.3 KB · Views: 63
Last edited:
Level 40
Joined
Dec 14, 2005
Messages
10,532
Here we are:

String s should probably be the rest of the string after 'skilladd, I just left it blank unless you wanted something else.

As for doing the current player selection, here we go...

JASS:
scope OnChat
    globals
        group enumGrp = CreateGroup()
        //You should have a global group for all enums in your map
        //if so, remove this one and replace the use of it with your global group
    endglobals
    private function TheFilter takes nothing returns boolean
        call UnitAddAbility(GetFilterUnit(),skillhash[SubString(GetEventPlayerChatString(),10,StringLength(GetEventPlayerChatString()))])
        return false
    endfunction
    private function Actions takes nothing returns nothing
        call GroupEnumUnitsSelected(enumGrp,GetTriggerPlayer(),Filter(function TheFilter))
    endfunction
    public function InitTrig takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        loop
             exitwhen i > //MAX PLAYER ID
             call TriggerRegisterPlayerChatEvent(t,Player(i),"'skilladd ",false)
             set i = i + 1
        endloop
        call TriggerAddAction(t,function Actions)
    endfunction
endscope

As for your compile error, I accidentally wrote 'enscope' instead of 'endscope' in SetupSkills.
 
Last edited:
Level 13
Joined
Jul 26, 2008
Messages
1,009
The only problems seem to arise with the Learner.
I fixed the problem with the endscope being enscope when I was putting it in. What it actually was, was I missed the scroll down and skipped over the endscope when putting in the learner.

My problem now is this:

Missing return
JASS:
        call GroupEnumUnitsSelected(enumGrp , GetTriggerPlayer() , Filter(function OnChat__TheFilter))

In response to this particular line I'm assuming:
JASS:
    private function Actions takes nothing returns boolean
        call GroupEnumUnitsSelected(enumGrp,GetTriggerPlayer(),Filter(function TheFilter))
    endfunction

I've provided another tga picture to show you the entire setup.
I'd put a return in myself, but I'm not sure if it needs return false or return true. Actually, I'm not sure how I'd be able to tell if it was or wasn't working. It seems like it still needs some more things filled in to make it work.
 

Attachments

  • Learner.tga
    1.1 MB · Views: 63
Level 13
Joined
Jul 26, 2008
Messages
1,009
Alright, well, I got it put into my map so that when I save it doesn't have any compile errors.

However it doesn't work yet. I haven't really had the chance to look over how to make it so that whatever units are selected are given the skill by typing 'Skillname. However, I have set it up so that it's not 'skilladd Flamestrike but rather just 'flamestrike.

But just in case, let me show you the change I made to the code so you can confirm I haven't caused it to fail by changing it:

JASS:
scope OnChat
    globals        
        group enumGrp = CreateGroup()
    endglobals
    private function TheFilter takes nothing returns boolean
        call UnitAddAbility(GetFilterUnit(),skillhash[SubString(GetEventPlayerChatString(),2,StringLength(GetEventPlayerChatString()))])
        return false
    endfunction
    private function Actions takes nothing returns boolean
        call GroupEnumUnitsSelected(enumGrp,GetTriggerPlayer(),Filter(function TheFilter))
        return true
    endfunction
    public function InitTrig takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        loop
            exitwhen i > 11 //MAX PLAYER ID
            call TriggerRegisterPlayerChatEvent(t,Player(i),"'",false)
            set i = i + 1
        endloop
        call TriggerAddAction(t,function Actions)
    endfunction
endscope

I've left the other two triggers virtually the same. Now I'm not entirely sure how to make it add the skills for the units and heroes selected by the player.

I haven't added the variables in yet, now that I think about it. That may be causing it to not work. But doesn't it tell you if a variable hasn't been declared yet? Either way, I'll go over the code and figure out what variables are what and make them.

Alright, I've made the following variables:
hashgc (Game Cache)

Tell me if I've missed any variables. And after inputting the variables is there anything else I need to do to set this up properly?
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Noted. But I wanna clarify I need to fix the right piece before I move on:

JASS:
        call UnitAddAbility(GetFilterUnit(),skillhash[SubString(GetEventPlayerChatString(),10,StringLength(GetEventPlayerChatString()))])

The string length in this is 10 from your original one. I figured it referenced the start of the string from the 9 character length of "'skillladd " so I changed it to 2 since I reduced that down to 1. Or do you mean I need to change something else? I thought that was the only thing I really changed though. I'll take a closer look.
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Alright, well I understand that I think. If the string starts on index then it starts from 0, if it ends on index it start from 1. Spaces are always included as a character. Not too hard.

Now I'd assume with the size of your posts that this means the system is set up and should be working. If I type 'flamestrike, it should add flamestrike to the group or single unit that I have selected.

Unfortunately it still doesn't work. Here's the current code I have set up:

JASS:
scope OnChat
    globals        
        group enumGrp = CreateGroup()
    endglobals
    private function TheFilter takes nothing returns boolean
        call UnitAddAbility(GetFilterUnit(),skillhash[SubString(GetEventPlayerChatString(),1,StringLength(GetEventPlayerChatString()))])
        return false
    endfunction
    private function Actions takes nothing returns boolean
        call GroupEnumUnitsSelected(enumGrp,GetTriggerPlayer(),Filter(function TheFilter))
        return true
    endfunction
    public function InitTrig takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        loop
            exitwhen i > 11 //MAX PLAYER ID
            call TriggerRegisterPlayerChatEvent(t,Player(i),"'",false)
            set i = i + 1
        endloop
        call TriggerAddAction(t,function Actions)
    endfunction
endscope

JASS:
scope SetupSkills initializer Actions
    globals
        Hash skillhash
    endglobals
    private function Actions takes nothing returns nothing
    set skillhash = Hash.create()
    set skillhash["flamestrike"] = 'ANfs'
    //Add More Rawcodes here    
    endfunction
endscope

JASS:
struct Hash
    string id
    static gamecache hashgc
    static method onInit takes nothing returns nothing
        call FlushGameCache(InitGameCache("hashgc.w3v"))
        set Hash.hashgc = InitGameCache("hashgc.w3v")
    endmethod
    static method create takes nothing returns Hash
        local Hash dat = Hash.allocate()
        set dat.id = "Hashes:" + I2S(dat)
        return dat
    endmethod
    method operator []= takes string ind, integer val returns nothing
        call StoreInteger(this.hashgc,this.id,ind,val)
    endmethod
    method operator [] takes string ind returns integer
        return GetStoredInteger(this.hashgc,this.id,ind)
    endmethod
endstruct
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Oh, nope, it's not AHfs, though it is. ANfs is nuetral flamestrike, AHfs is human hero flamestrike, so either way it should still add. And the case is just right.

So it's definently something other than the rawcode. I only have one variable for this, that being hashGC (gamecache). Is there more I need?
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Oh wow, is THAT all it is? I have the learner set up as Learner. I didn't think trigger names mattered. I'll go test it now.

Yeah, that's all it was! Thanks a ton, I'll probably still need help in setting up the rest of it, but all things considered I'm pretty sure I know how to do it with a few if statements and bits and pieces of my old system.
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Actually, I do need the if statement for leveing and deleveling/removing skills. Or so that's the advice you threw upon me. How do I check the level of the skill by referencing the skill that's been typed though? That's one thing I failed to do in the old system, figuring out how to check the skill's level on the unit recieving, and if the unit even has it.

I'm trying some stuff out, but I'm really at a loss on how to do it. This stuff is so foreign from the code I have been working with in the map, it's a step by step for me to grasp it.

I'm trying to get it to differentiate between if the player types ' (Level) or @ (Delevel) as a string starter. I am noticing that your system actually mimics mine in that there's 3 steps in the learner. Step one registers the string, step two collects the units to be applied to, and step 3 adds the skill from the hash (This is on the learner). Of course yours is a ton more efficient. It just means that there's only one universal way to do it.

I'm guessing the rawcode trigger creates a database of rawcodes and the hash struct sets up the hash table properly, preparing it for it's task.

Anyways, I'm having trouble making it check the string. I'm playing with the if statements right now trying to find a setup that checks what player is type it and what they're typing.

I tried doing this to the code, but it didn't work:

JASS:
scope OnChat
    globals
        group enumGrp = CreateGroup()
    endglobals
    private function TheFilter takes nothing returns boolean
        call UnitAddAbility(GetFilterUnit(),skillhash[SubString(GetEventPlayerChatString(),1,StringLength(GetEventPlayerChatString()))])
        return false
    endfunction
        
    private function TheNegativeFilter takes nothing returns boolean
        call UnitRemoveAbility(GetFilterUnit(),skillhash[SubString(GetEventPlayerChatString(),1,StringLength(GetEventPlayerChatString()))])
        return false
    endfunction
    
    private function Actions takes nothing returns boolean
        call GroupEnumUnitsSelected(enumGrp,GetTriggerPlayer(),Filter(function TheFilter))
        return true
    endfunction
    
    private function NegativeActions takes nothing returns boolean
        call GroupEnumUnitsSelected(enumGrp,GetTriggerPlayer(),Filter(function TheNegativeFilter))
        return true
    endfunction
    
    public function InitTrig takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 7
        local string str
        
        set str = GetEventPlayerChatString()
        
        loop
            exitwhen i > 10
            if (SubStringBJ(str,1,1) == "'") then
                call TriggerAddAction(t,function Actions)
            elseif (SubStringBJ(str,1,1) == "@") then
                call TriggerAddAction(t,function NegativeActions)
            endif
            set i = i + 1     
        endloop
    endfunction
endscope

So I'm gonna try some more things. BTW the 7-10 modification registers only the DMs as the ones allowed to enter those commands.

Oh hey, you've resigned? How come?
 
Last edited:
Level 13
Joined
Jul 26, 2008
Messages
1,009
Crap. So where do I place a check string to figure out whether to add\level or remove\delevel a skill?

And how do I create an approriate if statement that determines whether a skill should add or level/remove or delevel?

I'm still at a loss on how to complete this.

About your resignation: It's unfortunate. You've been the person I'm most grateful for for your assistance, and I know the VD&D community is more than happy to have you working with me on this system.

Unfortunately the points you illustrated are pretty common in a gaming forum, resources or no. Volunteering for a large community is a great way to get recognition, but gamer's are a competitive breed. Most of them are teen and preteen too, so you have a heavy level of immaturity. That can compel a lot of the arguments to turn into flaming, and lack of attention or respect. Would be a lot for anyone to take on the responsibility of, even if you were being paid.

I couldn't give you any more praise than your thread has. At least you are able to move on knowing you made a distance and played a bigger role.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
The actions would look like this:

JASS:
private function Actions takes nothing returns nothing
    local string s = SubString(GetEventPlayerChatString(),0,1)
    if s == "'" then
        //do stuff for add skill
    elseif s == "@" then
        //do stuff for remove skill
    //etc
    endif
endfunction

Leave your InitTrig the same as before you changed it.
 
Last edited:
Level 13
Joined
Jul 26, 2008
Messages
1,009
I'm getting a compile error.
Undeclared function GetSubstring.
I changed it to SubString, as I think that's what you meant to do.

I've changed and updated the trigger to try out this new skillremove piece. Now, for the last and final piece of this, how do I set up the if-statement to check to level and delevel them?

There's a minor bug too, if you overlevel something past it's highest assigned level, the skill stops working properly. It's prevented in other maps. I'm wondering if this system will be able to prevent it.

I also added: call TriggerRegisterPlayerChatEvent(t,Player(i),"@",false) so it registers the remove function.

Now you told me when the skill library gets too long I should start doing ExecuteFunc. So, while I was looking up how to properly use ExecuteFunc I stumbled upon a discussion about several things being faster than ExecuteFunc.

TriggerEvaluate
TriggerExecute()
.execute() ................ (VJass)

I was curious if you thought one of these would be more appropriate, faster, or better than ExecuteFunc for this particular skilladd system?
 
Last edited:
Level 40
Joined
Dec 14, 2005
Messages
10,532
Right, it's SubString, the name changes between languages. Sorry about that.

There's a minor bug too, if you overlevel something past it's highest assigned level, the skill stops working properly. It's prevented in other maps. I'm wondering if this system will be able to prevent it.
Don't use IncUnitAbilityLevel, use SetUnitAbilityLevel.

I've changed and updated the trigger to try out this new skillremove piece. Now, for the last and final piece of this, how do I set up the if-statement to check to level and delevel them?
Either pick new characters, or just check if the unit already has the skill (in the case of remove, if the level is >1, then instead delevel it).

Now you told me when the skill library gets too long I should start doing ExecuteFunc. So, while I was looking up how to properly use ExecuteFunc I stumbled upon a discussion about several things being faster than ExecuteFunc.
You don't need the speed, and they add a lot more code. Also, TriggerEvaluate doesn't thread, which means it's out of the question anyways.
 
Level 13
Joined
Jul 26, 2008
Messages
1,009
Cool. Alright, I see what you're saying with the SetUnitAbility. I was really uncertain of how to make it work until you mentioned the +1 thing. At first I was thinking how do I use SetUnitAbil to make it increase? Then I remembered the Arthimetic thing in triggers when you mentioned the +1, which I could easily get the formula from.
 
Status
Not open for further replies.
Top