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

Refund System

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
This system provides a demo map with a few buildings set up to be refunded.

Any unit you give the Refund Ability (user must create one for their map, or copy the one from the template) to will return x% of its gold and lumber initial cost instantly upon cast.

It will also display a user specified special effect on the building being refunded, as well as two floating text tags for the resources gained back.

This system does not provide support for multiple upgrades. It will only get the cost the of the building it is added to.

JASS:
scope Refund initializer init
   
    native GetUnitGoldCost takes integer id returns integer
    native GetUnitWoodCost takes integer id returns integer
   
    globals
        private constant real REFUND_RETURN_RATE_GOLD = .50 // Set the percentage return rate
        private constant real REFUND_RETURN_RATE_WOOD = .50 // Set the percentage return rate
        private constant real HITPOINTS_REQUIRED = 100 // Set the required life percentage of the building you are refunding
        private constant string STRING_MSG = "|cff808080This building is hurt and may not be refunded." // Set the failure message for the player
        private constant boolean WANT_HP_CHECK = true // If you want to check for hitpoints, set this to true. If you do not want to check hitpoints, set it to false.
        private constant string SFX_MODEL = "UI\\Feedback\\GoldCredit\\GoldCredit.mdl" // Special effect model
        private constant integer REFUND_ABIL_ID = 'A000' // Refund ability raw code
    endglobals
   
    private function main takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local integer id = GetUnitTypeId(u)
        local texttag tg
        local texttag tl
        local integer goldAmount
        local integer woodAmount
           
        if (GetSpellAbilityId() != REFUND_ABIL_ID) then
            set u = null
            return
        endif
        if GetUnitLifePercent(u) == HITPOINTS_REQUIRED or WANT_HP_CHECK == false then
            set tg = CreateTextTag()
            set tl = CreateTextTag()
            set goldAmount = R2I(GetUnitGoldCost(id) * REFUND_RETURN_RATE_GOLD)
            set woodAmount = R2I(GetUnitWoodCost(id) * REFUND_RETURN_RATE_WOOD)
            call SetPlayerState(GetOwningPlayer(u), PLAYER_STATE_RESOURCE_GOLD, (GetPlayerState(GetOwningPlayer(u), PLAYER_STATE_RESOURCE_GOLD) + goldAmount))
            call SetPlayerState(GetOwningPlayer(u), PLAYER_STATE_RESOURCE_LUMBER, (GetPlayerState(GetOwningPlayer(u), PLAYER_STATE_RESOURCE_LUMBER) + woodAmount))
            call KillUnit(u)
            call DestroyEffect(AddSpecialEffectTarget(SFX_MODEL, u, "overhead"))
            // Gold Text Tag
            call SetTextTagText(tg, "(+" + I2S(goldAmount) + ")", 0.020)
            call SetTextTagPos(tg, GetUnitX(u), GetUnitY(u), 0.00)
            call SetTextTagColor(tg, 255, 220, 0, 255)
            call SetTextTagVelocity(tg, 0, .02)
            call SetTextTagVisibility(tg, true)
            call SetTextTagFadepoint(tg, 0.10)
            call SetTextTagLifespan(tg, 1.5)
            call SetTextTagPermanent(tg, false)
            // Lumber Text Tag
            call SetTextTagText(tl, "(+" + I2S(woodAmount)+ ")", 0.020)
            call SetTextTagPos(tl, GetUnitX(u), GetUnitY(u) - 50, 0.00)
            call SetTextTagColor(tl, 0, 255, 0, 0)
            call SetTextTagVelocity(tl, 0, - 0.02)
            call SetTextTagVisibility(tl, true)
            call SetTextTagFadepoint(tl, 0.10)
            call SetTextTagLifespan(tl, 1.5)
            call SetTextTagPermanent(tl, false)
        else
            call DisplayTextToPlayer(GetOwningPlayer(u), 0, 0, STRING_MSG)
        endif
        set tg = null
        set tl = null
        set u = null
    endfunction
           
    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition(function main))
        set t = null
    endfunction
endscope
Contents

Refund System (Map)

Reviews
MyPad
Looks rather simple, but probably useful. Needless to say, I'll delve into the code. Phew, this one was relatively lightweight. I think it may come in handy to some. Useful
Cool, never thought this can be something of a big idea.

Just some stuff I found:

1. This code:
JASS:
call AddSpecialEffectTargetUnitBJ("overhead", GetTriggerUnit(), "UI\\Feedback\\GoldCredit\\GoldCredit.mdl")
call DestroyEffectBJ(GetLastCreatedEffectBJ())
can be optimized into:
JASS:
call DestroyEffectBJ(AddSpecialEffectTargetUnitBJ("overhead", GetTriggerUnit(), "UI\\Feedback\\GoldCredit\\GoldCredit.mdl"))

2. Special effect SFX should be in configuration.
 
Cool, never thought this can be something of a big idea.

Just some stuff I found:

1. This code:
JASS:
call AddSpecialEffectTargetUnitBJ("overhead", GetTriggerUnit(), "UI\\Feedback\\GoldCredit\\GoldCredit.mdl")
call DestroyEffectBJ(GetLastCreatedEffectBJ())
can be optimized into:
JASS:
call DestroyEffectBJ(AddSpecialEffectTargetUnitBJ("overhead", GetTriggerUnit(), "UI\\Feedback\\GoldCredit\\GoldCredit.mdl"))

2. Special effect SFX should be in configuration.


Hi daffa, thank you for your suggestions. Coding is not my forte :grin:
 
Looks rather simple, but probably useful. Needless to say, I'll delve into the code.

  • This is more on personal taste, but I think the native declarations should be moved inside the library using them.

  • You can make the function return early so that you don't have to deal with nulling the text tags when they don't exist yet.
    A sample of the code rewrite is shown below:

    Code:
    if (GetSpellAbilityId() != REFUND_ABIL_ID) then
        set u = null
        return
    endif
    // ===========
    // Rest of the code.
    // ===========
    set tg = null
    set tl = null
    set u = null

  • You can use SetTextTagPosUnit(texttag, unit, real zOffset) as an alternative to SetTextTagPos.

  • While the bundle is called Refund System, the underlying code appears to be closely tied to an ability, making this more of a spell in my book. Hence, you may choose to keep it a library or go with scopes (For spells, I prefer scopes for encapsulation).

Phew, this one was relatively lightweight. I think it may come in handy to some.

Useful
 
Looks rather simple, but probably useful. Needless to say, I'll delve into the code.

  • This is more on personal taste, but I think the native declarations should be moved inside the library using them.

  • You can make the function return early so that you don't have to deal with nulling the text tags when they don't exist yet.
    A sample of the code rewrite is shown below:

    Code:
    if (GetSpellAbilityId() != REFUND_ABIL_ID) then
        set u = null
        return
    endif
    // ===========
    // Rest of the code.
    // ===========
    set tg = null
    set tl = null
    set u = null

  • You can use SetTextTagPosUnit(texttag, unit, real zOffset) as an alternative to SetTextTagPos.

  • While the bundle is called Refund System, the underlying code appears to be closely tied to an ability, making this more of a spell in my book. Hence, you may choose to keep it a library or go with scopes (For spells, I prefer scopes for encapsulation).

Phew, this one was relatively lightweight. I think it may come in handy to some.

Useful
Hello pad,

thank you for the review. due to a request, i have updated the system to include these changes (except for the SetTextTag change, because I am using an offset) as well as included new configurables to check for required HP amount to refund. Please let me know if you'd like to see any other changes made! :grin:
 
Level 39
Joined
Feb 27, 2007
Messages
5,013
Error in this line: GetUnitLifePercent(u) == HITPOINTS_REQUIRED, should be >= otherwise this check will literally always fail (because you're comparing if a real is exactly equal to another real and good fucking luck with that and floating point precision) if HITPOINTS_REQUIRED is not 100.

Could use a small amount of logic to round non-integer gold/lumber refund remainders. Right now the R2I() call forces it to always truncate and never round. It would only change 1 gold/lumber either way but it might matter if the map this is used in has very low gold/lumber costs for units.

Also there are 3 values that you are recomputing multiple times in the main function, which could simply be computed once each and stored in some locals (or a global if you really don't want to null things): GetUnitX(u), GetUnitY(u), and GetOwningPlayer(u). In PLAYER_UNIT_EVENT_ events you can actually use GetTriggerPlayer() which is the same as GetOwningPlayer(GetTriggerUnit()) with one less call.
 
Top