System PUI - Perfect Unit Indexing

Cohadar

master of fugue
New version: v6.2 (fully backwards compatible)
Changes:
* You can now get unit from an index GetIndexUnit(index) -> unit
* Added PUI module that does the same stuff as PUI textmacro

JASS:

//===========================================================================
//  PUI -- Perfect Unit Indexing by Cohadar -- v6.2
//===========================================================================
//
//  PURPOUSE:
//       * Extending UnitUserData()
//       * This is basically perfect hashing algorithm for units
//
//  HOW TO USE:
//       * You have two functions at your disposal: 
//           GetUnitIndex(unit) -> index
//           GetIndexUnit(index) -> unit
//
//       * Put you custom unit properties in struct that extends arrays.
//         Use unit index as id of that struct.
//         There is an example of this in demo map.
//         
//  PROS:
//       * Unit indexes are assigned only to units that actually need them.
//       * Unit index will be automatically recycled when unit is removed from the game.
//       * Automatically detects null unit handles and removed unit handles (in debug mode)
//
//  CONS:
//       * This system needs exclusive access to UnitUserData
//
//  DETAILS:
//       * Uses internal vJass struct index allocation/deallocation algorithm.
//       * Periodically checks and recycles unit indexes
//
//  THANKS TO:
//       * Vexorian - for his help with PUI textmacro
//       * Builder Bob - for testing and bugfinding
//       * Joker(Div) - bugfinding
//
//  HOW TO IMPORT:
//       * Just create a trigger named PUI
//       * convert it to text and replace the whole trigger text with this one
//
//===========================================================================


library PUI initializer Init

//===========================================================================
globals    
    // maximum number of indexed units on your map at a single moment of time
    private constant integer MAX_INDEXES = 1024  // up to 8192
    
    // period of recycling, 32 indexes per second
    private constant real PERIOD = 0.03125   
    
    // current check index
    private integer C = 0 
endglobals

//===========================================================================
// Using internal struct algorithm for index allocation/dealocation
// This way I don't have to write messy code of my own
//===========================================================================
private struct UnitIndex
    unit u
    
    //----------------------------------------------------------
    static method create takes unit whichUnit returns UnitIndex
        local UnitIndex index 
        
        // check for null unit handle
        debug if whichUnit == null then
        debug     call BJDebugMsg("|c00FF0000ERROR: PUI - Index requested for null unit")
        debug     return 0
        debug endif
        
        set index = UnitIndex.allocate()
        set index.u = whichUnit
        call SetUnitUserData(whichUnit, index)
        
        // check for removed unit handle
        debug if GetUnitUserData(whichUnit) == 0 then
        debug     call BJDebugMsg("|c00FFCC00WARNING: PUI - Bad unit handle")
        debug     return 0
        debug endif        
        
        return index
    endmethod
    
    //----------------------------------------------------------
    static method Recycler takes nothing returns boolean
        local UnitIndex index = C
        set C = C + 1
        if C == MAX_INDEXES then
            set C = 0
        endif
        if index.u != null then
            if (GetUnitUserData(index.u) == 0) then
                set index.u = null
                call index.destroy()
            endif
        endif
        return false
    endmethod
endstruct

//===========================================================================
//  Returns index of some unit, if unit has no index it gets a new one.
//===========================================================================
function GetUnitIndex takes unit whichUnit returns integer
    local UnitIndex index = GetUnitUserData(whichUnit)
    if index == 0 then
        set index = UnitIndex.create(whichUnit)
    endif
    return index
endfunction

//===========================================================================
//  Return unit that has specified index, or null in no such unit exists.
//===========================================================================
function GetIndexUnit takes integer index returns unit
    local UnitIndex i = index
    if i.u != null then
        if GetUnitUserData(i.u) == index then
            return i.u
        endif
    endif
    return null
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterTimerEvent(trig, PERIOD, true)
    call TriggerAddCondition(trig, Condition(function UnitIndex.Recycler))
endfunction

endlibrary

//***************************************************************************
//
//  PUI TEXTMACROS
// 
//***************************************************************************

//===========================================================================
//  Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
//  Do NOT put handles that need to be destroyed here (timer, trigger, ...)
//  Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
    private static unit   array pui_unit
    private static $TYPE$ array pui_data
    
    //-----------------------------------------------------------------------
    //  Returns default value when first time used
    //-----------------------------------------------------------------------
    static method operator[] takes unit whichUnit returns $TYPE$
        local integer pui = GetUnitIndex(whichUnit)
        if .pui_unit[pui] != whichUnit then
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = $DEFAULT$
        endif
        return .pui_data[pui]
    endmethod
    
    //-----------------------------------------------------------------------
    static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
        local integer pui = GetUnitIndex(whichUnit)
        set .pui_unit[pui] = whichUnit
        set .pui_data[pui] = whichData
    endmethod
endstruct
//! endtextmacro

//===========================================================================
//  Never destroy PUI structs directly.
//  Use .release() instead, will call .destroy()
//  The best option is never to release or destroy PUI structs manually
//    because they will be recycled automatically with unit handle.
//===========================================================================
//! textmacro PUI
    private static unit    array pui_unit
    private static integer array pui_data
    private static integer array pui_id
    
    //-----------------------------------------------------------------------
    //  Returns zero if no struct is attached to unit
    //-----------------------------------------------------------------------
    static method operator[] takes unit whichUnit returns integer
        local integer pui = GetUnitIndex(whichUnit)
        if .pui_data[pui] != 0 then
            if .pui_unit[pui] != whichUnit then
                // recycled handle detected
                call .destroy(.pui_data[pui])
                set .pui_unit[pui] = null
                set .pui_data[pui] = 0            
            endif
        endif
        return .pui_data[pui]
    endmethod
    
    //-----------------------------------------------------------------------
    //  This will overwrite already attached struct if any
    //-----------------------------------------------------------------------
    static method operator[]= takes unit whichUnit, integer whichData returns nothing
        local integer pui = GetUnitIndex(whichUnit)
        if .pui_data[pui] != 0 then
            call .destroy(.pui_data[pui])
        endif
        set .pui_unit[pui] = whichUnit
        set .pui_data[pui] = whichData
        set .pui_id[whichData] = pui
    endmethod

    //-----------------------------------------------------------------------
    //  If you do not call release struct will be destroyed when unit handle gets recycled
    //-----------------------------------------------------------------------
    method release takes nothing returns nothing
        local integer pui= .pui_id[integer(this)]
        call .destroy()
        set .pui_unit[pui] = null
        set .pui_data[pui] = 0
    endmethod
//! endtextmacro

//***************************************************************************
//
//  PUI MODULE  (does the same stuff as PUI textmacro)
// 
//***************************************************************************

//===========================================================================
//  Never destroy PUI structs directly.
//  Use .release() instead, will call .destroy()
//  The best option is never to release or destroy PUI structs manually
//    because they will be recycled automatically with unit handle.
//===========================================================================
module PUI
    private static unit    array pui_unit
    private static integer array pui_data
    private static integer array pui_id
    
    //-----------------------------------------------------------------------
    //  Returns zero if no struct is attached to unit
    //-----------------------------------------------------------------------
    static method operator[] takes unit whichUnit returns integer
        local integer pui = GetUnitIndex(whichUnit)
        if .pui_data[pui] != 0 then
            if .pui_unit[pui] != whichUnit then
                // recycled handle detected
                call .destroy(.pui_data[pui])
                set .pui_unit[pui] = null
                set .pui_data[pui] = 0            
            endif
        endif
        return .pui_data[pui]
    endmethod
    
    //-----------------------------------------------------------------------
    //  This will overwrite already attached struct if any
    //-----------------------------------------------------------------------
    static method operator[]= takes unit whichUnit, integer whichData returns nothing
        local integer pui = GetUnitIndex(whichUnit)
        if .pui_data[pui] != 0 then
            call .destroy(.pui_data[pui])
        endif
        set .pui_unit[pui] = whichUnit
        set .pui_data[pui] = whichData
        set .pui_id[whichData] = pui
    endmethod

    //-----------------------------------------------------------------------
    //  If you do not call release struct will be destroyed when unit handle gets recycled
    //-----------------------------------------------------------------------
    method release takes nothing returns nothing
        local integer pui= .pui_id[integer(this)]
        call .destroy()
        set .pui_unit[pui] = null
        set .pui_data[pui] = 0
    endmethod
endmodule


JASS:

//==============================================================================
//  PUI -- Perfect Unit Indexing by Cohadar -- v5.3
//==============================================================================
//
//  PURPOUSE:
//       * Extending UnitUserData()
//       * This is basically perfect hashing algorithm for units
//
//  HOW TO USE:
//       * You have only one function at your disposal GetUnitIndex(unit)
//         It will return a unique index for each unit in range 1..8190
//
//       * What you will do with that index is all up to you
//         Of course using global arrays is the most obvious choice
//         Advanced jassers will think of a couple of more clever ones ofc.
//
//       * There are also 2 textmacros available at the end of library code
//         They can be used for easier attaching to units
//         PUI for structs 
//         PUI_PROPERTY for unit, integer, real, boolean and string variables
//
//  PROS: 
//       * You can use any number of systems that previously could not work together
//         because they all required exclusive access of UnitUserData()
//
//       * You can also use this to attach spell data structs to casters
//
//       * There are no SetUnitIndex() or ClearUnitIndex() functions here
//         Each unit gets assigned one index that cannot be changed
//         That index will be automatically recycled when unit is removed from the game.
//
//  CONS:
//       * This system uses UnitUserData() itself
//         That means that if you want to use PUI you must recode 
//         any other system that uses UnitUserData() to use GetUnitIndex() instead
//
//       * If you use UnitIndex for arrays of non-native types (timers, effects and similar)
//         you must check if timer on that index already exists before you create a new one.
//         This can happen if GetUnitIndex() assigns a recycled index (index of some dead and removed unit)
//         to the newly created unit for which you intended to use timer for
//
//       * All in all this is not a sys for newbies, it gives great power,
//         but it requires knowledge and carefull handling
//
//  DETAILS:
//       * System is using unit array to keep track of all units with an index.
//         Array is periodically checked for removed units,
//         when removed unit is found, index is recycled.
//         Indexes have "decay time" to prevent bugs
//         caused by attaching to "Can't Raise, Does not decay" type units,
//         or by using RemoveUnit() function
//
//  SYSTEM COMMANDS: (debug mode only, red player only)
//
//       * type -pui to display indexes of currently selected units
//       * type -puistats to display some stats
//       * type -puitest to verify that all indexes are valid and unique
//
//  THANKS TO:
//       * Vexorian - for his help with PUI textmacro
//       * Builder Bob - for testing and bugfinding
//       * Joker(Div) - bugfinding
//
//  HOW TO IMPORT:
//       * Just create a trigger named PUI
//       * convert it to text and replace the whole trigger text with this one
//
//==============================================================================


library PUI initializer Init

//==============================================================================
globals    
    //-----------------------------------------------
    private constant real INDEX_DECAY_TIME = 5.  // seconds
    
    //-----------------------------------------------    
    private constant real PERIOD = 0.03125   // 32 fps
        
    //-----------------------------------------------
    private constant integer DECAY_TICKS = R2I(INDEX_DECAY_TIME/PERIOD)
    
    //-----------------------------------------------
    private integer maxIndex = 0
    private integer usedIndexCount = 0
    private integer checker  = 0
    private integer array Indexz
    private unit    array Unitz
    
    //-----------------------------------------------
    private integer freeIndexCount = 0
    private integer array Freez

    //-----------------------------------------------
    private integer decayIndexCount = 0
    private integer decayer  = 0
    private integer array Decayz
    private integer array TimeEndz
    
    private integer tick = 0
endglobals

//==============================================================================
private function PeriodicRecycler takes nothing returns boolean
    set tick = tick + 1
    
    // unit recycler
    if usedIndexCount > 0 then
        set checker = checker + 1
        if checker > usedIndexCount then
            set checker = 1
        endif
        if (GetUnitUserData(Unitz[checker]) == 0) then
            set decayIndexCount = decayIndexCount + 1
            set Decayz[decayIndexCount] = Indexz[checker]
            set TimeEndz[decayIndexCount] = tick + DECAY_TICKS
            
            set Indexz[checker] = Indexz[usedIndexCount]
            set Unitz[checker] = Unitz[usedIndexCount]
            set usedIndexCount = usedIndexCount - 1
        endif
    endif

    // index recycler
    if decayIndexCount > 0 then
        set decayer = decayer + 1
        if decayer > decayIndexCount then
            set decayer = 1
        endif
        if TimeEndz[decayer] <= tick then
            set freeIndexCount = freeIndexCount + 1
            set Freez[freeIndexCount] = Decayz[decayer]

            set Decayz[decayer] = Decayz[decayIndexCount]
            set TimeEndz[decayer] = TimeEndz[decayIndexCount]          
            set decayIndexCount = decayIndexCount - 1
        endif    
    endif
	
    // for debugging 
    //if ModuloInteger(tick, 8) == 0 then
    //    call ClearTextMessages()
    //    call BJDebugMsg("Used/Free/Decaying: " + I2S(usedIndexCount) + "/" + I2S(freeIndexCount) + "/" + I2S(decayIndexCount))
    //endif
    
    return false
endfunction

//==============================================================================
//  Main and only function exported by this library
//==============================================================================
function GetUnitIndex takes unit whichUnit returns integer
    local integer index
    
    debug if whichUnit == null then
    debug   call BJDebugMsg("|c00FF0000ERROR: PUI - Index requested for null unit")
    debug   return 0
    debug endif
    
    set index = GetUnitUserData(whichUnit)

    if index == 0 then
        set usedIndexCount = usedIndexCount + 1

        if freeIndexCount > 0 then
            set Indexz[usedIndexCount] = Freez[freeIndexCount]
            set freeIndexCount = freeIndexCount - 1
        else
            set maxIndex = maxIndex + 1
            set Indexz[usedIndexCount] = maxIndex
        endif
        
        set Unitz[usedIndexCount] = whichUnit
        call SetUnitUserData(whichUnit, Indexz[usedIndexCount])
        set index = GetUnitUserData(whichUnit)
       
        // this happens when requesting unit index for removed unit
        debug if index == 0 then
        debug     call BJDebugMsg("|c00FFCC00WARNING: PUI - Bad unit handle")
        debug endif
    endif
    
    return index
endfunction

//==============================================================================
private function DisplayStats takes nothing returns nothing
    call BJDebugMsg("=============================================")    
    call BJDebugMsg("Biggest index ever = " + I2S(maxIndex))    
    call BJDebugMsg("Indexes in use = " + I2S(usedIndexCount))
    call BJDebugMsg("Decaying indexes = " + I2S(decayIndexCount))
    call BJDebugMsg("Released indexes = " + I2S(freeIndexCount))
    call BJDebugMsg("=============================================")    
endfunction

//===========================================================================
private function DisplaySelectedEnum takes nothing returns nothing
    call BJDebugMsg( "PUI(" + ( GetUnitName(GetEnumUnit()) + ( ") = " + I2S(GetUnitUserData(GetEnumUnit())) ) ) )
endfunction

//===========================================================================
private function DisplaySelected takes nothing returns nothing
    local group g = CreateGroup()
    call SyncSelections()
    call GroupEnumUnitsSelected(g, Player(0), null)
    call ForGroup(g, function DisplaySelectedEnum)
    call DestroyGroup(g)
    set  g = null
endfunction


//==============================================================================
globals
    private integer testCounter = 0
    private integer array TestCountz
endglobals

//==============================================================================
private function Test takes nothing returns nothing
    local integer i
    set testCounter = testCounter + 1
    
    set i = 1
    loop
        exitwhen i > usedIndexCount
        // happens when you abuse UnitUserData outside PUI
        if GetUnitTypeId(Unitz<i>) != 0 and GetUnitUserData(Unitz<i>) != Indexz<i> then
            call BJDebugMsg(&quot;|c00FF0000ERROR: PUI - Invalid index detected&quot;)
            return
        endif
        // if this error happens it means PUI is bugged
        if TestCountz[Indexz<i>] != testCounter then
            set TestCountz[Indexz<i>] = testCounter
        else
            call BJDebugMsg(&quot;|c00FF0000ERROR: PUI - Double index detected&quot;)
            return
        endif
        set i = i + 1
    endloop
    
    call BJDebugMsg(&quot;|c0000ff00OK: PUI - All indexes were valid and unique&quot;)
endfunction

//==============================================================================
private function Init takes nothing returns nothing
    local trigger trig 
    
    set trig = CreateTrigger()
    call TriggerRegisterTimerEvent( trig, PERIOD, true )
    call TriggerAddCondition( trig, Condition(function PeriodicRecycler) )

    debug set trig = CreateTrigger()
    debug call TriggerRegisterPlayerChatEvent( trig, Player(0), &quot;-pui&quot;, true )
    debug call TriggerAddAction( trig, function DisplaySelected )      
    
    debug set trig = CreateTrigger()
    debug call TriggerRegisterPlayerChatEvent( trig, Player(0), &quot;-puistats&quot;, true )
    debug call TriggerAddAction( trig, function DisplayStats )
    
    debug set trig = CreateTrigger()
    debug call TriggerRegisterPlayerChatEvent( trig, Player(0), &quot;-puitest&quot;, true )
    debug call TriggerAddAction( trig, function Test )    
endfunction

endlibrary


//===========================================================================
//  Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
//  Do NOT put handles that need to be destroyed here (timer, trigger, ...)
//  Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
    private static unit   array pui_unit
    private static $TYPE$ array pui_data
    
    //-----------------------------------------------------------------------
    //  Returns default value when first time used
    //-----------------------------------------------------------------------
    static method operator[] takes unit whichUnit returns $TYPE$
        local integer pui = GetUnitIndex(whichUnit)
        if .pui_unit[pui] != whichUnit then
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = $DEFAULT$
        endif
        return .pui_data[pui]
    endmethod
    
    //-----------------------------------------------------------------------
    static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
        local integer pui = GetUnitIndex(whichUnit)
        set .pui_unit[pui] = whichUnit
        set .pui_data[pui] = whichData
    endmethod
endstruct
//! endtextmacro

//===========================================================================
//  Never destroy PUI structs directly.
//  Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
    private static unit    array pui_unit
    private static integer array pui_data
    private static integer array pui_id
    
    //-----------------------------------------------------------------------
    //  Returns zero if no struct is attached to unit
    //-----------------------------------------------------------------------
    static method operator[] takes unit whichUnit returns integer
        local integer pui = GetUnitIndex(whichUnit)
        if .pui_data[pui] != 0 then
            if .pui_unit[pui] != whichUnit then
                // recycled handle detected
                call .destroy(.pui_data[pui])
                set .pui_unit[pui] = null
                set .pui_data[pui] = 0            
            endif
        endif
        return .pui_data[pui]
    endmethod
    
    //-----------------------------------------------------------------------
    //  This will overwrite already attached struct if any
    //-----------------------------------------------------------------------
    static method operator[]= takes unit whichUnit, integer whichData returns nothing
        local integer pui = GetUnitIndex(whichUnit)
        if .pui_data[pui] != 0 then
            call .destroy(.pui_data[pui])
        endif
        set .pui_unit[pui] = whichUnit
        set .pui_data[pui] = whichData
        set .pui_id[whichData] = pui
    endmethod

    //-----------------------------------------------------------------------
    //  If you do not call release struct will be destroyed when unit handle gets recycled
    //-----------------------------------------------------------------------
    method release takes nothing returns nothing
        local integer pui= .pui_id[integer(this)]
        call .destroy()
        set .pui_unit[pui] = null
        set .pui_data[pui] = 0
    endmethod
//! endtextmacro
</i></i></i></i></i>
 

Attachments

Why are those two functions, DisplayTopIndex() and RecycleIndex(), returning boolean values?
 

Cohadar

master of fugue
Because they are Conditions, not Actions.
(Standard trick in advanced vJass coding to increase speed of things)
 
Okay, and shouldn't you null the trigger variable "trig" in the Init function?

Off-Topic:
In you signature in wc3campaigns, DD v1.0 -link leads to PUI thread. :p
 

Cohadar

master of fugue
Yes this is for spellmaking, I already posted 2 spells that use it.
Soon will be 3.
 

hell_knight

Playing WoW
Does that give every single unit an unique custom value ? I am assuming GetUnitIndex(unit) is getting their custom value or something?

Kind of confused. Because if it is I could use this and make my stuff mui
 

Rheias

New Helper (I got over 2000 posts)
Cohadar, I'm not exactly sure I understand how this system works... Do I set the custom value normally, and simply I get it by getting the unit index, or did I miss something?

Thanks for sharing this. :)
 

Cohadar

master of fugue
System sets indexes for you, do NOT set anything yourself, just use GetUnitIndex() instead of GetUnitUserData()
 

Rheias

New Helper (I got over 2000 posts)
It is obivious that I'm missing something, if the system sets indexes for me, how exactly am I supposed to attach a struct to a unit? Ususally what I would have done is:

call SetUnitUserData(whichUnit,whichStruct)
...
set whichStruct = GetUnitUserData(whichUnit)

How would I do that using your system?
 

Cohadar

master of fugue
It is obivious that I'm missing something, if the system sets indexes for me, how exactly am I supposed to attach a struct to a unit? Ususally what I would have done is:

call SetUnitUserData(whichUnit,whichStruct)
...
set whichStruct = GetUnitUserData(whichUnit)

How would I do that using your system?



By looking at the example map or any other spell I posted?
 

hell_knight

Playing WoW
IN GUI custom values is GetUnitUserData(udg_unit) , so I can convert to JASS and replace that with GetUnitIndex(udg_unit)
That correct? Will anything schrew up if I do this?
 

phyrex1an

Staff Member and irregular helper
Staff member
IN GUI custom values is GetUnitUserData(udg_unit) , so I can convert to JASS and replace that with GetUnitIndex(udg_unit)
That correct? Will anything schrew up if I do this?
That will work as long as you doesn't use (eg, change) the custom value of the unit yourself somewhere else in the script. That's not a 'limitation' of GUI but rather of the system itself (and honestly, why use the custom value directly if you are using this?)
 

Rheias

New Helper (I got over 2000 posts)
Alright, I took a look, I think I get it, but from this part.

set ret.PUI[GetUnitUserData(victim)] = ret

I looked and I couldn't find any member in your struct called PUI. Further more, I tried to create a new struct and using PUI in it and it syntaxed me, so I'm a bit lost.
 

Cohadar

master of fugue
Ok I extracted the core of PUI usage so you can see it.
I will also write this:
JASS:

SetTimerStructA ~ static method create
GetTimerStructA ~ static method Get
ClearTimerStructA ~ method onDestroy

Using PUI is inherently more complicated than using ABC simply because unit attaching is complicated than timer or trigger attaching,
with units you have to check for previous attachments to make stuff MUI.

I tried to make it simpler but all simplifications would include use of textmacros and besides if you are going to write spells with unit attaching you better know how to work with structs.
So there is no easy way here, simply learn it and use it.


JASS:

scope YourSpell

//===========================================================================
private struct Data
    unit whichUnit
    private static Data array PUI  //&lt;---------

    //-------------------------------------------------------------------------  
    static method create takes unit whichUnit returns Data
        local Data ret = Data.allocate()
        set ret.whichUnit = whichUnit
        set Data.PUI[GetUnitIndex(whichUnit)] = ret // create unit-&gt;Data link
        return ret
    endmethod
    
    //-------------------------------------------------------------------------  
    static method Get takes unit whichUnit returns Data
        local integer index
    
        set index = GetUnitIndex(whichUnit) // PUI
        if Data.PUI[index] == 0 then
            debug call BJDebugMsg(&quot;|c00FFCC00&quot;+&quot;Data struct created on first use for unit: &quot; + GetUnitName(whichUnit))
            return Data.create(whichUnit)
        endif
        
        return Data.PUI[index]
    endmethod    

    //-------------------------------------------------------------------------  
    method onDestroy takes nothing returns nothing
        set Data.PUI[GetUnitIndex(.whichUnit)] = 0 // break unit-&gt;Data link
    endmethod    
endstruct

endscope
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    They aren't cutting my responsibilities or interjecting in my management abilities or anything, it's just stupid shit that comes up. And they don't exactly have people lining up for my job, there's not that many people applying for the positions I hire
  • Varine Varine:
    Eh, whatever. Thanks for listening guys
  • jonas jonas:
    Sure :) Let us know how it ends
  • Varine Varine:
    All of these things will end happily, they're just stressful. And I still lack many good friends that I can go to, and the ones I can are preoccupied with similar things. Thus general chit chat, cuz for some reason TH and Ghan and Tom all actively keep it up.
  • Varine Varine:
    Just gotta keep Miss Mazie up through the week until her shock wears off and she realizes that she still has family all around her, and bossman will do whatever he's going to do and I'll respond appropriately when it happens. Thank you all for the support, I do very much appreciate everyone being here for me through the years
    +3
  • vypur85 vypur85:
    Best of luck Varine!
  • vypur85 vypur85:
    I just gotten myself an offer to work in China. The pay quadruples my current one. Damn.... Not really ready to start a new life there in China.
  • The Helper The Helper:
    I have heard that they pay pretty good to English teachers in China - you would be an expat
  • jonas jonas:
    Cool, what kind of job?
  • Accname Accname:
    I would be careful with jobs in China. They can be hit and miss depending on where in China you go. Places like hong kong / Shengzen / Beijing can be neat. Other places not so much.
  • Accname Accname:
    I would recommend searching for some first person experiences for the city you got the offer in. Especially now when the political situation in China is deteriorating.
  • jonas jonas:
    Accname, long time no see
  • jonas jonas:
    What have you been up to
  • tom_mai78101 tom_mai78101:
    Hey Accname, welcome back.
  • Accname Accname:
    Not much. Working in the Renewable Energy Sector as an IT Consultant. Its okay, but I think I preferred working at the university. It was more relaxed and you met all kinds of crazy people there.
  • vypur85 vypur85:
    I gotten a teaching position for Biology in a college in Wuhan (yes, there)... I suppose it should be fine there (I hope). Many of my ex colleagues are teaching in China as well currently (none in Wuhan though)
  • vypur85 vypur85:
    And I signed the contract already. I guess there's no turning back....
  • jonas jonas:
    @Accname how many hours do you work? I heard in some sectors IT consultants rack up insane hours
  • jonas jonas:
    @vypur85 sounds nice, have fun : )
  • Accname Accname:
    I am supposed to work 40 hrs a week, but I can work more if I like and I will be paid for those hours (as long as I don't go too far, there are laws and company policies, etc)
  • Accname Accname:
    In practice its basically work as much as you like, as long as the job gets done in time.
  • jonas jonas:
    Haha, my job is like that as well... that usually means I have a few 70-80 hours weeks a year, and lots of 20 hours weeks...
  • jonas jonas:
    a few weeks ago, one of my friends basically said "jonas, I received an invitation to submit something to conference X but I'm too lazy to do it and also the conference isn't advanced enough for my high level of research*, why don't you write something? Oh by the way, the deadline is in two weeks. Enjoy!" so I got two 80 hour weeks out of that kind offer. (*of course he didn't say those parts, but it's a better story this way)
  • jonas jonas:
    now I'll have next week off to make up for overtime :p and I'll play some good old gothic 2
  • The Helper The Helper:
    Hope you are enjoying that gothic 2~

    Members online

    No members online now.

    Affiliates

    Hive Workshop NUON Dome
    Top