| 08-03-2009, 05:32 AM | #1 |
Ok so I'm pretty green to the whole JASS and vJass stuff so I started by taking a system that i made initially using GUI and converting it into what i think* is it's Jass equivalent, and integrating it to use Rising Dusk's Intuitive damage detection System. The problem is, I'm not sure where I'm doing pointless actions and where i may be leaking, if at all. So without further ado, this is what I've got: JASS:scope DamageDisplaySystem initializer Init globals //Just color modifiers for each player colour so that the FT color matches player color private real array RED[11] private real array BLUE[11] private real array GREEN[11] //other arrays needed to allocate information and update slots as FTs are created/destroyed. private constant integer FT_MAX = 50 // Maximum number of floating texts that will be created per player. Can be scaled between values of 1-100 as needed. For most maps it could be left at about 20-25. private constant real StackTimer = 0.25 // Maximum time interval between damage events in which the floating text on the unit will update before it is displayed and destroyed. private constant real PeriodicCheck = 0.025 // this can be modified as needed private group array DMG_PLAYER_GROUP[11] // Organizes units damaged by player private integer array DMG_PLAYER_GROUP_INT[11] private unit array DMG_PLAYER_GROUP_UNIT[FT_MAX] private real array DMG_PLAYER_GROUP_DMG[FT_MAX] private texttag array DMG_PLAYER_GROUP_FT[FT_MAX] private timer array DMG_PLAYER_GROUP_TIMER[FT_MAX] private timer FT_Timer = CreateTimer() endglobals private function H2I takes handle h returns integer return h return 0 endfunction function GetUnitZtrue takes unit u returns real return (GetUnitFlyHeight(u)+ GetLocationZ(Location(GetUnitX(u),GetUnitY(u)))) endfunction function GetUnitZ takes unit u returns real return (GetUnitFlyHeight(u)) endfunction function SetFT takes texttag Whichtag, string WhichString, integer DisplayPlayerID, real x, real y, real z, real RED, real BLUE, real GREEN, real ALPHA, real Xvel, real Yvel, real FadeTime, boolean Permanence, real size returns nothing call SetTextTagText(Whichtag, WhichString, size * 0.023 / 10) call SetTextTagPos(Whichtag, x, y, z+50.) call SetTextTagColorBJ(Whichtag, RED, GREEN, BLUE, ALPHA) call SetTextTagVelocity(Whichtag, Xvel, Yvel) if GetLocalPlayer() == Player(DisplayPlayerID) and IsMaskedToPlayer(x,y,Player(DisplayPlayerID))==false and IsFoggedToPlayer(x,y,Player(DisplayPlayerID))==false then call SetTextTagVisibility(Whichtag, true) else call SetTextTagVisibility(Whichtag, false) endif call SetTextTagPermanent(Whichtag, Permanence) call SetTextTagSuspended(Whichtag,Permanence) if (Permanence == false) then call SetTextTagFadepoint(Whichtag, FadeTime*2/3) call SetTextTagLifespan(Whichtag, FadeTime) endif endfunction function QuickFT takes string WhichString, integer DisplayPlayerID, real x, real y, real z, real RED, real BLUE, real GREEN, real ALPHA, real Xvel, real Yvel, real FadeTime, boolean Permanence, real size returns texttag set bj_lastCreatedTextTag = CreateTextTag() call SetTextTagText(bj_lastCreatedTextTag, WhichString, size * 0.023 / 10) call SetTextTagPos(bj_lastCreatedTextTag, x, y, z+50.) call SetTextTagColorBJ(bj_lastCreatedTextTag, RED, GREEN, BLUE, ALPHA) call SetTextTagVelocity(bj_lastCreatedTextTag, Xvel, Yvel) if GetLocalPlayer() == Player(DisplayPlayerID) and IsMaskedToPlayer(x,y,Player(DisplayPlayerID))==false and IsFoggedToPlayer(x,y,Player(DisplayPlayerID))==false then call SetTextTagVisibility(bj_lastCreatedTextTag,true) else call SetTextTagVisibility(bj_lastCreatedTextTag, false) endif call SetTextTagPermanent(bj_lastCreatedTextTag, Permanence) call SetTextTagSuspended(bj_lastCreatedTextTag,Permanence) if (Permanence == false) then call SetTextTagFadepoint(bj_lastCreatedTextTag, FadeTime*2/3) call SetTextTagLifespan(bj_lastCreatedTextTag, FadeTime) endif return bj_lastCreatedTextTag endfunction function SetTextTagAlpha takes texttag Whichtag, real transparency returns nothing endfunction private function Conditions takes nothing returns boolean return GetTriggerDamageType() != DAMAGE_TYPE_TEST endfunction private function Actions takes nothing returns nothing local unit u = GetTriggerDamageSource() local unit t = GetTriggerDamageTarget() local real d = GetTriggerDamage() local integer i if IsUnitInGroup(t, DMG_PLAYER_GROUP[GetPlayerId(GetOwningPlayer(u))]) == false then set DMG_PLAYER_GROUP_INT[GetPlayerId(GetOwningPlayer(u))] = DMG_PLAYER_GROUP_INT[GetPlayerId(GetOwningPlayer(u))] + 1 call GroupAddUnit(DMG_PLAYER_GROUP[GetPlayerId(GetOwningPlayer(u))], t ) set DMG_PLAYER_GROUP_UNIT[( ( FT_MAX * ( GetPlayerId(GetOwningPlayer(u))) ) + DMG_PLAYER_GROUP_INT[GetPlayerId(GetOwningPlayer(u))] )] = t set DMG_PLAYER_GROUP_DMG[( ( FT_MAX * ( GetPlayerId(GetOwningPlayer(u))) ) + DMG_PLAYER_GROUP_INT[GetPlayerId(GetOwningPlayer(u))] )] = d set DMG_PLAYER_GROUP_FT[( ( FT_MAX * ( GetPlayerId(GetOwningPlayer(u))) ) + DMG_PLAYER_GROUP_INT[GetPlayerId(GetOwningPlayer(u))] )] = CreateTextTag() call SetFT(DMG_PLAYER_GROUP_FT[( ( FT_MAX * ( GetPlayerId(GetOwningPlayer(u))) ) + DMG_PLAYER_GROUP_INT[GetPlayerId(GetOwningPlayer(u))] )],I2S(R2I(d)), GetPlayerId(GetOwningPlayer(u)), GetUnitX(t),GetUnitY(t),GetUnitZ(t),RED[GetPlayerId(GetOwningPlayer(u))],GREEN[GetPlayerId(GetOwningPlayer(u))],BLUE[GetPlayerId(GetOwningPlayer(u))],50.,0.,0.,5.0,true,7.+2.*DMG_PLAYER_GROUP_DMG[( ( FT_MAX * ( GetPlayerId(GetOwningPlayer(u))) ) + DMG_PLAYER_GROUP_INT[GetPlayerId(GetOwningPlayer(u))] )]/ 600.00) call TimerStart(DMG_PLAYER_GROUP_TIMER[( ( FT_MAX * ( GetPlayerId(GetOwningPlayer(u))) ) + DMG_PLAYER_GROUP_INT[GetPlayerId(GetOwningPlayer(u))] )],StackTimer,false,null) else set i = ( FT_MAX * ( GetPlayerId(GetOwningPlayer(u))) ) loop exitwhen i > ((FT_MAX * ( GetPlayerId(GetOwningPlayer(u))) ) + DMG_PLAYER_GROUP_INT[GetPlayerId(GetOwningPlayer(u))]) if t == DMG_PLAYER_GROUP_UNIT[i] then set DMG_PLAYER_GROUP_DMG[i] = ( DMG_PLAYER_GROUP_DMG[i] + d ) call TimerStart( DMG_PLAYER_GROUP_TIMER[i],StackTimer,false,null) endif set i = i + 1 endloop endif set u = null set t = null set i = 0 set d = 0 endfunction private function Conditions2 takes nothing returns boolean return not(DMG_PLAYER_GROUP_INT[0] == -1 and DMG_PLAYER_GROUP_INT[1] == -1 and DMG_PLAYER_GROUP_INT[2] == -1 and DMG_PLAYER_GROUP_INT[3] == -1 and DMG_PLAYER_GROUP_INT[4] == -1 and DMG_PLAYER_GROUP_INT[5] == -1 and DMG_PLAYER_GROUP_INT[6] == -1 and DMG_PLAYER_GROUP_INT[7] == -1 and DMG_PLAYER_GROUP_INT[8] == -1 and DMG_PLAYER_GROUP_INT[9] == -1 and DMG_PLAYER_GROUP_INT[10] == -1 and DMG_PLAYER_GROUP_INT[11] == -1) endfunction private function Actions2 takes nothing returns nothing local integer i = 0 local integer j local integer maxslot local integer currentslot loop exitwhen i > 0 set maxslot = DMG_PLAYER_GROUP_INT[i] set j = ( ( FT_MAX * ( i ) ) ) loop exitwhen j > ( ( FT_MAX * ( i ) ) + maxslot ) set currentslot = ( ( ( FT_MAX * ( i ) ) + maxslot ) - ( ( j ) - ( FT_MAX * ( i ) ) ) ) call SetFT(DMG_PLAYER_GROUP_FT[currentslot],( I2S(R2I(DMG_PLAYER_GROUP_DMG[currentslot])) ), i,GetUnitX(DMG_PLAYER_GROUP_UNIT[currentslot]),GetUnitY(DMG_PLAYER_GROUP_UNIT[currentslot]),GetUnitZ(DMG_PLAYER_GROUP_UNIT[currentslot]),RED[i],GREEN[i],BLUE[i],50.,0.,0.,5.0,true,( 7.00 + ( 2.00 * ( DMG_PLAYER_GROUP_DMG[currentslot] / 600.00 ) ) )) if (TimerGetRemaining(DMG_PLAYER_GROUP_TIMER[currentslot]) <= 0.) then call SetFT(DMG_PLAYER_GROUP_FT[currentslot],( I2S(R2I(DMG_PLAYER_GROUP_DMG[currentslot])) ), i, GetUnitX(DMG_PLAYER_GROUP_UNIT[currentslot]),GetUnitY(DMG_PLAYER_GROUP_UNIT[currentslot]),GetUnitZ(DMG_PLAYER_GROUP_UNIT[currentslot]),RED[i],GREEN[i],BLUE[i],50.,0.,0.,5.0,false,( 7.00 + ( 2.00 * ( DMG_PLAYER_GROUP_DMG[currentslot] / 600.00 ) ) )) call DestroyTextTag(DMG_PLAYER_GROUP_FT[currentslot]) call QuickFT(( I2S(R2I(DMG_PLAYER_GROUP_DMG[currentslot])) + "!" ), i, GetUnitX(DMG_PLAYER_GROUP_UNIT[currentslot]), GetUnitY(DMG_PLAYER_GROUP_UNIT[currentslot]),GetUnitZ(DMG_PLAYER_GROUP_UNIT[currentslot]),RED[i],GREEN[i],BLUE[i],0.,GetRandomReal(-0.007,0.007),0.02,( 0.75+1.5 * ( DMG_PLAYER_GROUP_DMG[currentslot] / 300.00 ) ),false,( 7.00 + ( 2.00 * ( DMG_PLAYER_GROUP_DMG[currentslot] / 600.00 ) ) )) // Replacing last slot into empty slot call GroupRemoveUnit( DMG_PLAYER_GROUP[i], DMG_PLAYER_GROUP_UNIT[currentslot] ) set DMG_PLAYER_GROUP_UNIT[currentslot] = DMG_PLAYER_GROUP_UNIT[( ( FT_MAX * ( i ) ) + DMG_PLAYER_GROUP_INT[i] )] set DMG_PLAYER_GROUP_DMG[currentslot] = DMG_PLAYER_GROUP_DMG[( ( FT_MAX * ( i ) ) + DMG_PLAYER_GROUP_INT[i] )] set DMG_PLAYER_GROUP_FT[currentslot] = CreateTextTag() call SetFT(DMG_PLAYER_GROUP_FT[currentslot],"", i, GetUnitX(DMG_PLAYER_GROUP_UNIT[currentslot]),GetUnitY(DMG_PLAYER_GROUP_UNIT[currentslot]),GetUnitZ(DMG_PLAYER_GROUP_UNIT[currentslot]),40.,40.,40.,0.,0.,0.,5.0,true,7.+2.*DMG_PLAYER_GROUP_DMG[currentslot]/ 600.00) // Getting Rid of the last slot stats call TimerStart(DMG_PLAYER_GROUP_TIMER[currentslot], TimerGetRemaining(DMG_PLAYER_GROUP_TIMER[( ( FT_MAX * ( i ) ) + DMG_PLAYER_GROUP_INT[i] )]),false, null) set DMG_PLAYER_GROUP_UNIT[( ( FT_MAX * ( i ) ) + DMG_PLAYER_GROUP_INT[i] )] = null call DestroyTextTag(DMG_PLAYER_GROUP_FT[( ( FT_MAX * ( i ) ) + DMG_PLAYER_GROUP_INT[i] )] ) set DMG_PLAYER_GROUP_INT[i] = ( DMG_PLAYER_GROUP_INT[i] - 1 ) endif set j = j + 1 endloop set i = i + 1 endloop set i = 0 set j = 0 set maxslot = 0 set currentslot = 0 endfunction private function Init takes nothing returns nothing local trigger trg = CreateTrigger() local integer i = 0 local integer j = 0 set RED[0] = 100.00 set BLUE[0] = 0.00 set GREEN[0] = 0.00 set RED[1] = 0.00 set BLUE[1] = 100.00 set GREEN[1] = 0.00 set RED[2] = 10.98 set BLUE[2] = 72.55 set GREEN[2] = 90.20 set RED[3] = 32.95 set BLUE[3] = 50.59 set GREEN[3] = 0.00 set RED[4] = 100.00 set BLUE[4] = 0.00 set GREEN[4] = 100.00 set RED[5] = 100.00 set BLUE[5] = 5.49 set GREEN[5] = 54.12 set RED[6] = 12.55 set BLUE[6] = 0.00 set GREEN[6] = 75.29 set RED[7] = 89.80 set BLUE[7] = 69.02 set GREEN[7] = 35.69 set RED[8] = 58.82 set BLUE[8] = 58.82 set GREEN[8] = 58.82 set RED[9] = 49.41 set BLUE[9] = 94.51 set GREEN[9] = 74.90 set RED[10] = 6.27 set BLUE[10] = 27.45 set GREEN[10] = 38.43 set RED[11] = 30.59 set BLUE[11] = 0.00 set GREEN[11] = 16.47 loop exitwhen i > 11 set DMG_PLAYER_GROUP_INT[i] = -1 set DMG_PLAYER_GROUP[i] = CreateGroup() set i = i + 1 endloop set i = 0 set j = 0 loop exitwhen i > 11 loop exitwhen j > FT_MAX set DMG_PLAYER_GROUP_TIMER[(i*FT_MAX)+j]=CreateTimer() set j = j + 1 endloop set i = i + 1 endloop call TriggerAddAction(trg, function Actions) call TriggerAddCondition(trg, Condition(function Conditions)) call TriggerRegisterDamageEvent(trg, 0) set trg = CreateTrigger() call TriggerAddAction(trg, function Actions2) call TriggerAddCondition(trg, Condition(function Conditions2)) call TriggerRegisterTimerExpireEvent(trg, FT_Timer) call TimerStart(FT_Timer, PeriodicCheck,true, null) set i = 0 set j = 0 set trg = null endfunction endscope Anyways, Before you dive in and ask what everything is for I'll lay out the system for you. It's pretty basic in design and the idea came to me when I noticed some maps displaying the damage dealt from abilities to the player. I thought this was a neat Idea, although i had a beef about cluttered floating texts flying all over the place. I also wanted a method so that i could get the total damage dealt if multiple damage events hit all at once (ie for shotgun style abilities, like pluck yew from tob). So i came up with this. The system is setup to handle up to 100 floating texts displayed per character, but this can be tweaked to whatever you require. The floating text will appear dimmed over the unit, displaying the current total damage dealt to said unit with the same colour as your player colour. There is a timer which you can set which is the time between damage events in which the floating text will continuously update the damage being dealt. After that time fades, the floating text will light up and float away like any normal text, displaying the total stacked damage on the unit. The purpose for this system is to allow chain-damage events to be stacked and displayed to the player, something like displaying combo damage dealt to a unit. Now as for my method, I basically have 2 sets of arrays which store the player and the slot occupied by the damaged unit. Now the trigger is split up into the triggered damage events and monitoring the floating text until it dies. There's a periodic update which can be adjusted as needed which, so long as at least one slot is open for any player, cycles from the last position down to the first slot for each player, updates the texts, and replaces any expired slots similar to how most other systems work (ie knockback systems). As far as i know it work's just fine and doesn't really seem have any leaks that I can think of (though I'm sure there are, since I'm not quite used to all the things that can go wrong with this stuff), but I'm pretty sure my method for storing the units and information can be simplified with structs and other clean ups, tho i haven't really gotten into using structs yet. anywho, I'd appreciate any help with cleaning this up. I will be using this system in the future and Hopefully I can get a user freindly one up once I'm better with JASS. |
| 08-03-2009, 07:30 AM | #2 |
This sounds really cool - particularly because I've been wanting to write up something similar. Hopefully I can help you out - I'll look over the code this evening. |
| 08-03-2009, 07:34 AM | #3 |
lil question: how do you get the damage to show? (can't find it in your code) |
| 08-03-2009, 08:27 AM | #4 |
Looking over the code, there's a whole bunch of stuff that we can change. For example, you require the use of IDDS, which itself requires the use of GroupUtils. Thus, you should be using GroupUtils to handle groups. (Sorry, that was my modification. Default IDDS only requires table.) Also, there's no way to guarantee that player-colours remain the same as at init, so I'd avoid colouring damage that way, and instead allow a way to link damage colour to damage type (as defined by IDDS, a requirement). Further, I'd then include functions to call the system manually for user-defined events, such as unit healing. Give me some time, I'll go over this in more detail and post my suggestions and recommendations. As I said, this is something I've been wanting to do for a while, so I'm excited :). |
| 08-03-2009, 10:55 AM | #5 |
Well. There's one thing that's going to kill you: the game engine can only display 99 texttags for each player at any given time. Albeit this means that if 99 texttags are shown for Player 1 with GetLocalPlayer() blocks, then a separate 99 can be shown for Player 2... but when you go over 99 the first ones start to disappear. As for your system:
I really would like to make another "I have no life so I do this to people's code" post, but I seriously must go. I hope this helps you out a lot :) |
| 08-03-2009, 12:31 PM | #6 | ||
Quote:
That shouldn't be a problem - as long as textags have their permanence set to false and are given a lifespan, they are removed. I don't think there are many situations where you'd be showing 90 or more textags at once. Quote:
Enjoy your vacation, by the by. |
| 08-03-2009, 05:34 PM | #7 |
Wow I am very grateful for all the feed back. Not quite everything is clear to me but most of it makes sense. As for replacing slots, I've been told how it's done so i shouldn't have too much trouble with it now. Anyways, Thanks a bunch for all the tips and fixes. Hope you have a great trip. NOW, for whomever is still here I have questions which will modify how i go about fixing this. 1) Some of the functions i made in here I will be using outside of this system. I actually have some 10-15 functions that will be used over the entire map. Is there a way i can simply group them all in one tidy spot, and just refer to them any other private functions? ..More questions to come as I get stuck... |
| 08-03-2009, 07:23 PM | #8 |
The answer tot hat question would depend on a lot of factors. For starters, if you have decent documentation listing all the public functions available, it doesn't matter where in the library those functions are. I still prefer to group them together for more organisation when writing code, but when it comes to large libraries with multiple subsections that may actually make things less organised, also if I have a textmacro that generates both public and private functions, I won't go out of my way to split it into two so that public functions would end up getting generated together. |
| 08-04-2009, 12:21 AM | #9 |
Well for now I only have about 5 or 6 functions but I plan to make more, and would rather they stay altogether. These will be more like small but useful functions that are used with multiple applications, from spells, to systems to whatever else you can do in an AoS. So having them in their own spot would work out for the best. Do I just create a library for all of them or what? These are all independent of each other. |
| 08-04-2009, 12:55 AM | #10 |
Wait a minute, you mean a compilation of miscelaneous functions? Usually, those are either so short you can inline them or long enough to warrant their own library. Can you name some examples? |
| 08-04-2009, 02:50 AM | #11 |
GetUnitZ and GetUnitZTrue. I'm using that for my knockback system as well as this. QuickFT and Set FT also will be used for a bunch of other spells and things not related to this. I just put them in here because it was easier to go back and check what my inputs were when they were right there. But those functions I don't really need in there, they're more miscellanious but used in multiple systems. And I have more that i need to make still. Do I just throw all those into 1 library or what? |
| 08-04-2009, 03:33 AM | #12 |
Put related functions in related libraries. If you have a whole bunch of useful functions that don't really stand alone, you can throw them into your own library and use that as a requirement for other libraries that utilise them. In the case of a public system, we have to include all functions that are required by the system. Those that the end-user shouldn't need to use are made private, those functions that are used to access the system are left as-is or given a system prefix. |
| 08-04-2009, 12:45 PM | #13 |
I prefer to inline stuff like GetUnitZ; it's only three lines of code (a global location declaration/initialization, a MoveLocation call and a GetLocationZ call) and you shouldn't really need it in more than a few places. I'm not really sure how GetUnitZTrue is supposed to be different from GetUnitZ, nor what QuickFT and SetFT are supposed to be, so I can't really comment on those. In general, however, I have never needed a library of miscelaneous functions myself, so I'm sure that whatever those are they are either inlineable or deserve their own library (maybe one is even already in our resource section). |
| 08-04-2009, 08:31 PM | #14 |
The difference between the 2 getUnitZs is that one takes into account terrain height, while the other simply takes height from terrain. I'd have to test but I'm not 100% sure everything will work properly with getUnitZtrue. Also QuickFT creates an unassigned floating text which will self terminate. SetFT is used to modify a newly created floating text or just update another one. I did have a CreateFT function as well for making temporarily trackable floating texts but it wasn't working so i removed it. The system itself first creates a trackable floating text that updates and then when it's time to expire, destroys that floating text, flushes out that slot, and creates a self terminating one to display the final damage dealt. Anyways, thanks for the advice. Seeing as I'm working to eventually make this system streamlined I'll have to keep those smaller functions in here anyways. |
| 08-04-2009, 11:46 PM | #15 |
So...now it doesn't work with the new patch... ugh.... |
