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

Doppelganger v1.5

Spell Name: Doppelganger v1.5

This map was inpired by UnMi. He created the Mirror of Truth spell in the hall of fame, but for some reasons, I cant open his map, maybe patch 1.24 is not done yet at that time so I decided to make this.

Full Description:
Summons a mirror portal that reveals the inner fear of an enemy unit.
An enemy unit that goes near to the portal will be forced to sleep and create it's doppelganger.
The doppelganger deals 2 times damage, but takes 2 times more damage from enemy attacks, and will disappear after 20 seconds or when its hit points is zero.

|cffffcc00Level 1|r - Portal lasts 20 seconds
|cffffcc00Level 2|r - Portal lasts 35 seconds
|cffffcc00Level 3|r - Portal lasts 50 seconds
|cffffcc00Level 4|r - Portal lasts 65 seconds
|cffffcc00Level 5|r - Portal lasts 80 seconds


JASS:
//Spell Name: Doppelganger v1.5
//Created by: Mckill2009

//===HOW TO USE:
//- Make a new trigger and convert to custom text via EDIT >>> CONVERT CUSTOM TEXT
//- The trigger name MUST be >>> Doppelganger (see the name below)
//- Copy ALL that is written here and overwrite the existing texts in the custom text
//- Copy the Dummy/custom abilities/buffs etc... to your object editor
//- Make sure you inputed the correct raw codes of the base spell/buffs/dummy etc...
//- If your raw code is different, you MUST CHANGE IT...
//- You can view the raw codes by pressing CTRL+D in the object editor
//- Examples of raw codes are 'A000', 'h000' etc... 

//===REQUIRED VARIABLES:
// DPL_Hash = hashtable
// DPL_DPL_Group = group
// DPL_Timer = timer
// DPL_Portal = unit
// DPL_Counter = integer

//==========CONFIGURABLES==========
//===IMPORTANT NOTICE: Raw codes MUST BE CHANGED if your raw code has changed as indicated below
function DPL_SpellId takes nothing returns integer
    return 'A000' //the main spell
endfunction

function DPL_SleepId takes nothing returns integer
    return 'A002' //the portal will cast this spell to enemy units
endfunction

function DPL_ImageCasterId takes nothing returns integer
    return 'H001' //a dummy unit hero, this is the caster of the image
endfunction

function DPL_PortalId takes nothing returns integer
    return 'h000' //raw code of the portal unit, it is recommended to use the Shimmering Portal
endfunction

function DPL_ItemId takes nothing returns integer
    return 'I000' //this is an item which is given to the dummy hero
endfunction

function DPL_GetDuration takes integer i returns real
    return 10 + i * 15. //the life of the portal
endfunction

constant function DPL_TimerInterval takes nothing returns real
    return 0.1 //dont change
endfunction

constant function DPL_AoE takes nothing returns real
    return 400. //the reach of the portal casting the sleep
endfunction

//==========END OF CONFIGURABLES==========

function DPL_LoopAction takes nothing returns nothing
    local unit portal = GetEnumUnit() 
    local unit first
    local integer portalID = GetHandleId(portal) 
    local real maxduration = LoadReal(udg_DPL_Hash, portalID, 1)
    local real mindur = LoadReal(udg_DPL_Hash, portalID, 2)
    if maxduration > 0 then
        call SaveReal(udg_DPL_Hash, portalID, 1, maxduration - DPL_TimerInterval())
        call SaveReal(udg_DPL_Hash, portalID, 2, mindur + DPL_TimerInterval())
        if mindur >= 9 and (LoadInteger(udg_DPL_Hash, portalID, 3)==0) then
            //setting the saved integer to 1 in order for this not to run again
            //otherwise the add ability will always run
            call SaveInteger(udg_DPL_Hash, portalID, 3, 1) 
            call UnitAddAbility(portal, DPL_SleepId())    
        elseif mindur > 9 then //it takes 9 seconds for the portal to animate it's birth
            call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(portal), GetUnitY(portal), DPL_AoE(), null)
            loop
                set first = FirstOfGroup(bj_lastCreatedGroup)
                exitwhen first==null
                if IsUnitEnemy(portal, GetOwningPlayer(first)) and not IsUnitType(first, UNIT_TYPE_STRUCTURE) and not IsUnitType(first, UNIT_TYPE_SLEEPING) and not IsUnitType(first, UNIT_TYPE_DEAD) then
                    call IssueTargetOrder(portal, "sleep", first) 
                endif  
                call GroupRemoveUnit(bj_lastCreatedGroup, first)
            endloop
        endif
    else
        call KillUnit(portal)
        call FlushChildHashtable(udg_DPL_Hash, portalID)
        call GroupRemoveUnit(udg_DPL_Group, portal)
        set udg_DPL_Counter = udg_DPL_Counter - 1
        if udg_DPL_Counter == 0 then
            call PauseTimer(udg_DPL_Timer)
        endif
    endif
    set portal = null
endfunction

function DPL_CastOn takes unit u, integer spellId, integer portalId returns nothing
    local real facing = GetUnitFacing(u) * bj_DEGTORAD
    local real x = GetUnitX(u)+100*Cos(facing)
    local real y = GetUnitY(u)+100*Sin(facing)
    local integer portalID
    local integer level = GetUnitAbilityLevel(u, spellId)
    local unit portal = CreateUnit(GetTriggerPlayer(), portalId, x, y, facing)
    set portalID = GetHandleId(portal)
    call SetUnitAnimation(portal, "birth")
    call SaveReal(udg_DPL_Hash, portalID, 1, DPL_GetDuration(level))
    call SaveReal(udg_DPL_Hash, portalID, 2, 0) //mindur
    call SaveInteger(udg_DPL_Hash, portalID, 3, 0) //this is used for checking only
    call GroupAddUnit(udg_DPL_Group, portal)
    set portal = null
endfunction

function DPL_GroupLooper takes nothing returns nothing
    call ForGroup(udg_DPL_Group, function DPL_LoopAction)
endfunction

function DPL_CastCond takes nothing returns boolean
    local unit u
    local unit imagecaster
    if GetSpellAbilityId()==DPL_SpellId() then 
        if udg_DPL_Counter == 0 then
            call TimerStart(udg_DPL_Timer, DPL_TimerInterval(), true, function DPL_GroupLooper)
        endif
        set udg_DPL_Counter = udg_DPL_Counter + 1
        call DPL_CastOn(GetTriggerUnit(), DPL_SpellId(), DPL_PortalId())        
    elseif GetSpellAbilityId()==DPL_SleepId() then 
        set u = GetSpellTargetUnit()
        set imagecaster = CreateUnit(GetTriggerPlayer(), DPL_ImageCasterId(), GetUnitX(u), GetUnitY(u), 0)
        call UnitApplyTimedLife(imagecaster, 'BTLF', 1.0) 
        call UnitUseItemTarget(imagecaster, UnitAddItemById(imagecaster, DPL_ItemId()), u)
        set u = null
        set imagecaster = null
    endif
    return false
endfunction                               

function InitTrig_Doppelganger takes nothing returns nothing
    local trigger t = CreateTrigger() 
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, function DPL_CastCond)
    set t = null
    set udg_DPL_Hash = InitHashtable() //hashtable
    //Preloading
    set bj_lastCreatedUnit = CreateUnit(Player(15), DPL_ImageCasterId(),0,0,0)
    call UnitAddAbility(bj_lastCreatedUnit, DPL_SleepId())
    call RemoveUnit(bj_lastCreatedUnit)        
endfunction



v1.5
- Code fully optimized as per Bribe's suggestion.

v1.4
- Filter Units replaced by ForGroup loop
- Buff removed coz its useless
- Global configuration gets back to function

v1.3
- Timer replaced by a global loops and group
- Function setup replaced by global variable setup
- Code reduced and more readable
- Descrition changed

v1.1
- Converted to JASS
- Description changed
- Sleep ability added
- Proper anmation for portal (Birth and Death)

v1.0
- Spell Optimized
- Triggers reduced from 4 to 3
- Dummies reduced from 3 to 2
- Portal animation has exact timing

v0.2
- Leaks cleared
- Clearing hashtables optimized


Keywords:
illusion, image, mirror, blademaster, portal, dummy, epic, system, target, jass, vjass
Contents

Doppelganger v1.5 (Map)

Reviews
21 Feb 2012 Bribe: Great configurables and easy to use/implement. Always nice to see mckill2009 resources because you always keep it readable as well. Approved 4/5 (Recommended).

Moderator

M

Moderator

21 Feb 2012
Bribe: Great configurables and easy to use/implement. Always nice to see mckill2009 resources because you always keep it readable as well.

Approved 4/5 (Recommended).
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
I like this spell, the idea is somewhat original.


Remove the + 0.00
  • Unit - Create 1 Dummy (portal caster) for (Owner of DP_Caster) at DP_CasterLoc facing ((Facing of DP_Caster) + 0.00) degrees
There's no need for this:
  • Hashtable - Save Handle Of(Triggering unit) as (Key dummy) of (Key (Triggering unit)) in Hash
and change this to picked unit:
  • Set DP_Mirror = (Load (Key dummy) of (Key (Picked unit)) in Hash)
This
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (DP_Grp is empty) Equal to True
    • Then - Actions
      • Trigger - Turn off (This trigger)
    • Else - Actions
should be right after this:
  • Unit Group - Remove DP_Mirror from DP_Grp
Leak:
  • Unit - Create 1 Dummy (illusion caster) for (Owner of (Triggering unit)) at (Position of (Target unit of ability being cast)) facing (Position of (Target unit of ability being cast))
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
thanks guys for your positive comments...and yes I think the image is dispellable coz its based on mirror image, about the portal, no coz its a dummy...

Yeah I've noticed the leak, just forgot to make a variable for that...Im gonna fix this in the next update...

btw like I mentioned above, this spell is inpired by UnMi's Mirror of Truth spell...
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
First, nice screenshot! The spell looks really cool.

I have to be a buzz kill. Custom value of units--- replace that with a hashtable.

(Key (Picked unit)) involves three function calls GetHandleIdBJ(GetEnumUnit())) which can be heavily optimized by stoing (Key (Picked unit)) into an integer variable before re-referencing it.

Looks quite nice!
 
Level 9
Joined
Dec 3, 2010
Messages
162
Just a few things.

Firstly, in your DPL_CAST, you forgot to null your timer. You should.

JASS:
function DPL_SCOPETHEM takes nothing returns boolean
    local unit u = GetFilterUnit()
    local boolean b = IsUnitEnemy(udg_DPL_Portal, GetOwningPlayer(u)) and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false and GetWidgetLife(u) > 0.405 and GetUnitAbilityLevel(u, DPL_BUFFID())==0 and udg_DPL_Count<=2
    if b==true then
        set udg_DPL_Count = udg_DPL_Count + 1
        call IssueTargetOrder(udg_DPL_Portal, "sleep", u) 
    endif
    set u =null
    return false          
endfunction

Why do you use a boolean? You can just directly check the conditions in the if statement. That's one less variable.

JASS:
IsUnitType(u, UNIT_TYPE_STRUCTURE)==false // instead of doing this

not IsUnitType(u, UNIT_TYPE_STRUCTURE) // you can do this

Other than that, looks pretty good code-wise.
 
Top