| 07-02-2011, 11:39 AM | #1 | ||||||||||
Transition library:library Transition requires optional CineCam //***************************************************************** //* TRANSITION //* //* written by: Anitarf //* //* This library was designed to streamline the coding of gradual //* changes of values. It allows you to gradually change a value //* without having to deal with timers and lists of instances. //* Simply implement the Transition module or extend the //* Transition struct and you will automatically gain the //* following methods: //* //* method startTransition( real duration, integer transitiontype, boolean autodestroy ) //* method stopTransition() //* //* Your struct must declare an onTransition method that takes a //* real argument and returns nothing. This method will be called //* periodically for all instances that had startTransition called //* on them for the duration specified when startTransition was //* called. During this time, a factor value will gradually //* increase for the instance from 0.0 to 1.0 and will be passed //* to your onTransition method, where you can use it to gradually //* change whatever game value you are changing. //* //* If this sounds too complicated, look at some examples of use //* and you will see that it is actually quite simple. Plenty of //* such examples should already come bundled with this library so //* so I see no need to include one in this documentation. //* //* The transitiontype argument passed to the startTransition //* method allows you to tweak how the transition factor changes //* from 0.0 to 1.0. More details on this are available in the //* calibration section where the transition types are declared. //* //* The autodestroy argument passed to the startTransition method //* determines whether the struct will be automatically destroyed //* when the transition finishes or is stopped. //* //* If this library is used in the same map as CineCam, then it //* will synchronize with CineCam instead of using its own update //* period, ensuring that the transitions are in sync with camera //* movement. //* //* If you extend the Transition struct, the onTransition method //* will be evaluated instead of called, which is slightly slower. //* On the other hand, extending the Transition struct will not //* copy the entire transition code the way implementing the //* Transition module does, which saves a little over a kilobyte //* in map size, so you might prefer to not use the module in //* situations where speed isn't important. //***************************************************************** globals constant real TRANSITION_UPDATE_PERIOD=0.02 // How often should a transition be updated. constant integer TRANSITION_TYPE_LINEAR=0 // The transition factor will increase steadily for the duration of the transition. constant integer TRANSITION_TYPE_SQUARE1=-1 // The transition factor will increase slower at the start and faster at the end of the transition. constant integer TRANSITION_TYPE_SQUARE2=-2 // The transition factor will increase faster at the start and slower at the end of the transition. constant integer TRANSITION_TYPE_SINE=-3 // The transition factor will increase slower at the start and the end of the transition and faster in the middle. endglobals function interface TransitionType takes real linearFactor returns real // You may also use functions following this interface to define a custom transition type. // END OF CALIBRATION SECTION // ================================================================ module Transition private real time private real duration private integer transtype private boolean autodestroy private boolean running // Should the instance be kept on the list? private boolean active // Is the instance still on the list? private static thistype array V private static integer N = 0 private static timer T private static method main takes nothing returns nothing local thistype this local integer i=0 local integer j=0 local real f loop exitwhen i>=thistype.N set this=thistype.V[i] if .running then static if LIBRARY_CineCam then set .time=.time+CINECAM_UPDATE_PERIOD else set .time=.time+TRANSITION_UPDATE_PERIOD endif if .time>=.duration then set .time=.duration set .running=false endif set f=.time/.duration if .transtype==TRANSITION_TYPE_LINEAR then // Do nothing. elseif .transtype==TRANSITION_TYPE_SINE then set f = (1.0 - Cos(f*bj_PI)) / 2 elseif .transtype==TRANSITION_TYPE_SQUARE1 then set f = f*f elseif .transtype==TRANSITION_TYPE_SQUARE2 then set f = 1.0 - f set f = 1.0 - f*f else set f = TransitionType(.transtype).evaluate(f) endif call .onTransition(f) set thistype.V[j]=this set j=j+1 else set .active=false if .autodestroy then call .destroy() endif endif set i=i+1 endloop set thistype.N=j static if not(LIBRARY_CineCam) then if thistype.N==0 then call PauseTimer(thistype.T) endif endif endmethod // ---------------------------------------------------------------- private static method onInit takes nothing returns nothing static if LIBRARY_CineCam then call OnCineCamUpdate(thistype.main) else set thistype.T=CreateTimer() endif endmethod // ---------------------------------------------------------------- // public methods: public method startTransition takes real duration, integer transitiontype, boolean autodestroy returns nothing static if LIBRARY_CineCam then if duration<CINECAM_UPDATE_PERIOD then set duration=CINECAM_UPDATE_PERIOD endif else if duration<TRANSITION_UPDATE_PERIOD then set duration=TRANSITION_UPDATE_PERIOD endif endif set .autodestroy=autodestroy set .transtype=transitiontype set .duration=duration set .time=0.0 if not .active then static if not(LIBRARY_CineCam) then if thistype.N==0 then call TimerStart(thistype.T, TRANSITION_UPDATE_PERIOD, true, function thistype.main) endif endif set thistype.V[thistype.N]=this set thistype.N=thistype.N+1 set .active=true endif set .running=true endmethod public method stopTransition takes nothing returns nothing set .running=false endmethod endmodule // ================================================================ struct Transition stub method onTransition takes real factor returns nothing endmethod implement Transition endstruct endlibrary CineCam library:library CineCam initializer Init requires optional xebasic //***************************************************************** //* CINECAM //* //* written by: Anitarf //* //* This library takes control of the game camera, allowing you to //* move it in more complex ways than the native camera functions //* allow. Instead of supporting only simple instant and linear //* camera movements, this library also enables camera movements //* that follow square and cube polynomial functions with emphasis //* on matching starting and end velocities so that the camera can //* transition smoothly from one move to another. //* //* //* CineCamEnable( boolean enable ) //* Activates or deactivates the CineCam system. The system is //* disabled by default, so this function needs to be called //* before the camera can be modified with CineCam functions. //* //* //* CineCamMove( camerasetup whichSetup, real duration, integer movetype ) //* Moves the camera to the given camerasetup using the specified //* movetype (for more information about movetypes, see the public //* constants section below). //* //* CineCamFieldMove( integer field, real endvalue, real endvelocity, real duration, integer movetype ) //* Modifies the specified camera field to the endvalue (for more //* information about CineCam fields, see the public constants //* section below). //* //* CineCamTargetLock( unit u, real xoff, real yoff, real zoff, real duration, boolean smooth ) //* Locks the camera target to the given unit with specified //* offsets from the unit's origin over the given duration. The //* boolean argument determines if the locking should be a simple //* linear transition or a smooth movement. This overrides the //* regular camera target x, y and z movement. //* //* CineCamFacingLock( unit u, real xoff, real yoff, real zoff, real duration, boolean smooth ) //* Similar to above, except it locks the camera facing instead. //* This overrides the regular camera rotation and angle of attack //* movement. //* //* CineCamTargetUnlock( real duration, boolean smooth ) //* CineCamFacingUnlock( real duration, boolean smooth ) //* Unlocks the camera target or facing, resuming regular movement //* of the overridden camera fields. Note that while the camera //* was locked, those fields were still being processed as usual //* so they may have changed since the camera was locked. //* //* //* OnCineCamUpdate( Callback c ) //* Allows you to synchronize external code with camera updates. //* Any function that takes nothing and returns nothing can be //* used as a callback and it will be executed immediately before //* every camera update. //* //* //* CineCamSmooth( boolean enable ) //* Warcraft3 automatically adjusts the camera's height based on //* the height of surrounding terrain. A camera positioned at the //* bottom of a valley will end up being higher than its z offset //* value. This smooths out the standard camera movement, however //* when making cinematics it can get in the way of precise camera //* positioning. //* With this function, the camera smoothing can be disabled. Note //* that the method used to achieve this has some drawbacks: it is //* not instant, which is not a problem when the camera is slowly //* moving over changing terrain, but it does cause the camera to //* jitter a bit when instantly moved to a point with a different //* surrounding terrain configuration. //* Also, cameras placed with the camera palette in the editor are //* closer to regular Warcraft cameras than to those that have //* smoothing disabled, so disabling smoothing is only useful when //* trying to do things like locking cameras to units. //* Because of this, the terrain smoothing is enabled by default. //* //* CineCamGetSpeed() -> real //* CineCamSetSpeed( real speed ) //* This modifies the overall speed of CineCam. It was implemented //* primarily to allow CineScript to change the speed of execution //* while maintaining the correct camera trajectory. The default //* value is 1.0 and probably does not need to be changed outside //* of CineScript. //* //* //* unit CINECAM_FADE_FILTER_UNIT //* Warcraft3 fade filters are affected by global lightning, so it //* is not possible to fade to white when the global lighting is //* dark, for example during the night. CineCam works around this //* problem by placing a unit with a special unshaded white plane //* model in front of the camera. By modifying the vertex color of //* that unit, you can get a fade filter of any color regardless //* of the global lighting. This approach has some disadvantages, //* it only offers one filter texture and blend mode and the fade //* filter unit can not always be precisely centered on the camera //* due to various factors, which in extreme cases results in it //* falling offscreen and not being displayed, but in the few //* cases where you need an unshaded filter, you can always tweak //* the camera so that this does not occur. //***************************************************************** globals // The following constant specifies how often the camera is updated. constant real CINECAM_UPDATE_PERIOD = 0.02 // If the following constant is true, then cameras will continue to move // along their trajectory even after they have reached their destination. private constant boolean CINECAM_CONTINUE_MOVE = true // This is the unit type of the unit used to make unshaded fade filters. // When you copy this library to a new map, make sure to also import the // custom fade model and copy the fade unit in the object editor, then // set this constant to the new rawcode of the fade unit in the new map. // If you don't need the unshaded fade filter feature, set this constant // to 0 and the fade filter unit will not be created. private constant integer CINECAM_FADE_FILTER_UNIT_TYPE = 'e000' // This variable will contain the fade filter unit once CineCam creates it. unit CINECAM_FADE_FILTER_UNIT // If you have xebasic in your map, its height enabler ability will be used when making // the fade filter unit flyable, otherwise this library will use the following ability: private constant integer HEIGHT_ENABLER = 'Amrf' endglobals // END OF CALIBRATION SECTION // ================================================================ globals constant integer CINECAM_MOVE_INSTANT = 0 constant integer CINECAM_MOVE_LINEAR = 1 constant integer CINECAM_MOVE_SQUARE = 2 constant integer CINECAM_MOVE_CUBIC = 3 constant integer CINECAM_FIELD_TARGET_X = 1 constant integer CINECAM_FIELD_TARGET_Y = 2 constant integer CINECAM_FIELD_ZOFFSET = 3 constant integer CINECAM_FIELD_ROTATION = 4 constant integer CINECAM_FIELD_ANGLE_OF_ATTACK = 5 constant integer CINECAM_FIELD_ROLL = 6 constant integer CINECAM_FIELD_FIELD_OF_VIEW = 7 constant integer CINECAM_FIELD_FARZ = 8 constant integer CINECAM_FIELD_TARGET_DISTANCE = 9 // END OF PUBLIC CONSTANTS // ================================================================ private location stupid = Location(0.0,0.0) private timer t private real array time private real array startval private real array startvel private real array accprim private real array accsec private real array endtime private unit OvRxyzUnitPrim = null private unit OvRxyzUnitSec = null private real OvRxyzTime = 0.0 private real OvRxyzEndtime = 0.0 private boolean OvRxyzSmooth = false private unit OvRangUnitPrim = null private unit OvRangUnitSec = null private real OvRangTime = 0.0 private real OvRangEndtime = 0.0 private boolean OvRangSmooth = false private real array OvRoffset private unit fadeUnit = null private boolean ignoreTerrain = false private real speed = 1.0 endglobals // ================================================================ private function CurrentValue takes integer field returns real return startval[field] + ( startvel[field] * time[field] ) + ( accprim[field] * Pow(time[field], 2.0) / 2.0 )+ ( accsec[field] * Pow(time[field], 3.0) / 3.0 ) endfunction private function CurrentVelocity takes integer field returns real return startvel[field] + ( accprim[field] * time[field] )+ ( accsec[field] * time[field] *time[field] ) endfunction function SetCameraZ takes real z returns nothing // Thanks to Toadcop for this function. set z = GetCameraField(CAMERA_FIELD_ZOFFSET)+z-GetCameraTargetPositionZ() call SetCameraField(CAMERA_FIELD_ZOFFSET,z,- 0.01) call SetCameraField(CAMERA_FIELD_ZOFFSET,z,0.01) endfunction // ================================================================ public function interface Callback takes nothing returns nothing globals private Callback array C private integer N=0 endglobals function OnCineCamUpdate takes Callback c returns nothing set C[N]=c set N=N+1 endfunction // ================================================================ private function MainLoop takes nothing returns nothing // Function copied from the old Cinematic System. // Don't ask me how it works, I don't know anymore. local real array s local real ovrFactor local integer i = 0 // ---------------------------------------------------------------- // camera callbacks loop exitwhen i==N call C[i].execute() set i=i+1 endloop set i=0 // ---------------------------------------------------------------- // camera field motion loop exitwhen i == 9 // Used to be 18 values in the old system - fog and fade filter are now covered by different // libraries so only the 9 camera values are still updated while indexes 10-18 remain unused. set i = i + 1 set time[i] = time[i] + CINECAM_UPDATE_PERIOD*speed static if not( CINECAM_CONTINUE_MOVE ) then if time[i] > endtime[i] then set time[i] = endtime[i] endif endif set s[i] = CurrentValue(i) if i==CINECAM_FIELD_ROTATION or i==CINECAM_FIELD_ANGLE_OF_ATTACK or i==CINECAM_FIELD_ROLL then loop exitwhen s[i] < 360.0 set s[i] = s[i] - 360.0 endloop loop exitwhen s[i] > 0 set s[i] = s[i] + 360.0 endloop endif endloop // ---------------------------------------------------------------- // camera lock-to-unit override if OvRxyzUnitPrim!=null or OvRxyzUnitSec!=null then set OvRxyzTime = OvRxyzTime + CINECAM_UPDATE_PERIOD*speed if OvRxyzTime > OvRxyzEndtime then set OvRxyzTime = OvRxyzEndtime endif if OvRxyzEndtime>0.0 then set ovrFactor = OvRxyzTime/OvRxyzEndtime else set ovrFactor = 1.0 endif if OvRxyzSmooth then set ovrFactor = (1.0 - Cos(ovrFactor*bj_PI)) / 2 endif call MoveLocation(stupid, GetUnitX(OvRxyzUnitPrim),GetUnitY(OvRxyzUnitPrim)) set s[19] = GetLocationZ(stupid) call MoveLocation(stupid, GetUnitX(OvRxyzUnitSec),GetUnitY(OvRxyzUnitSec)) set s[20] = GetLocationZ(stupid) if OvRxyzUnitSec==null then //going from null to unit set s[1] = s[1]*(1-ovrFactor) + (GetUnitX(OvRxyzUnitPrim)+OvRoffset[1])*ovrFactor set s[2] = s[2]*(1-ovrFactor) + (GetUnitY(OvRxyzUnitPrim)+OvRoffset[2])*ovrFactor set s[3] = s[3]*(1-ovrFactor) + (s[19]+GetUnitFlyHeight(OvRxyzUnitPrim)+OvRoffset[3])*ovrFactor elseif OvRxyzUnitPrim==null then //going from unit to null set s[1] = s[1]*ovrFactor + (GetUnitX(OvRxyzUnitSec)+OvRoffset[7])*(1.0-ovrFactor) set s[2] = s[2]*ovrFactor + (GetUnitY(OvRxyzUnitSec)+OvRoffset[8])*(1.0-ovrFactor) set s[3] = s[3]*ovrFactor + (s[20]+GetUnitFlyHeight(OvRxyzUnitSec)+OvRoffset[9])*(1.0-ovrFactor) else //going from unit to unit set s[1] = (GetUnitX(OvRxyzUnitSec)+OvRoffset[7])*(1-ovrFactor) + (GetUnitX(OvRxyzUnitPrim)+OvRoffset[1])*ovrFactor set s[2] = (GetUnitY(OvRxyzUnitSec)+OvRoffset[8])*(1-ovrFactor) + (GetUnitY(OvRxyzUnitPrim)+OvRoffset[2])*ovrFactor set s[3] = (s[20]+GetUnitFlyHeight(OvRxyzUnitSec)+OvRoffset[9])*(1-ovrFactor) + (s[19]+GetUnitFlyHeight(OvRxyzUnitPrim)+OvRoffset[3])*ovrFactor endif if OvRxyzTime == OvRxyzEndtime then if OvRxyzUnitPrim==null then set OvRxyzUnitSec = null endif endif endif // ---------------------------------------------------------------- // camera face-to-unit override if OvRangUnitPrim!=null or OvRangUnitSec!=null then set OvRangTime = OvRangTime + CINECAM_UPDATE_PERIOD*speed if OvRangTime > OvRangEndtime then set OvRangTime = OvRangEndtime endif if OvRangEndtime>0.0 then set ovrFactor = OvRangTime/OvRangEndtime else set ovrFactor = 1.0 endif if OvRangSmooth then set ovrFactor = (1.0 - Cos(ovrFactor*bj_PI)) / 2 endif call MoveLocation(stupid, GetUnitX(OvRangUnitPrim),GetUnitY(OvRangUnitPrim)) set s[19] = GetLocationZ(stupid) call MoveLocation(stupid, GetUnitX(OvRangUnitSec),GetUnitY(OvRangUnitSec)) set s[20] = GetLocationZ(stupid) if OvRangUnitPrim!=null then set s[21]=(GetUnitX(OvRangUnitPrim)+OvRoffset[4])-s[1] set s[22]=(GetUnitY(OvRangUnitPrim)+OvRoffset[5])-s[2] set s[23]=(s[19]+GetUnitFlyHeight(OvRangUnitPrim)+OvRoffset[6])-s[3] set s[24]=Atan2(s[22], s[21]) * bj_RADTODEG set s[25]=Atan2(s[23], SquareRoot(s[21]*s[21]+s[22]*s[22])) * bj_RADTODEG else set s[24]=s[4] set s[25]=s[5] endif if OvRangUnitSec!=null then set s[21]=(GetUnitX(OvRangUnitSec)+OvRoffset[10])-s[1] set s[22]=(GetUnitY(OvRangUnitSec)+OvRoffset[11])-s[2] set s[23]=(s[20]+GetUnitFlyHeight(OvRangUnitSec)+OvRoffset[12])-s[3] set s[26]=Atan2(s[22], s[21]) * bj_RADTODEG set s[27]=Atan2(s[23], SquareRoot(s[21]*s[21]+s[22]*s[22])) * bj_RADTODEG else set s[26]=s[4] set s[27]=s[5] endif if (s[26] - s[24]) > 180 then set s[24] = s[24] + 360 elseif (s[26] - s[24]) < -180 then set s[24] = s[24] - 360 endif set s[4] = ovrFactor * s[24] + (1-ovrFactor) * s[26] if (s[27] - s[25]) > 180 then set s[25] = s[25] + 360 elseif (s[27] - s[25]) < -180 then set s[25] = s[25] - 360 endif set s[5] = ovrFactor * s[25] + (1-ovrFactor) * s[27] if OvRangTime == OvRangEndtime then if OvRangUnitPrim==null then set OvRangUnitSec = null endif endif endif // ---------------------------------------------------------------- // distance to target cap if s[9]<100.0 then set s[9]=100.0 endif // ---------------------------------------------------------------- // camera fade filter unit if fadeUnit!=null then set s[23]=Sin(s[5]*bj_DEGTORAD) set s[20]=Cos(s[5]*bj_DEGTORAD) set s[21]=Cos(s[4]*bj_DEGTORAD)*s[20] set s[22]=Sin(s[4]*bj_DEGTORAD)*s[20] set s[19]=(s[9]-128.0) set s[20]=Tan(s[7]*bj_DEGTORAD/2) *40 set s[21]=s[1]-s[19]*s[21] set s[22]=s[2]-s[19]*s[22] set s[23]=s[3]-s[19]*s[23] call MoveLocation(stupid, s[21], s[22]) set s[23]=s[23]-GetLocationZ(stupid) call SetUnitX(fadeUnit, s[21]) call SetUnitY(fadeUnit, s[22]) call SetUnitFlyHeight(fadeUnit, s[23], 0.0) call SetUnitScale(fadeUnit, s[20],s[20],s[20]) endif // ---------------------------------------------------------------- // camera update call PanCameraToTimed( s[1], s[2], 0.0 ) if ignoreTerrain then call SetCameraZ(s[3]) else call MoveLocation(stupid, s[1], s[2]) call SetCameraField( CAMERA_FIELD_ZOFFSET, s[3]-GetLocationZ(stupid), 0.00 ) endif call SetCameraField( CAMERA_FIELD_ROTATION, s[4], 0.00 ) call SetCameraField( CAMERA_FIELD_ANGLE_OF_ATTACK, s[5], 0.00 ) call SetCameraField( CAMERA_FIELD_ROLL, s[6], 0.00 ) call SetCameraField( CAMERA_FIELD_FIELD_OF_VIEW, s[7], 0.00 ) call SetCameraField( CAMERA_FIELD_FARZ, s[8], 0.00 ) call SetCameraField( CAMERA_FIELD_TARGET_DISTANCE, s[9], 0.00 ) endfunction // ================================================================ function CineCamFieldMove takes integer field, real endvalue, real endvelocity, real duration, integer movetype returns nothing local real startvalue=CurrentValue(field) local real startvelocity=CurrentVelocity(field) local boolean radial=(field==CINECAM_FIELD_ROTATION or field==CINECAM_FIELD_ANGLE_OF_ATTACK or field==CINECAM_FIELD_ROLL) local real distance=endvalue-startvalue if duration < CINECAM_UPDATE_PERIOD then set duration = CINECAM_UPDATE_PERIOD endif if radial then loop exitwhen distance>0.0 set startvalue=startvalue-360.0 set distance=distance+360.0 endloop loop exitwhen distance<360.0 set startvalue=startvalue+360.0 set distance=distance-360.0 endloop endif if movetype==CINECAM_MOVE_INSTANT then set startval[field]= endvalue set startvel[field] = 0.0 set accprim[field] = 0.0 set accsec[field] = 0.0 elseif movetype==CINECAM_MOVE_LINEAR then set startval[field]= startvalue if not radial or distance<(360.0-distance) then set startvel[field] = distance/duration else set startvel[field] = (distance-360.0)/duration endif set accprim[field] = 0.0 set accsec[field] = 0.0 elseif movetype==CINECAM_MOVE_SQUARE then set startval[field]= startvalue set startvel[field] = startvelocity if not radial or RAbsBJ(distance - startvelocity * duration)<RAbsBJ((distance-360.0) - startvelocity * duration) then set accprim[field] = 2 * ( distance - startvelocity * duration ) / ( duration * duration ) else set accprim[field] = 2 * ( (distance-360.0) - startvelocity * duration ) / ( duration * duration ) endif set accsec[field] = 0.0 elseif movetype==CINECAM_MOVE_CUBIC then set startval[field]= startvalue set startvel[field] = startvelocity if not radial or RAbsBJ(3 * ( startvelocity + endvelocity ) / ( duration * duration ) - 6 * distance / Pow( duration, 3.0 ))<RAbsBJ(3 * ( startvelocity + endvelocity ) / ( duration * duration ) - 6 * (distance-360.0) / Pow( duration, 3.0 )) then set accprim[field] = 6 * distance / ( duration * duration ) - ( 2 * endvelocity + 4 * startvelocity ) / duration set accsec[field] = 3 * ( startvelocity + endvelocity ) / ( duration * duration ) - 6 * distance / Pow( duration, 3.0 ) else set accprim[field] = 6 * (distance-360.0) / ( duration * duration ) - ( 2 * endvelocity + 4 * startvelocity ) / duration set accsec[field] = 3 * ( startvelocity + endvelocity ) / ( duration * duration ) - 6 * (distance-360.0) / Pow( duration, 3.0 ) endif endif set time[field] = 0.0 set endtime[field] = duration endfunction // ================================================================ function CineCamMove takes camerasetup whichSetup, real duration, integer movetype returns nothing call MoveLocation(stupid, CameraSetupGetDestPositionX( whichSetup ), CameraSetupGetDestPositionY( whichSetup )) call CineCamFieldMove(1, CameraSetupGetDestPositionX( whichSetup ), 0.0, duration, movetype) call CineCamFieldMove(2, CameraSetupGetDestPositionY( whichSetup ), 0.0, duration, movetype) call CineCamFieldMove(3, CameraSetupGetField( whichSetup, CAMERA_FIELD_ZOFFSET ) + GetLocationZ(stupid), 0.0, duration, movetype) call CineCamFieldMove(4, CameraSetupGetField( whichSetup, CAMERA_FIELD_ROTATION ), 0.0, duration, movetype) call CineCamFieldMove(5, CameraSetupGetField( whichSetup, CAMERA_FIELD_ANGLE_OF_ATTACK ), 0.0, duration, movetype) call CineCamFieldMove(6, CameraSetupGetField( whichSetup, CAMERA_FIELD_ROLL ), 0.0, duration, movetype) call CineCamFieldMove(7, CameraSetupGetField( whichSetup, CAMERA_FIELD_FIELD_OF_VIEW ), 0.0, duration, movetype) call CineCamFieldMove(8, CameraSetupGetField( whichSetup, CAMERA_FIELD_FARZ ), 0.0, duration, movetype) call CineCamFieldMove(9, CameraSetupGetField( whichSetup, CAMERA_FIELD_TARGET_DISTANCE ), 0.0, duration, movetype) endfunction // ================================================================ function CineCamTargetLock takes unit u, real xoff, real yoff, real zoff, real duration, boolean smooth returns nothing set OvRxyzUnitSec = OvRxyzUnitPrim set OvRxyzUnitPrim = u set OvRxyzSmooth = smooth set OvRoffset[7]=OvRoffset[1] set OvRoffset[8]=OvRoffset[2] set OvRoffset[9]=OvRoffset[3] set OvRoffset[1]=xoff set OvRoffset[2]=yoff set OvRoffset[3]=zoff set OvRxyzTime = 0.0 set OvRxyzEndtime = duration endfunction function CineCamTargetUnlock takes real duration, boolean smooth returns nothing call CineCamTargetLock(null, 0.0,0.0,0.0, duration, smooth) endfunction // ================================================================ function CineCamFacingLock takes unit u, real xoff, real yoff, real zoff, real duration, boolean smooth returns nothing set OvRangUnitSec = OvRangUnitPrim set OvRangUnitPrim = u set OvRangSmooth = smooth set OvRoffset[10]=OvRoffset[4] set OvRoffset[11]=OvRoffset[5] set OvRoffset[12]=OvRoffset[6] set OvRoffset[4]=xoff set OvRoffset[5]=yoff set OvRoffset[6]=zoff set OvRangTime = 0.0 set OvRangEndtime = duration endfunction function CineCamFacingUnlock takes boolean smooth, real duration returns nothing call CineCamFacingLock(null, 0.0,0.0,0.0, duration, smooth) endfunction // ================================================================ function CineCamEnable takes boolean b returns nothing if b then call CineCamMove( GetCurrentCameraSetup(), 0.0, CINECAM_MOVE_INSTANT ) call TimerStart(t, CINECAM_UPDATE_PERIOD, true, function MainLoop ) call ShowUnit(fadeUnit, true) else call ShowUnit(fadeUnit, false) call PauseTimer(t) endif endfunction // ================================================================ function CineCamSmooth takes boolean b returns nothing set ignoreTerrain = not b endfunction // ================================================================ function CineCamSetSpeed takes real r returns nothing set speed = r endfunction function CineCamGetSpeed takes nothing returns real return speed endfunction // ================================================================ private function Init takes nothing returns nothing set t = CreateTimer() if CINECAM_FADE_FILTER_UNIT_TYPE==0 then set fadeUnit = null else set fadeUnit = CreateUnit(Player(12), CINECAM_FADE_FILTER_UNIT_TYPE, 0,0,0) call SetUnitVertexColor(fadeUnit, 255,255,255,0) static if LIBRARY_xebasic then call UnitAddAbility(fadeUnit, XE_HEIGHT_ENABLER) call UnitRemoveAbility(fadeUnit, XE_HEIGHT_ENABLER) else call UnitAddAbility(fadeUnit, HEIGHT_ENABLER) call UnitRemoveAbility(fadeUnit, HEIGHT_ENABLER) endif endif set CINECAM_FADE_FILTER_UNIT = fadeUnit endfunction endlibrary CineScript library:library CineScript requires LinkedList, TimerUtils //***************************************************************** //* CINESCRIPT //* //* written by: Anitarf //* requires: -LinkedList //* -TimerUtils //* //* This is a library intended primarily for cinematic composition //* and playback. It enables you to assemble scripts - each script //* contains a list of actions with matching timestamps which are //* then executed in order once the script is run. //* //* First, a quick API overview: //* //* struct CineScript //* real x //* real y //* real angle //* real speed //* real time //* //* static method create() -> CineScript //* method destroy() //* method run() //* method stop() //* method globalX( real x, real y ) -> real //* method globalY( real x, real y ) -> real //* method globalAngle( real angle ) -> real //* //* struct ScriptAction //* readonly CineScript parent //* readonly real timestamp //* readonly real duration //* //* static method create( CineScript parent, real timestamp, real duration ) -> ScriptAction //* method destroy() //* //* The ScriptAction struct must be extended by new structs to //* actually do anything. These structs can declare their own .run //* method as defined by the Action interface, as well as .update //* and .stop methods if the action has a duration longer than 0. //* Many such extensions are provided in separate libraries. //* //* A ScriptAction is always created for a specific parent script. //* When that script is run, it will wait until it reaches the //* timestamp of the action, at which point it will call the .run //* method of that action. Furthermore, if the speed of the script //* is modified during the duration of an action, the .update //* method of that action will be called, allowing it to adapt to //* the new speed. This allows scripts to be fast-forwarded, run //* in slow motion or even be paused at any time. Likewise, if the //* script is stopped during the duration of an action, the .stop //* method of that action will be called. //* //* You can also skip to a specific timestamp by changing the time //* value, however actions with timestamps before that time value //* will not be run, so any changes they may make to the gamestate //* will not take effect. //* //* In addition to speed, a CineScript can have a custom position //* and facing direction. This way, you can for example script a //* combination of unit movements and special effects for a spell //* and then run this script at any point where the unit uses that //* spell. Unlike script speed, position and direction are meant //* to be set before the script is run. These values may still be //* modified once the script is running, but the position of //* already processed actions will not be updated. The methods //* .globalX, .globalY and .globalAngle can be used to convert an //* action's local coordinates to global values. The direction is //* measured in degrees. //* //* Destroying a script will also destroy all actions registered //* with it, so there is no need to keep track of them, however //* you may also destroy individual actions as long as the action //* being destroyed is not currently being processed. //***************************************************************** private interface Action // This method will never be called if the script is paused (speed set to 0.0), // so if the method contains division by speed that will never result in division by 0. method run takes nothing returns nothing defaults nothing // This method will never be called for instant actions (duration of 0.0), // so if the method contains division by duration that will never result in division by 0. method update takes real timeElapsed returns nothing defaults nothing // This method will never be called for instant actions (duration of 0.0), // so if the method contains division by duration that will never result in division by 0. method stop takes nothing returns nothing defaults nothing endinterface // END OF CALIBRATION SECTION // ================================================================ private keyword addAction // This CineScript method may not be called from outside this library. // It is instead called automatically when you create an action. struct CineScript // Script properties. real x=0.0 real y=0.0 private real a=0.0 // Use the angle operators to read/modify. private real s=1.0 // Use the speed operators to read/modify. // Readonly properties. readonly real duration=0.0 // Internal variables. private timer t private boolean new=true // Safety boolean in case a script is restarted while processing. private boolean running=false // Is the script currently running (does it have an active timer)? private boolean processing=false // Are script actions currently being executed? private real timestamp private real timeskip=0.0 // From where is the script being played? private List list // The ordered list of actions. private Link pending // The next action to be processed. // ---------------------------------------------------------------- // This method is called only from the create method of ScriptAction. // It can't be called from elsewhere beause it is private to this library. // It ensures all actions added to the list are properly ordered. method addAction takes ScriptAction a, real timestamp, real duration returns Link local Link l=.list.last if .duration < timestamp+duration then set .duration = timestamp+duration endif if l==0 or ScriptAction(l.data).timestamp<=timestamp then return Link.createLast(.list, integer(a)) endif loop exitwhen l.prev==0 or ScriptAction(l.prev.data).timestamp<=timestamp set l=l.prev endloop return l.insertBefore(integer(a)) endmethod // ---------------------------------------------------------------- private static method timerEndCallback takes nothing returns nothing call CineScript(GetTimerData(GetExpiredTimer())).stop() endmethod private static method timerCallback takes nothing returns nothing local CineScript this=CineScript(GetTimerData(GetExpiredTimer())) local ScriptAction a=ScriptAction(.pending.data) set .new=false set .processing=true set .timestamp=a.timestamp loop exitwhen not (.s>0.0) // If the script was paused then quit. call a.run() // Run the action. exitwhen not(.running) or .new // If an action stopped or restarted the script then quit. set .pending=.pending.next exitwhen .pending==0 // If this was the last action then quit. set a=ScriptAction(.pending.data) exitwhen a.timestamp>.timestamp // Next action needs a timer, quit. endloop set .processing=false if .new or not (.s>0.0) or not .running then // Script was destroyed and remade while processing, // it was paused or it was stopped, do nothing. elseif .pending!=0 then // Script needs a wait before the next action, run the timer. call TimerStart(.t, (a.timestamp-.timestamp)/.s, false, function CineScript.timerCallback) else // The script finished running, run the finish timer. call TimerStart(.t, (.duration-.timestamp)/.s, false, function CineScript.timerEndCallback) endif endmethod // ---------------------------------------------------------------- // Public methods: method stop takes nothing returns nothing local ScriptAction a local Link l if .running then call ReleaseTimer(.t) set .running=false // Loop through all the actions run so far. set l=.list.first loop exitwhen l==.pending set a=ScriptAction(l.data) // Stop the action if it was run and is still running. if a.timestamp>=.timeskip and a.timestamp+a.duration>.timestamp and a.stop.exists then call a.stop() endif set l=l.next endloop set .timeskip=0.0 endif endmethod method run takes nothing returns nothing call .stop() set .timestamp=.timeskip set .pending=.list.first loop if .pending==0 then return endif exitwhen ScriptAction(.pending.data).timestamp>=.timestamp set .pending=.pending.next endloop set .t=NewTimer() call SetTimerData(.t, this) if .s!=0.0 then call TimerStart(.t, (ScriptAction(.pending.data).timestamp-.timestamp)/.s, false, function CineScript.timerCallback) endif set .running=true set .new=true endmethod // Potentially inline-friendly methods intended to be used mainly by script actions. method globalX takes real x, real y returns real return .x + Cos(.a)*x - Sin(.a)*y endmethod method globalY takes real x, real y returns real return .y + Sin(.a)*x + Cos(.a)*y endmethod method globalAngle takes real angle returns real return .a*bj_RADTODEG + angle endmethod // ---------------------------------------------------------------- // Public operators: method operator time takes nothing returns real if .running then if .s==0.0 then // If the script is paused then return the stored timestamp. return .timestamp elseif .processing then // If the script is being processed then return current action's timestamp. return ScriptAction(.pending.data).timestamp elseif .pending!=0 then // If the script is waiting for the next action then calculate the timestamp. return ScriptAction(.pending.data).timestamp - TimerGetRemaining(.t)*.s else // If the script is finishing then calculate the timestamp. return .duration - TimerGetRemaining(.t)*.s endif endif // If the script is not running, return timeskip. return .timeskip endmethod method operator time= takes real newTime returns nothing if .running then call .stop() set .timeskip=newTime call .run() else set .timeskip=newTime endif endmethod method operator speed takes nothing returns real return .s endmethod method operator speed= takes real newSpeed returns nothing local ScriptAction a local Link l if newSpeed<0.0 then set newSpeed=0.0 // Correct faulty inputs. endif // Get the current timestamp of the script, then update the speed. set .timestamp=.time set .s=newSpeed // If the script is running, update the timer and old actions. if .running then call PauseTimer(.t) // Restart the timer if needed. if not(.processing) and .s>0.0 then if .pending!=0 then call TimerStart(.t, (ScriptAction(.pending.data).timestamp - .timestamp)/.s, false, function CineScript.timerCallback) else call TimerStart(.t, (.duration - .timestamp)/.s, false, function CineScript.timerEndCallback) endif endif // Loop through all the actions run so far. set l=.list.first loop exitwhen l==.pending set a=ScriptAction(l.data) // Update the action if it was run and is still running. if a.timestamp>=.timeskip and a.timestamp+a.duration>.timestamp and a.update.exists then call a.update(.timestamp-a.timestamp) endif set l=l.next endloop endif endmethod method operator angle takes nothing returns real return .a*bj_RADTODEG endmethod method operator angle= takes real newAngle returns nothing set .a = newAngle*bj_DEGTORAD endmethod // ---------------------------------------------------------------- // Constructor and destructor: static method create takes nothing returns CineScript local CineScript this=CineScript.allocate() set .list=List.create() return this endmethod method onDestroy takes nothing returns nothing // Release the timer if needed. call .stop() // Destroy all the actions belonging to the script. loop exitwhen .list.first==0 call ScriptAction(.list.first.data).destroy() endloop call .list.destroy() endmethod endstruct // ================================================================ struct ScriptAction extends Action private Link l // Readonly values. readonly CineScript parent readonly real timestamp readonly real duration static method create takes CineScript s, real timestamp, real duration returns ScriptAction local ScriptAction this=ScriptAction.allocate() if duration<0.0 then set duration=0.0 endif set .timestamp=timestamp set .duration=duration set .parent=s set .l=s.addAction(this, timestamp, duration) return this endmethod method onDestroy takes nothing returns nothing call .l.destroy() endmethod endstruct endlibrary |
| 07-02-2011, 12:14 PM | #2 |
Finally! This is amazing Ani! |
| 07-02-2011, 07:29 PM | #3 |
Nice to see the this cinematic system finally converted into vJass. I don't see the previous "gc()" calls which, in itself, is a huge change for the better. The local real array in the CineCam library could be turned into something more efficient, like a global array or a series of individual, properly named variables, due to the huge overhead of local arrays. I hope that this sees some good public usage. |
| 07-02-2011, 09:39 PM | #4 | |||
Quote:
Don't get this the wrong way, I know you didn't mean to say that this was "just a port", I just wanted to point out anyway that considerable improvements have been made beyond the obvious "it's in vJass now", since the first post doesn't mention that beyond the initial description. Quote:
Quote:
|
| 07-02-2011, 10:41 PM | #5 | |
Quote:
But local variables are quite faster than globals, so I think that the use of a local array is absolutely justified, at least in this case. Perhaps you could use a linked list (similar to the one in grim001's ListModule) instead of an array when looping through the struct instances in the Transition library. This should most definitely see some good usage, although WC3 modding is slowly dying. |
| 07-03-2011, 10:01 PM | #6 | |
Quote:
Citation needed. My testing I seem to recall showed no discernible difference in speed. |
| 07-03-2011, 10:14 PM | #7 | |
Quote:
And you know it yourself that vJass can generate hundreds of globals, which in turn makes them slower. |
| 07-03-2011, 11:11 PM | #8 | |
Quote:
Why would you be very sure of that? Consider that we aren't just dealing with a VM, we're dealing with a Blizzard VM. |
| 07-04-2011, 12:38 PM | #9 | |||
Quote:
Quote:
Quote:
|
| 07-04-2011, 02:35 PM | #10 | |||
Quote:
"I once had the idea that it would be better to use only global vars in my engine in order to avoid repeatedly initializing locals. The result is that the whole engine ran about 20% slower.Perhaps you trust him more than you trust me. Quote:
Quote:
But anyway, here's a post by weaaddar: "An array in jass will always use 8192 elements. No matter if your only using 1 or 400. Its very bad to use arrays that you do not need to use. As each type in war3 uses a 4kb integer as its pointer. So you are using roughly 32kb of ram per array. Sure it doesn't seem like much but remember that types also have there own memory usage on the handling stack." |
| 07-06-2011, 11:25 AM | #11 |
Amazing sample cinematic, I crave more. |
| 07-10-2011, 04:00 AM | #12 |
Awesome! I was always waiting for vJASS cine libs. I'll definitely check this out. |
| 08-18-2011, 09:04 PM | #13 |
Wow, this is a really incredible system, wish I had noticed this earlier! Learning to use it now, I first thought of using your old system, but was put off by a lot of outdated stuff (which, still, worked really well in practice). |
| 01-29-2014, 12:28 PM | #14 |
Reupload map? |
| 01-29-2014, 04:09 PM | #15 | |
Quote:
I have some further updates planned for this, at the moment I am waiting to see if I'll be able to convince Vex to add a feature I'd like to use to JassHelper. |
