| 10-18-2008, 01:21 PM | #1 | |
Hi guys. This is the bomber spell I was trying to do with some help. I finally ended it a few days ago. After submitting it to the script section no one gave any more advices, so I believe this is ready. If there is anything to change, go ahead and let me know. This should be more of a template than a simple spell, because it's structure was made to accommodate change, however, I only have 1 sample. The code is commented, the spell is JESP (as usual) and I hope people like it. This spell was remade from the start several times, I hope this version stands up. Please note I followed a different approach from all my previous spells. Therefore I feel forced to make people know the main differences between this bombardment spell and other bombardment spells. Differences: - For the first time, your bomber can DIE !!! (lol) he is vulnerable to damage! - You have a limited control on the planes flight plan ! (means you can turn him a little around). - Bombs have realistic fall, this means they obey to the laws of physics when they fall. This spell belongs to my project Castle vs Castle Flame Edition and I hope you all enjoy it. Description: - A spell calls a gnomish plane from behind the caster. The plane will drop Incendiary bombs and burn the enemies. Requirements: - Jass NewGen Pack (uses vJASS) - TimerUtils - Table History:
Bomber://=========================================================================== //A spell calls a gnomish plane from behind the caster. The plane will drop //Incendiary bombs and burn the enemies. This plane can be controled by the //user and it can die when bombing as well. It is also JESP standard. // //Requires TimerUtils and Table // //@author Flame_Phoenix // //@credits //- Anitarf, the only guy who helped me when all others didn't //- Bobo_The_Kodo, for suggestions on code and bug fixing //- Here-b-Trollz, for suggestions on code and bug fixing //- xxdingo93xx, my first loyal student, who gave me the motivation to end this spell //- My first teacher of vJASS: Blue_Jeans //- All other people I forgot or ignored // //@version 1.7 // //Note: You can make the plane unselectable and invulnerable by using the WE //abilities. Just add them to the unit. If you still want to make the plane //vulnerable, but don't want people to change its flight course than just //set the plane's movement speed to 0 in the object editor. //=========================================================================== scope FireBomber initializer Init //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals //Settings for the Bomber private constant integer AID = 'A000' //rawcode of the ability private constant integer BOMBER_ID = 'h002' //rawcode of the bomber private constant real FADE_TIME = 0.3 //the amount of time the plane will take to fade in private constant real DIVE_TIME = 0.7 //the time the plane will take into diving in and out private constant real PLANE_SPEED = 8 //the speed of the plane private constant integer BOMBARD_HEIGHT = 400 //the bombard height private constant real MOVE_TIME = 0.02 //the frequency of the timer private constant real BOMB_INTERVAL = 1. //the interval that separates each bomb private constant string BOOM = "Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl" //the effect that appears when the bomber dies before finishing the mission private constant integer PLANE_RED = 255 //The Red RGB color of the Plane private constant integer PLANE_GREEN = 255 //The Green RGB color of the Plane private constant integer PLANE_BLUE = 255 //The Blue RGB color of the Plane private constant integer FADE_MAX = 255 //The maximum Alpha the plane will have. This is ralated to the transparency and therefore to the fading. //Settings for the Bombs private constant integer BOMB_ID = 'h003' //the Id of the bomb private constant integer DUM_ID = 'h001' //the dummy unit that will attack and cause fire private constant string DUM_ORDER = "attackground" //it's order private constant integer FIRE_ABILITY_ID = 'A001' //Burning Oil Ability private constant integer FIRE_UNIT_ID = 'h005' //Fire effect unit private constant real FIRE_TIME = 20.//duration of the fire effect private constant real GRAVITY = 5.5 //the velocity at which the bomb will fall endglobals private constant function PlaneStartDistance takes integer level returns real //the ditance that the plane will be from the caster when it is created //you can make it depend on the level like in the next example return 850. + (level * 0) endfunction private constant function BombsNumber takes integer level returns integer //the number of bombs the plane will drop return 5 + (level * 0) endfunction private function BombEffect takes unit aBomb returns nothing //This is the effect of the bomb. It will be displayed when it dies. //the function takes as a parameter the dying bomb local unit bomb = aBomb local real bombX = GetUnitX(bomb) local real bombY = GetUnitY(bomb) local player p = GetOwningPlayer(bomb) local unit dummy local unit fire //if the bomb is falling inside the map we create the fire if (GetRectMinX(bj_mapInitialPlayableArea) <= bombX) and (bombX <= GetRectMaxX(bj_mapInitialPlayableArea)) and (GetRectMinY(bj_mapInitialPlayableArea) <= bombY) and (bombY <= GetRectMaxY(bj_mapInitialPlayableArea)) then //here the dummy launches burning oil to the ground set dummy = CreateUnit(p, DUM_ID, bombX, bombY, 0.0) call UnitAddAbility(dummy, FIRE_ABILITY_ID) call IssuePointOrder(dummy, DUM_ORDER, bombX, bombY) call UnitApplyTimedLife( dummy,'BTLF', 1.) //here we use a dummy unit as an effect of the fire set fire = CreateUnit(p, FIRE_UNIT_ID, bombX, bombY, 0.0) call UnitApplyTimedLife( fire,'BTLF', FIRE_TIME) call SetUnitPathing(fire, false) endif set bomb = null set p = null set dummy = null set fire = null endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== globals private group Bombers //to store the bombers! private HandleTable activeTable //your private Table's global variable endglobals //this keeps track of all information we need for the bomber plane private struct PlaneData unit caster //the caster of the spell integer level //the level of the ability unit bomber //this is the plane that will appear real height //this is the innitial height of the plane. It starts with 600 (look object editor) timer mover //this timer will move the plane real elapsedTime //this specifies the current time of the spell integer bombsDropped //number of bombs dropped real totalFlightDuration //the total amount of time the flight will take boolean isOnBombingHeight //if true than our plane is dropping bombs, else it is flying static method create takes unit caster, real spellX, real spellY, real angle returns PlaneData local PlaneData data = PlaneData.allocate() //set variables about the caster set data.caster = caster set data.level = GetUnitAbilityLevel(data.caster, AID) //variables about the bomber set data.bomber = CreateUnit(GetOwningPlayer(data.caster), BOMBER_ID, spellX - PlaneStartDistance(data.level) * Cos(angle * bj_DEGTORAD), spellY - PlaneStartDistance(data.level) * Sin(angle * bj_DEGTORAD), angle) set data.height = GetUnitFlyHeight(data.bomber) set data.mover = NewTimer() set data.bombsDropped = 0 set data.isOnBombingHeight = false set data.elapsedTime = 0 //the total amount of fly time will take set data.totalFlightDuration = (FADE_TIME + DIVE_TIME) + (2 * DIVE_TIME) + (2 * FADE_TIME) + (BombsNumber(data.level) * BOMB_INTERVAL) //this makes the plane start invisible call SetUnitVertexColor(data.bomber, PLANE_RED, PLANE_GREEN, PLANE_BLUE, R2I(255 * (data.elapsedTime / FADE_TIME))) call SetUnitPathing(data.bomber, false) //put the struct in the Table, we just use the bomber's //handle adress as the key which tells us where in the //Table the struct is stored set activeTable[data.bomber] = data call GroupAddUnit(Bombers, data.bomber) return data endmethod method onDestroy takes nothing returns nothing //since the bomber dies now we clean the table call activeTable.flush(.bomber) //the unit is not anymore in the active units group call GroupRemoveUnit(Bombers, .bomber) call ReleaseTimer(.mover) if (.elapsedTime >= .totalFlightDuration) then call ShowUnit(.bomber, false) call KillUnit(.bomber) else call DestroyEffect(AddSpecialEffectTarget(BOOM, .bomber, "origin")) endif endmethod endstruct //this takes care of each bomb individualy. So if the bomber dies while bombing //the released bombs will still fall and damage the enemies ! private struct BombData unit bomb real bombX real bombY timer t static method bombFall takes nothing returns nothing local BombData data = BombData(GetTimerData(GetExpiredTimer())) //here we move the bomb ! set data.bombX = GetUnitX(data.bomb) + PLANE_SPEED * Cos(bj_DEGTORAD*GetUnitFacing(data.bomb)) set data.bombY = GetUnitY(data.bomb) + PLANE_SPEED * Sin(bj_DEGTORAD*GetUnitFacing(data.bomb)) call SetUnitPosition(data.bomb,data.bombX, data.bombY) if (GetUnitFlyHeight(data.bomb) > GRAVITY) then //if the bomb didn't reach ground, make it fall call SetUnitFlyHeight(data.bomb, GetUnitFlyHeight(data.bomb) - GRAVITY, 0) else //if it reached the ground we kill it and fire the effect trigger call data.destroy() endif endmethod static method create takes unit plane returns BombData local BombData data = BombData.allocate() set data.bomb = CreateUnit(GetOwningPlayer(plane), BOMB_ID, GetUnitX(plane), GetUnitY(plane), GetUnitFacing(plane)) set data.bombX = GetUnitX(plane) set data.bombY = GetUnitY(plane) call SetUnitPathing(data.bomb, false) call SetUnitFlyHeight(data.bomb, GetUnitFlyHeight(plane), 0) set data.t = NewTimer() call SetTimerData(data.t, integer(data)) call TimerStart(data.t, MOVE_TIME, true, function BombData.bombFall) return data endmethod method onDestroy takes nothing returns nothing call BombEffect(.bomb) call ReleaseTimer(.t) call KillUnit(.bomb) endmethod endstruct //====================================================================================== private function onDeath takes nothing returns boolean local PlaneData plane local unit u = GetTriggerUnit() //we make sure that the unit that dies is the bomber if(IsUnitInGroup(u, Bombers)) then //recover that plane (the struct) from the bomber set plane = activeTable[u] call plane.destroy() endif set u = null return false endfunction //====================================================================================== private function MovePlane takes nothing returns nothing local PlaneData plane = PlaneData(GetTimerData(GetExpiredTimer())) local BombData bomb set plane.elapsedTime = plane.elapsedTime + MOVE_TIME //here we make the plane fade in or fade out, depending on the timer if (plane.elapsedTime <= FADE_TIME) then call SetUnitVertexColor(plane.bomber, PLANE_RED, PLANE_GREEN, PLANE_BLUE, R2I(255 * (plane.elapsedTime / FADE_TIME))) elseif (plane.elapsedTime >= plane.totalFlightDuration - FADE_TIME) then call SetUnitVertexColor(plane.bomber, PLANE_RED, PLANE_GREEN, PLANE_BLUE, R2I(255 * ((plane.totalFlightDuration - plane.elapsedTime) / FADE_TIME))) endif //here we make the plane dive in or dive out, depending on the timer if (plane.elapsedTime >= FADE_TIME) and not(plane.isOnBombingHeight) then set plane.isOnBombingHeight = true call SetUnitFlyHeight(plane.bomber, BOMBARD_HEIGHT, (plane.height - BOMBARD_HEIGHT) / DIVE_TIME) elseif (plane.elapsedTime >= plane.totalFlightDuration - DIVE_TIME - FADE_TIME) and (plane.isOnBombingHeight) then set plane.isOnBombingHeight = false call SetUnitFlyHeight(plane.bomber, plane.height, (plane.height - BOMBARD_HEIGHT) / DIVE_TIME) endif //if we already faded in and dived in, we drop the bombs ! if (plane.elapsedTime > (FADE_TIME + DIVE_TIME) + (plane.bombsDropped * BOMB_INTERVAL)) and (plane.bombsDropped < BombsNumber(plane.level)) then set bomb = BombData.create(plane.bomber) set plane.bombsDropped = plane.bombsDropped + 1 endif //if we already did everything we wanted, we kill everything ! xD if (plane.elapsedTime > plane.totalFlightDuration) then call plane.destroy() endif call SetUnitPosition(plane.bomber, GetUnitX(plane.bomber) + PLANE_SPEED * Cos(bj_DEGTORAD*GetUnitFacing(plane.bomber)), GetUnitY(plane.bomber) + PLANE_SPEED * Sin(bj_DEGTORAD*GetUnitFacing(plane.bomber))) endfunction //====================================================================================== private function Conditions takes nothing returns boolean local location spellLoc local real angle local PlaneData plane if (GetSpellAbilityId() == AID) then set spellLoc = GetSpellTargetLoc() set angle = bj_RADTODEG * Atan2(GetLocationY(spellLoc) - GetUnitY(GetTriggerUnit()), GetLocationX(spellLoc) - GetUnitX(GetTriggerUnit())) set plane = PlaneData.create(GetTriggerUnit(), GetLocationX(spellLoc), GetLocationY(spellLoc), angle) call SetTimerData(plane.mover, integer(plane)) call TimerStart(plane.mover, MOVE_TIME, true, function MovePlane) call RemoveLocation(spellLoc) endif set spellLoc = null return false endfunction //====================================================================================== private function Init takes nothing returns nothing local trigger FireBomberTrigger = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(FireBomberTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(FireBomberTrigger, Condition( function Conditions ) ) set FireBomberTrigger = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(FireBomberTrigger, EVENT_PLAYER_UNIT_DEATH ) call TriggerAddCondition(FireBomberTrigger, Condition(function onDeath)) set FireBomberTrigger = null //SETTING OUR GLOBALS set activeTable = HandleTable.create() //Create our spell's private Table for the bombers set Bombers = CreateGroup() //Preloading Effect call Preload(BOOM) endfunction endscope This spell took me years (yes years) to develop. I always dreamed to create it, but all my previous versions were never good enough. I hope this one breaks the cycle. It is now, with version 1.5, the best bomber spell I've ever seen in my entire life, and yes, I made a research for bombing spells. I am very proud of it, but, as always, the last word goes to the moderators which will decide if this is good enough for approval. This combines nearly all my knowledge of vJASS that I acquired since I entered this community. If it gets approved, I am sure people will like it better than many other bombing spells. |
| 10-18-2008, 02:58 PM | #2 |
lol, from ss it looks like hes dropping a red water balloon. |
| 10-18-2008, 03:49 PM | #3 | |
Quote:
1 - It is a gnome, it is supposed to be funny ! What were you expecting ? 2 - I actually didn't found a better model, and I kinda like this one =) Glad you liked the spell though =) |
| 10-18-2008, 10:34 PM | #4 |
The bombs don't fall properly, partly due to the fact that the next bombs receive the same acceleration as the previous bombs. The fix would be to keep track of each bombs individual acceleration. You can do this by attaching an elapsed time to each bomb with "SetUnitIndex." edit: sorry for the hasty post. What I mean is that the dropping speed of each consecutive bomb is at the same speed the previous bomb finished at. To change this, you can use SetUnitIndex and GetUnitIndex since it is safe to assume the only things working with the bombs are your spell. Using the custom value, you can store the bombs time and their acceleration and starting velocity will be uniform. If you are not sure what im talking about, cast the spell and pay close attention to the falling rate of the bombs. The first one is perfect, but the consecutive bombs fall at a faster and faster rate, which doesn't look that great. The only other thing I would add is a boolean to include ally units in the damage group. Some maps may want to have it damage allies and enemies. Once both of these are complete, I would say you achieved your goal and made a great bomber spell! |
| 10-19-2008, 01:23 PM | #5 | ||
Quote:
I never used GetUnitIndex and SetUnitIndex before in my life, to be honest I didn't even know they existed ! (lol) ... I will try making some research and ask some people what they are and how I can use them. Quote:
In this specific case the fire is caused by a dummy spell. I will however add another bomber spell "Exploding bomber" which will drop exploding bombs, and this sample will show people your exact point. However I will only do this after fixing the bomb fall. Thx for the comments and feedback ! EDIT EDIT EDIT OK guys, new version released !!! I am quite sure you will all like the update ! Version 1.1 ! EDIT EDIT EDIT Ok guys, new version released !! Thx to Ammorth and his nice feedback. He gave me the determination to this the ultimate bombing spell. Version 1.2, hoping you all enjoy it. |
| 11-09-2008, 08:25 PM | #6 |
Ok guys, I just want to share that the spell was now updated to version 1.3 See "history" for more information. Hope you all like the updates enough to approve it. |
| 11-10-2008, 03:12 PM | #7 | |
You should make the bomb stuff a library so it is added before the other scope, you are only calling methods right now so it isn't noticeable, but still, those methods are called by TriggerEvaluate, doesn't seem worth it. Quote:
|
| 11-10-2008, 08:12 PM | #8 | |
Quote:
Should I delete the spell thing ? Should I use an interface (like you also suggested) ? Please I really want to work on this, but I need more directions. |
| 11-10-2008, 08:15 PM | #9 |
Read my post, 5 times if necessary. |
| 11-10-2008, 08:29 PM | #10 |
So basically, I only need to create a library every time I create a bomb type ? EDIT EDIT EDIT Anyway, before making any updates, and just to be sure, you some something like this right ? libraryBomb://=========================================================================== //An Incendiary bomb that will burn th ground once it falls. // //@author Flame_Phoenix // //@credits //- My first teacher of vJASS: Blue_Jeans //- All other people I forgot or ignored // //@version 1.5 //=========================================================================== library IncendiaryBombs initializer Init requires TimerUtils //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A002' //The id of the DropFireBomb spell private constant integer BOMB_ID = 'h003' //the Id of the bomb private constant integer DUM_ID = 'h001' //the dummy unit that will attack and cause fire private constant string DUM_ORDER = "attackground" //it's order private constant integer FIRE_ABILITY_ID = 'A001' //Burning Oil Ability private constant integer FIRE_UNIT_ID = 'h005' //Fire effect unit private constant real FIRE_TIME = 20.//duration of the fire effect private constant real MOVE_TIME = 0.02 //the frequency of the timer private constant real BOMB_SPEED = 6. //the speed of the plane private constant real GRAVITY = 7. //the velocity at which the bomb will fall endglobals private function BombEffect takes unit aBomb returns nothing //This is the effect of the bomb. It will be displayed when it dies. //the function takes as a parameter the dying bomb local unit bomb = aBomb local player p = GetOwningPlayer(bomb) local unit dummy local unit fire //here the dummy launches burning oil to the ground set dummy = CreateUnit(p, DUM_ID, GetUnitX(bomb), GetUnitY(bomb), 0.0) call UnitAddAbility(dummy, FIRE_ABILITY_ID) call IssuePointOrder(dummy, DUM_ORDER, GetUnitX(bomb), GetUnitY(bomb)) call UnitApplyTimedLife( dummy,'BTLF', 1.) //here we use a dummy unit as an effect of the fire set fire = CreateUnit(p, FIRE_UNIT_ID, GetUnitX(bomb), GetUnitY(bomb), 0.0) call UnitApplyTimedLife( fire,'BTLF', FIRE_TIME) set bomb = null set p = null set dummy = null set fire = null endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== //this takes care of each bomb individualy. So if the bomber dies while bombing //the released bombs will still fall and damage the enemies ! private struct BombData unit bomb real bombX real bombY timer t static method bombFall takes nothing returns nothing local BombData data = BombData(GetTimerData(GetExpiredTimer())) //here we move the bomb ! set data.bombX = GetUnitX(data.bomb) + BOMB_SPEED * Cos(bj_DEGTORAD*GetUnitFacing(data.bomb)) set data.bombY = GetUnitY(data.bomb) + BOMB_SPEED * Sin(bj_DEGTORAD*GetUnitFacing(data.bomb)) call SetUnitPosition(data.bomb,data.bombX, data.bombY) if (GetUnitFlyHeight(data.bomb) > GRAVITY) then //if the bomb didn't reach ground, make it fall call SetUnitFlyHeight(data.bomb, GetUnitFlyHeight(data.bomb) - GRAVITY, 0) else //if it reached the ground we kill it and fire the effect trigger call data.destroy() endif endmethod static method create takes unit plane returns BombData local BombData data = BombData.allocate() set data.bomb = CreateUnit(GetOwningPlayer(plane), BOMB_ID, GetUnitX(plane), GetUnitY(plane), GetUnitFacing(plane)) set data.bombX = GetUnitX(plane) set data.bombY = GetUnitY(plane) call SetUnitPathing(data.bomb, false) call SetUnitFlyHeight(data.bomb, GetUnitFlyHeight(plane), 0) set data.t = NewTimer() call SetTimerData(data.t, integer(data)) call TimerStart(data.t, MOVE_TIME, true, function BombData.bombFall) return data endmethod method onDestroy takes nothing returns nothing call BombEffect(.bomb) call ReleaseTimer(.t) call KillUnit(.bomb) endmethod endstruct //=========================================================================== private function Conditions takes nothing returns boolean local BombData data if GetSpellAbilityId() == AID then set data = BombData.create(GetTriggerUnit()) endif return false endfunction //=========================================================================== private function Init takes nothing returns nothing local trigger IncendiaryBombsTrg = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(IncendiaryBombsTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(IncendiaryBombsTrg, Condition( function Conditions ) ) set IncendiaryBombsTrg = null endfunction endlibrary |
| 11-11-2008, 12:49 PM | #11 |
Update, Version 1.5 was now released and it follows an approach that Anitarf decided was the best (I also agreed with him). I hope this version is now good enough for approval. Please read History for more information about the changes made. |
| 11-12-2008, 09:41 PM | #12 |
When I said that other thing I forgot the bomb was a spell. So it was all right, but using a library like that doesn't hurt Regarding the new version, I liked it more when the bomb stuff was separated in another scope, much more flexible, also, don't say that this performs better, it doesn't, using two scopes has nothing to do about efficiency. |
| 11-13-2008, 08:15 AM | #13 | |
Quote:
Well, I changed this to the new version because Anitarf told me to do so. I said there wasn't a necessity for 2 scopes, and I kinda have to agree with him. He also believes I use to much Object Editor stuff, and I also agreed with him. SO, my final question is, who is right after all ? To who shall I obey to see this spell approved ??? Also, where do I say this is more efficient without 2 scopes ? I kinda don't remember ... (lol) It is just easier to import and create new spells. ABout flexibility, the spell doesn't lose any from my point of view because the Bomb thing still has all parts it should have. Anyway, I am opened to opinions ... EDIT EDIT I just saw the efficiency thing, I will change it soon! |
| 11-13-2008, 11:17 AM | #14 | ||||
Quote:
Quote:
Quote:
Quote:
|
| 11-13-2008, 11:58 AM | #15 | |
Quote:
I will try speaking to Anitarf and confront him with your theory. Also, the description is now improved, removed the efficiency thing. |
