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

Data Collision +rep to who helps

Status
Not open for further replies.
Level 11
Joined
Jun 30, 2008
Messages
580
The Problem - When a hero gains 2 quests at the same time from a giver, when the hero kills units, the data collides
But: When you gain the quests seperatly they work fine.

JASS:
library QuestLib initializer Init
  globals
    constant string     QUEST_UPDATE        = "|cff008000Quest Update:"
    constant integer    STRING_CAP          = 5
    constant string     COMPLETE_COLOR      = "|cff808080"
    constant string     UPDATE_STRING       = " - "
    constant string     Lr                  = "|r"
    constant integer    TALK_ID             = 'A000'
    constant integer    MAX_QUESTS          = 200
    constant real       UPDATE_DURATION     = 7.0
    constant integer    MAX_PLAYERS         = 10
    integer index = 0
    hashtable q = InitHashtable()
  endglobals
  
  struct Players
    unit hero
    integer questindex
    boolean array qenabled[MAX_QUESTS]
  endstruct
  
  struct QData
    integer array kills[STRING_CAP]
    integer array items[STRING_CAP]
    boolean array complete[STRING_CAP]
  endstruct

  /* Quest Types:
  Kill Quest    - 1
  Item Quest    - 2
  Travel Quest  - 3
  Event Quest   - 4
  */
  struct Quest
    unit        giver       = null
    integer     qindex      = 0
    integer     quests      = 0
    integer     level       = 0    
    integer     killunits   = 0
    integer     ritems      = 0
    integer     exp         = 0
    integer     gold        = 0
    integer     lumber      = 0
    integer     strings     = 0
    string      intro       = ""
    string      info        = ""
    string      complete    = ""
    string      reinfo      = ""
    string      name        = ""
    integer array   killunit[STRING_CAP]
    integer array   howmany[STRING_CAP]
    string array    unitname[STRING_CAP]
    integer array   rewarditem[12]
    boolean array   Qtypes[4]
        method IntQuest takes unit u, integer i, string s returns nothing
            set this.giver = u
            set this.level = i
            set this.name = s
            set this.quests = this.quests+1
            set index = index+1
            set this.qindex = index
            call SaveInteger(q, GetHandleId(this.giver), this.qindex, this)
        endmethod

        method IntKillQuest takes nothing returns nothing
            set this.Qtypes[1] = true
            set this.killunits = 0
        endmethod

        method IntKillUnit takes integer u, integer i, string n returns nothing
            if this.Qtypes[1] != true then
             call BJDebugMsg("Kill Quest Not Initialized")
            else
             if this.strings < STRING_CAP then 
              set this.killunits = this.killunits+1
              set this.killunit[this.killunits] = u
              set this.howmany[this.killunits] = i
              set this.unitname[this.killunits] = n
              set this.strings = this.strings+1
             else
              call BJDebugMsg("Max String Limit")
             endif
            call SaveInteger(q, u, this.qindex, this)
            endif
        endmethod

        method AddRItem takes integer i returns nothing
            set this.ritems = this.ritems+1
            set this.rewarditem[this.ritems] = i
        endmethod

        method AddReward takes integer e, integer g, integer l returns nothing
            set this.exp = e
            set this.gold = g
            set this.lumber = l
        endmethod

        method IntStrings takes string a, string b, string c, string d returns nothing
            set this.intro = a
            set this.info = b
            set this.complete = c
            set this.reinfo = d
        endmethod
        
  endstruct


  private function talk takes nothing returns nothing
    local real angle = GetUnitFacing(GetSpellTargetUnit())+180
    local integer i = 0
    local integer ii = 0
    local Quest dat
    local Players pdat = LoadInteger(q, GetHandleId(GetTriggerUnit()), GetPlayerId(GetOwningPlayer(GetTriggerUnit())))
    local QData qdat = QData.create()
    call BJDebugMsg(I2S(GetHandleId(pdat.hero)))
    call BJDebugMsg(I2S(GetPlayerId(GetOwningPlayer(pdat.hero))))
    loop
    exitwhen ii > MAX_QUESTS
    set ii = ii+1
     set dat = LoadInteger(q, GetHandleId(GetSpellTargetUnit()), ii)
    if dat.giver == GetSpellTargetUnit() then
     if pdat.hero == GetTriggerUnit() then
       set pdat.qenabled[dat.qindex] = true
        loop
        exitwhen i > dat.killunits
        set i = i + 1
         set qdat.kills[i] = 0
         set qdat.complete[i] = false
        endloop
       call BJDebugMsg("Quest Accepted")
       call BJDebugMsg(I2S(dat.qindex))
       call SaveInteger(q, dat.qindex, GetHandleId(pdat.hero), qdat)
     endif
    endif
    endloop
    /*call SetCameraTargetControllerNoZForPlayer( GetOwningPlayer(GetTriggerUnit()), GetSpellTargetUnit(), 0, 0, false )
    //call RotateCameraAroundLocBJ( 360.00, GetUnitLoc(GetTriggerUnit()), GetOwningPlayer(GetSpellTargetUnit()), 100000000.00 )
    call SetCameraFieldForPlayer( GetOwningPlayer(GetTriggerUnit()), CAMERA_FIELD_ROTATION, angle, 3 )
    call SetCameraFieldForPlayer( GetOwningPlayer(GetTriggerUnit()), CAMERA_FIELD_TARGET_DISTANCE, 250, 3 )
    call SetCameraFieldForPlayer( GetOwningPlayer(GetTriggerUnit()), CAMERA_FIELD_ANGLE_OF_ATTACK, 0, 3 )
    call SetCameraFieldForPlayer( GetOwningPlayer(GetTriggerUnit()), CAMERA_FIELD_ZOFFSET, 100, 3 )*/
  endfunction
  
  private function QuestUpdate takes player p, unit triggeru, unit killer returns nothing
    local Players pdat = LoadInteger(q, GetHandleId(killer), GetPlayerId(p)) 
    local Quest dat
    local QData qdat
    local integer i = 1
    local integer c = 0
    local integer ii = 1
    call BJDebugMsg(I2S(GetHandleId(pdat.hero)))
    call BJDebugMsg(I2S(GetPlayerId(GetOwningPlayer(pdat.hero))))
    //Goes Through All Quests
    loop
    exitwhen ii > MAX_QUESTS
    if pdat.hero == killer then
     set dat = LoadInteger(q, GetUnitTypeId(triggeru), ii)
     set qdat = LoadInteger(q, dat.qindex, GetHandleId(pdat.hero))
     if dat.Qtypes[1] == true then
      if pdat.qenabled[dat.qindex] == true then
       loop
       exitwhen i > dat.killunits
        if dat.killunit[i] == GetUnitTypeId(triggeru) then
         if (IsPlayerInForce(GetLocalPlayer(), GetForceOfPlayer(p))) then
        // Use only local code (no net traffic) within this block to avoid desyncs.
          call ClearTextMessages()
         endif
        call DisplayTimedTextToPlayer(p, 0, 0, UPDATE_DURATION, QUEST_UPDATE+Lr+" "+dat.name)
         if dat.howmany[i] > qdat.kills[i] then
          set qdat.kills[i] = qdat.kills[i] + 1
         endif
        endif
       set i = i+1
       endloop
       set i = 1
     //Goes Through All Units
     loop
     exitwhen i > dat.killunits
      if dat.killunit[i] == GetUnitTypeId(triggeru) then
       if dat.howmany[i] > qdat.kills[i] then
        call DisplayTimedTextToPlayer(GetOwningPlayer(killer), 0, 0, UPDATE_DURATION, UPDATE_STRING+I2S(qdat.kills[i])+" / "+I2S(dat.howmany[i])+" "+dat.unitname[i] + " Killed!")
       else
        set qdat.complete[i] = true
        set c = c+1
        call DisplayTimedTextToPlayer(GetOwningPlayer(killer), 0, 0, UPDATE_DURATION, COMPLETE_COLOR+UPDATE_STRING+I2S(qdat.kills[i])+" / "+I2S(dat.howmany[i])+" "+dat.unitname[i] + " Killed!" + " (Complete)")
       endif
      else
       if dat.howmany[i] > qdat.kills[i] then
        call DisplayTimedTextToPlayer(GetOwningPlayer(killer), 0, 0, UPDATE_DURATION, UPDATE_STRING+I2S(qdat.kills[i])+" / "+I2S(dat.howmany[i])+" "+dat.unitname[i] + " Killed!")
       else
        set qdat.complete[i] = true
        set c = c+1
        call DisplayTimedTextToPlayer(GetOwningPlayer(killer), 0, 0, UPDATE_DURATION, COMPLETE_COLOR+UPDATE_STRING+I2S(qdat.kills[i])+" / "+I2S(dat.howmany[i])+" "+dat.unitname[i] + " Killed!" + " (Complete)")
       endif
      endif
       if c == dat.strings then
        call DisplayTimedTextToPlayer(GetOwningPlayer(killer), 0, 0, UPDATE_DURATION, UPDATE_STRING+"Return to " + GetUnitName(dat.giver))
       endif
     set i = i + 1
     endloop
      endif
     endif
    set ii = ii+1
    endif
    endloop
    
  endfunction
  
  function InitHero takes player p, unit u returns nothing
        local Players pdat = Players.create()
        local integer i = 1
        set pdat.hero = u
        loop 
        exitwhen i > MAX_QUESTS
         set pdat.qenabled[i] = false
         set i = i + 1
        endloop
        call SaveInteger(q, GetHandleId(pdat.hero), GetPlayerId(GetOwningPlayer(pdat.hero)), pdat)
        call BJDebugMsg(I2S(GetHandleId(u)))
        call BJDebugMsg(I2S(GetPlayerId(GetOwningPlayer(u))))
  endfunction
  
  private function KillDeath takes nothing returns nothing
   call QuestUpdate(GetOwningPlayer(GetKillingUnit()), GetTriggerUnit(), GetKillingUnit())
  endfunction
  
  private function cond takes nothing returns boolean
     if ( not ( GetSpellAbilityId() == TALK_ID ) ) then
        return false
    endif
    return true
  endfunction
  
  private function KillCond takes nothing returns boolean
      if ( not ( GetOwningPlayer(GetTriggerUnit()) == Player(PLAYER_NEUTRAL_AGGRESSIVE) ) ) then
        return false
    endif
    return true
  endfunction
  
  private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerAddAction(t, function talk)
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function cond))
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition( t, Condition( function KillCond ) )
    call TriggerAddAction( t, function KillDeath )
  endfunction





endlibrary

JASS:
function InitQuests takes nothing returns nothing
        local Quest KillBoar = Quest.create()
        local Quest KillerP = Quest.create()
        call KillBoar.IntQuest(gg_unit_Ntin_0022, 1, "Killer Boars")
        call KillBoar.IntKillQuest()
        call KillBoar.IntKillUnit('hfoo', 5, "Footmen")
        call KillBoar.IntKillUnit('hrif', 2, "Riflemen")
        call KillBoar.AddRItem('i000')
        call KillBoar.AddReward(150, 15, 0)
        call KillBoar.IntStrings(/*
        */"This is the intro!",/* 
        */"This is the Info!",/* 
        */"This is the Complete msg!",/*
        */"This is the reinfo!"/*
        */)
        call KillerP.IntQuest(gg_unit_Ntin_0022, 1, "Bad Peasant")
        call KillerP.IntKillQuest()
        call KillerP.IntKillUnit('hpea', 4, "Peasant")
    
    
    
    
    
    
    
    
    
    endfunction
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
What do you mean "the data collides".

forsakener said:
JASS:
        method IntKillUnit takes integer u, integer i, string n returns nothing
            if this.Qtypes[1] != true then
             call BJDebugMsg("Kill Quest Not Initialized")
            else
             if this.strings < STRING_CAP then 
              set this.killunits = this.killunits+1
              set this.killunit[this.killunits] = u
              set this.howmany[this.killunits] = i
              set this.unitname[this.killunits] = n
              set this.strings = this.strings+1
             else
              call BJDebugMsg("Max String Limit")
             endif
            call SaveInteger(q, u, this.qindex, this)
            endif
        endmethod

I'm a little confused by your SaveInteger. What it is supposed to be doing?

forsakener said:
JASS:
        method IntQuest takes unit u, integer i, string s returns nothing
            set this.giver = u
            set this.level = i
            set this.name = s
            set this.quests = this.quests+1
            set index = index+1
            set this.qindex = index
            call SaveInteger(q, GetHandleId(this.giver), this.qindex, this)
        endmethod

Also you should be doing set index = index+1 after you have done set this.qindex = index. You really need to isolate what is causing the overlap, you've posted way too much code for anybody to debug without being able to compile it and run it. You should upload a map or something if you can't isolate where the problem is originating.
 
Level 11
Joined
Jun 30, 2008
Messages
580
Thanks for posting +rep anyway :D
I found the problem a few hours after I posted, If you look at function talk, I only create qdat once, so everything I save goes to only that qdat data, All i had to do is create qdat in the loop and it works perfectly
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
To be honest I didn't even look at the code after the main Quest struct.

You should use better indentation on your code though, it makes it way easier to navigate through. For example:

JASS:
    loop
        exitwhen ii > MAX_QUESTS
        set ii = ii+1
        set dat = LoadInteger(q, GetHandleId(GetSpellTargetUnit()), ii)
    
        if dat.giver == GetSpellTargetUnit() then
            if pdat.hero == GetTriggerUnit() then
                set pdat.qenabled[dat.qindex] = true
            
                loop
                    exitwhen i > dat.killunits
                    set i = i + 1
                    set qdat.kills[i] = 0
                    set qdat.complete[i] = false
                endloop
                call BJDebugMsg("Quest Accepted")
                call BJDebugMsg(I2S(dat.qindex))
                call SaveInteger(q, dat.qindex, GetHandleId(pdat.hero), qdat)
            endif
        endif

    endloop

This is much easier to read than:

JASS:
    loop
    exitwhen ii > MAX_QUESTS
    set ii = ii+1
     set dat = LoadInteger(q, GetHandleId(GetSpellTargetUnit()), ii)
    if dat.giver == GetSpellTargetUnit() then
     if pdat.hero == GetTriggerUnit() then
       set pdat.qenabled[dat.qindex] = true
        loop
        exitwhen i > dat.killunits
        set i = i + 1
         set qdat.kills[i] = 0
         set qdat.complete[i] = false
        endloop
       call BJDebugMsg("Quest Accepted")
       call BJDebugMsg(I2S(dat.qindex))
       call SaveInteger(q, dat.qindex, GetHandleId(pdat.hero), qdat)
     endif
    endif
    endloop
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Well when you have an array member inside a struct, it affects how many instances of that struct are possible for proper functionality at any given time. When you define a struct, by default, it is a parallel array with 8191 indexes. This means if you have an array declared inside this struct with [100] indexes, it will limit the struct to only handling 1/100th of what it could before.

What you're doing with struct s_name [32764] is declaring a larger parallel array, so the amount of structs that can function at once increases by a lot. Now, instead of only being able to handle roughly 81 instances, it is capable of handling 327. Notice that 32764 is 4 × 8191.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Yea but that will reduce the amount of structs you can use at a time. Taking another look at it you don't even use very large array sizes, so it shouldn't even be a problem. If it is, at some point in the future, you can do what I showed you to fix it.
 
Status
Not open for further replies.
Top