| 08-29-2009, 09:06 PM | #1 |
Well, I made a map called 'Stampede Surviver'. In that game you have to build barricades to be safe against stampedes. And I added a sell functionality to all the units. For that I used a transmute-casting dummy. The problem: The player wont get the full upograde costs back, when the unit upgrades. This library solves that problem. Actually I've been working on this for a while, at first I used a transmute dummy and Asynchronizer. But I did more research to create this library. I wanted to do more tests, but now Nestharus wants to get his GetUnitCost library approved, and I want to be a direct concurrency. Requires Table: http://www.wc3c.net/showthread.php?t=101246 Here is the code: JASS:library GetUnitCost initializer Init requires Table, CommonAIimports //=========================================================================== // Information: //============== // // This library allows you to get the cost of units in the following devices: // Gold, Wood, Food, FoodIncrement. Normally this is not possible. This is useful // for example, if you want to make a towerdefense and you want a sell button which gives // 75% of the gold and wood back you payed for it. But when the tower is an upgrade, so the // tower itself costs 100 gold plus 100 gold upgrade, and you'd use a dummy to cast Transmute on // the unit, the player would only get 75% gold back, not the gold for all the upgrades. // If you used this library, you'd be able to give him 150 instead of 75 gold back. // //=========================================================================== // Implementation: //================ // // 1. Make a new trigger, convert it to custom text // and replace all the code in the trigger by this. // 2. Give credits to Mr.Malte (:P) and use the system. //=========================================================================== // Functions: //=========== // // GetUnitCost(unit,type*) : Returns the total gold cost of a unit (upgrades included) // GetUnitTypeCost(unitId,type*) : Returns, how much a unit with ID X would cost. Note: // This ignores upgrade gold costs, so when you just want the cost of // THIS building/unit, you can use GetUnitTypeCost(GetUnitTypeId(#),#) // // * type can be COST_GOLD or COST_LUMBER // //=========================================================================== // Features: //========== // 1. Gets the cost of Units including its previous forms (when upgraded) // 2. Works for units and structures // 3. Uses a smart way to detect gold and wood // 4. Destroys UnitCost structs automatically, even when the units are removed. // //=========================================================================== globals private constant integer PLAYER_ID = 14 private constant integer DUMMY_ID = 'hpea' private constant integer RESOURCE_LIMIT = 100000 private constant boolean CLONE = true endglobals globals private trigger unitEnters = CreateTrigger() private trigger unitDies = CreateTrigger() private trigger unitUpgrades = CreateTrigger() private HandleTable UnitData private Table RawcodeData private real maxX private real maxY private unit dummy = null private player dummyOwner = Player(PLAYER_ID) endglobals // return true: Save the unit's cost. // return false: Don't save the unit's cost. private function UserFilter takes unit u returns boolean return ( GetUnitAbilityLevel(u,'Aloc') == 0 ) endfunction struct UnitCost integer Lumber integer Gold UnitCost Link = 0 method increaseBy takes UnitCost inc returns nothing set .Lumber = .Lumber + inc.Lumber set .Gold = .Gold + inc.Gold call inc.destroy() // And now synchronize the Clone. // Destroying and recreating would change its ID, so.. if CLONE then if .Link != 0 then set .Link.Gold = .Gold set .Link.Lumber = .Lumber endif endif endmethod // We want to give the user the UnitCost struct, but we dont want him to access it // and change its data. So we make a 'Parastruct' method Clone takes nothing returns UnitCost if CLONE then if .Link == 0 then set .Link = UnitCost.create() endif // Always restore the data to be safe. set .Link.Gold = .Gold set .Link.Lumber = .Lumber set .Link.Link = -1 // -1 means: This is just a clone. return .Link else return this endif endmethod method onDestroy takes nothing returns nothing if .Link > 0 then call .Link.destroy() endif endmethod endstruct private function GetUnitCostSimple takes integer ID returns UnitCost local UnitCost u = UnitCost.create() //if RawcodeData.exists(ID) then // return RawcodeData[ID] //endif if IsUnitIdType(ID,UNIT_TYPE_HERO) then call SetPlayerState(dummyOwner, PLAYER_STATE_RESOURCE_GOLD, RESOURCE_LIMIT) call SetPlayerState(dummyOwner, PLAYER_STATE_RESOURCE_LUMBER, RESOURCE_LIMIT) call SetPlayerState(dummyOwner, PLAYER_STATE_RESOURCE_FOOD_USED,0) call AddUnitToStock(dummy, ID, 1, 1) call IssueNeutralImmediateOrderById(dummyOwner, dummy, ID) call RemoveUnitFromStock(dummy,ID) set u.Gold = RESOURCE_LIMIT - GetPlayerState(dummyOwner,PLAYER_STATE_RESOURCE_GOLD) set u.Lumber = RESOURCE_LIMIT - GetPlayerState(dummyOwner,PLAYER_STATE_RESOURCE_LUMBER) else set u.Gold = GetUnitGoldCost(ID) set u.Lumber = GetUnitWoodCost(ID) endif //set RawcodeData[ID] = u return u endfunction // ====================================================================== // ============== USER FUNCTIONS ============== // ====================================================================== globals constant integer COST_GOLD = 0 constant integer COST_LUMBER = 1 endglobals function GetUnitCost takes unit u, integer cost returns integer local UnitCost temp = UnitData[u] if temp == 0 then set temp = GetUnitCostSimple(GetUnitTypeId(u)) set UnitData[u] = temp endif if cost == COST_GOLD then return temp.Gold elseif cost == COST_LUMBER then return temp.Lumber else debug call BJDebugMsg("GetUnitCost: Used undefined cost-type") endif return 0 endfunction function GetUnitTypeCost takes integer unitId, integer cost returns integer local UnitCost temp = GetUnitCostSimple(unitId) if cost == COST_GOLD then return temp.Gold elseif cost == COST_LUMBER then return temp.Lumber else debug call BJDebugMsg("GetUnitCost: Used undefined cost-type") endif return 0 endfunction // ====================================================================== // ====================================================================== // ====================================================================== private function onEnter takes nothing returns nothing local UnitCost uc local unit u = GetTriggerUnit() // Kill dummies. if GetOwningPlayer(u) == dummyOwner then call RemoveUnit(u) return endif // Actually it would make sense to only save the cost to the unit, // when it has been upgraded. But we can't detect the unit type it was // before, because the unit ID already changed when the onUpgrade trigger // fired. if UserFilter(u) then if UnitData.exists(u) == false then set uc = GetUnitCostSimple(GetUnitTypeId(u)) set UnitData[u] = uc endif endif endfunction private function onUpgrade takes nothing returns nothing local UnitCost uc = GetUnitCostSimple(GetUnitTypeId(GetTriggerUnit())) local UnitCost ucPrev = UnitData[GetTriggerUnit()] call ucPrev.increaseBy(uc) endfunction private function Destruct takes unit u returns nothing local UnitCost uc if GetOwningPlayer(u) != dummyOwner then set uc = UnitData[u] if uc != null then call uc.destroy() call UnitData.flush(u) endif endif endfunction private function onDie takes nothing returns nothing call Destruct(GetTriggerUnit()) endfunction hook RemoveUnit Destruct private function returnFlag takes nothing returns boolean return UserFilter(GetFilterUnit()) endfunction private function Init takes nothing returns nothing local integer index = 0 local group g = CreateGroup() // Catch also initial units. local unit u local boolexpr condition = Condition(function returnFlag) local UnitCost uc set UnitData = HandleTable.create() set RawcodeData = Table.create() // ========================================================================== set dummy = CreateUnit(dummyOwner,DUMMY_ID,GetRectMaxX(bj_mapInitialPlayableArea),GetRectMaxY(bj_mapInitialPlayableArea),270) call UnitAddAbility(dummy,'Asud') call UnitAddAbility(dummy,'Aloc') call SetUnitAcquireRange(dummy,0.) call ShowUnit(dummy,false) call SetUnitInvulnerable(dummy,true) // ========================================================================== call SetPlayerState(dummyOwner, PLAYER_STATE_RESOURCE_FOOD_CAP,300) call TriggerRegisterEnterRectSimple(unitEnters,bj_mapInitialPlayableArea) loop call TriggerRegisterPlayerUnitEvent(unitUpgrades, Player(index), EVENT_PLAYER_UNIT_UPGRADE_START, condition) call TriggerRegisterPlayerUnitEvent(unitDies, Player(index), EVENT_PLAYER_UNIT_DEATH, condition) set index = index + 1 exitwhen index == bj_MAX_PLAYER_SLOTS endloop call TriggerAddAction(unitEnters,function onEnter) call TriggerAddAction(unitUpgrades,function onUpgrade) call TriggerAddAction(unitDies,function onDie) // ========================================================================== call GroupEnumUnitsInRect(g,bj_mapInitialPlayableArea,condition) loop set u = FirstOfGroup(g) exitwhen u == null set uc = GetUnitCostSimple(GetUnitTypeId(u)) set UnitData[u] = uc call GroupRemoveUnit(g,u) endloop call DestroyBoolExpr(condition) call DestroyGroup(g) endfunction endlibrary + requires JASS:library CommonAIimports native GetUnitGoldCost takes integer unitid returns integer native GetUnitWoodCost takes integer unitid returns integer native GetUnitBuildTime takes integer unitid returns integer endlibrary constant native GetFoodMade takes integer unitId returns integer constant native GetFoodUsed takes integer unitId returns integer constant native GetUnitFoodMade takes unit whichUnit returns integer constant native GetUnitFoodUsed takes unit whichUnit returns integer This requires Table by Vexorian! SellUnit: JASS:library SellUnit initializer Init requires GetUnitCost //=========================================================================== // Information: //============== // // This library gives you a function that allows you to Sell Units to their owner. // When you let a dummy cast transmute onto the unit, you've got the problem that // only the costs for THIS unit are cought, so when I've got a unit that costs // 200 gold and upgrade it for 100 gold, and I want to refund 75% of the cost // to the owner, He'd only get 75 gold. // With this, he'd get the full 225 gold. // //=========================================================================== globals private sound GoldAdded private sound LumberAdded endglobals function SellUnit takes unit who, real refundPercentage returns nothing local texttag t = CreateTextTag() local integer GoldCost = R2I(I2R(GetUnitCost(who,COST_GOLD))*refundPercentage) local integer LumberCost = R2I(I2R(GetUnitCost(who,COST_LUMBER))*refundPercentage) local real x = GetUnitX(who) local real y = GetUnitY(who) call RemoveUnit(who) call SetPlayerState(GetOwningPlayer(who),PLAYER_STATE_RESOURCE_GOLD,GetPlayerState(GetOwningPlayer(who),PLAYER_STATE_RESOURCE_GOLD)+GoldCost) call SetPlayerState(GetOwningPlayer(who),PLAYER_STATE_RESOURCE_LUMBER,GetPlayerState(GetOwningPlayer(who),PLAYER_STATE_RESOURCE_LUMBER)+LumberCost) if GoldCost > 0 then call DestroyEffect(AddSpecialEffect("UI\\Feedback\\GoldCredit\\GoldCredit.mdl",x,y)) call SetSoundPosition(GoldAdded,x,y,100.) call SetSoundVolume(GoldAdded, 127) call StartSound(GoldAdded) call SetTextTagText(t, "+"+I2S(GoldCost), 0.023) call SetTextTagPos(t, x,y+30.,0.04) call SetTextTagColor(t, 255, 204, 51, 255) call SetTextTagVelocityBJ(t,64.,90.) call SetTextTagVisibility(t, (GetLocalPlayer() == GetOwningPlayer(who))) call SetTextTagFadepoint(t, 2) call SetTextTagLifespan(t, 2.5) call SetTextTagPermanent(t, false) endif if LumberCost > 0 then set t = CreateTextTag() if GoldCost <= 0 then call SetSoundPosition(LumberAdded, x,y,100) call SetSoundVolume(LumberAdded, 127) call StartSound(LumberAdded) endif call SetTextTagText(t, "|cff006C00+"+I2S(LumberCost)+"|r", 0.023) call SetTextTagPos(t, x,y,0.04) call SetTextTagVelocityBJ(t,64.,90.) call SetTextTagVisibility(t, (GetLocalPlayer() == GetOwningPlayer(who))) call SetTextTagFadepoint(t, 2) call SetTextTagLifespan(t, 2.5) call SetTextTagPermanent(t, false) endif set t = null endfunction private function Init takes nothing returns nothing set GoldAdded = CreateSound("Abilities\\Spells\\Other\\Transmute\\AlchemistTransmuteDeath1.wav" , false , true , true , 10 , 10 , "CombatSoundsEAX") call SetSoundParamsFromLabel(GoldAdded , "TransmuteMissileImpact") call SetSoundDuration(GoldAdded , 1601) set LumberAdded = CreateSound("Abilities\\Spells\\Items\\ResourceItems\\BundleOfLumber.wav" , false , true , true , 10 , 10 , "SpellsEAX") call SetSoundParamsFromLabel(LumberAdded , "ReceiveLumber") call SetSoundDuration(LumberAdded , 1347) endfunction endlibrary |
| 08-30-2009, 01:58 AM | #2 |
JASS:library CommonAIimports native GetUnitGoldCost takes integer unitid returns integer native GetUnitWoodCost takes integer unitid returns integer native GetUnitBuildTime takes integer unitid returns integer endlibrary constant native GetFoodMade takes integer unitId returns integer constant native GetFoodUsed takes integer unitId returns integer constant native GetUnitFoodMade takes unit whichUnit returns integer constant native GetUnitFoodUsed takes unit whichUnit returns integer |
| 08-30-2009, 06:37 AM | #3 |
Well, i can do Something against the name conflict. And those functions return very strange things sometimes AND I explained, why I made this system and what makes it useful/ better than the natives; they don't care about upgrades. But that makes my foodcost detection useless, I'll change it later. |
| 08-30-2009, 06:05 PM | #4 |
Ok, and here is an additional thing that goes with this library: JASS:library SellUnit initializer Init requires GetUnitCost //=========================================================================== // Information: //============== // // This library gives you a function that allows you to Sell Units to their owner. // When you let a dummy cast transmute onto the unit, you've got the problem that // only the costs for THIS unit are cought, so when I've got a unit that costs // 200 gold and upgrade it for 100 gold, and I want to refund 75% of the cost // to the owner, He'd only get 75 gold. // With this, he'd get the full 225 gold. // //=========================================================================== globals private constant real RESOURCE_REFUND_PERCENTAGE = 0.75 endglobals globals private sound GoldAdded private sound LumberAdded endglobals function SellUnit takes unit who returns nothing local texttag t = CreateTextTag() local integer GoldCost = R2I(I2R(GetUnitGoldCost(who))*RESOURCE_REFUND_PERCENTAGE) local integer LumberCost = R2I(I2R(GetUnitLumberCost(who))*RESOURCE_REFUND_PERCENTAGE) local real x = GetUnitX(who) local real y = GetUnitY(who) call RemoveUnit(who) call SetPlayerState(GetOwningPlayer(who),PLAYER_STATE_RESOURCE_GOLD,GetPlayerState(GetOwningPlayer(who),PLAYER_STATE_RESOURCE_GOLD)+GoldCost) call SetPlayerState(GetOwningPlayer(who),PLAYER_STATE_RESOURCE_LUMBER,GetPlayerState(GetOwningPlayer(who),PLAYER_STATE_RESOURCE_LUMBER)+LumberCost) if GoldCost > 0 then call DestroyEffect(AddSpecialEffect("UI\\Feedback\\GoldCredit\\GoldCredit.mdl",x,y)) call SetSoundPosition(GoldAdded,x,y,100.) call SetSoundVolume(GoldAdded, 127) call StartSound(GoldAdded) call SetTextTagText(t, "+"+I2S(GoldCost), 0.023) call SetTextTagPos(t, x,y+30.,0.04) call SetTextTagColor(t, 255, 204, 51, 255) call SetTextTagVelocityBJ(t,64.,90.) call SetTextTagVisibility(t, (GetLocalPlayer() == GetOwningPlayer(who))) call SetTextTagFadepoint(t, 2) call SetTextTagLifespan(t, 2.5) call SetTextTagPermanent(t, false) endif if LumberCost > 0 then set t = CreateTextTag() call SetSoundPosition(LumberAdded, x,y,100) call SetSoundVolume(LumberAdded, 127) call StartSound(LumberAdded) call SetTextTagText(t, "|cff006C00+"+I2S(LumberCost)+"|r", 0.023) call SetTextTagPos(t, x,y,0.04) call SetTextTagVelocityBJ(t,64.,90.) call SetTextTagVisibility(t, (GetLocalPlayer() == GetOwningPlayer(who))) call SetTextTagFadepoint(t, 2) call SetTextTagLifespan(t, 2.5) call SetTextTagPermanent(t, false) endif set t = null endfunction private function Init takes nothing returns nothing set GoldAdded = CreateSound("Abilities\\Spells\\Other\\Transmute\\AlchemistTransmuteDeath1.wav" , false , true , true , 10 , 10 , "CombatSoundsEAX") call SetSoundParamsFromLabel(GoldAdded , "TransmuteMissileImpact") call SetSoundDuration(GoldAdded , 1601) set LumberAdded = CreateSound("Abilities\\Spells\\Items\\ResourceItems\\BundleOfLumber.wav" , false , true , true , 10 , 10 , "SpellsEAX") call SetSoundParamsFromLabel(LumberAdded , "ReceiveLumber") call SetSoundDuration(LumberAdded , 1347) endfunction endlibrary |
| 08-30-2009, 06:35 PM | #5 |
I think Deaod was trying to ask, why use this instead of the cost natives? |
| 08-30-2009, 07:39 PM | #6 |
Nah, its just that he could have used those natives internally. I know his library does something which those natives dont (adding up the upgrade costs). Still, i think you could simplify your code by using these natives. |
| 08-30-2009, 07:58 PM | #7 |
Hmm, but I don't have access to them. Do I have to import them somehow? |
| 08-31-2009, 04:43 PM | #8 |
Ok, I can use the natives now. But the problem is; They don't work with heroes. So why should I use them instead of this? |
| 09-01-2009, 01:47 AM | #9 |
If unit type is not hero, then use native? if unit type is hero, then don't use native? |
| 09-02-2009, 08:34 PM | #10 |
Ok. updated. Changed all the complained things. |
| 09-04-2009, 02:19 PM | #11 |
Update. Now you can decide for which unit the gold cost function (including upgrades) shall be available. For example you can set it to 'no dummies' The filter is found in line 67. |
| 09-04-2009, 06:37 PM | #12 |
|
| 09-04-2009, 08:01 PM | #13 | |||
First of all: Thanks for reviewing this :) Quote:
Ok. I don't know, hoiw object mergers work, but Ill find out. Quote:
I disagree with this API. includeUpgrade should always be true, when you use these functions. Otherwise you could use GetUnitTypeGoldCost(GetUnitTypeId(u)). Also, I can't name a function GetUnitGoldCost, because it would come to a name conflict with the natives. I will keep it as GetUnitTotalGoldCost, because it is always total (with the upgrades) Quote:
That's fine. ´The rest is fine, too. I'll upload the new version today or tomorrow. |
| 09-04-2009, 08:15 PM | #14 | ||
Quote:
Quote:
JASS:globals constant integer COST_GOLD = 1 constant integer COST_LUMBER = 2 endglobals function GetUnitCost takes unit u, integer costType, boolean includeUpgrades returns integer function GetUnitTypeCost takes integer unitType, integer costType returns integer |
| 09-04-2009, 09:03 PM | #15 |
If you only want the upgrade cost, you can do this: JASS:GetUnitTypeCost(GetUnitTypeId(UNIT)) This would be GetUnitCost, but just return the cost of this thing. Because GetUnitTypeCost does always ignore upgrades. I updated the code. I did not only change the things you complained, I made various changes. |
