Browse Source

- Moved and renamed the "setTimerOn()" and "setTimerOff()" functionality from SceneObject to SimObject. These are now named "startTimer()" and "stopTimer()" and now allow the callback function to be specified as well as the timer period and an optional repeat count which if not specified or specified as zero will repeat infinitely.
- Modified all the toys to use "startTimer()" as it's far less verbose than the ".schedule()" equivalent in these cases.
- Updated Win32 (VS2010/VS2012), OSX & iOS builds.

MelvMay-GG 12 years ago
parent
commit
696bf04d2c

+ 1 - 0
engine/compilers/VisualStudio 2010/Torque 2D.vcxproj

@@ -1003,6 +1003,7 @@
     <ClInclude Include="..\..\source\sim\simObject.h" />
     <ClInclude Include="..\..\source\sim\SimObjectList.h" />
     <ClInclude Include="..\..\source\sim\simObjectPtr.h" />
+    <ClInclude Include="..\..\source\sim\simObjectTimerEvent.h" />
     <ClInclude Include="..\..\source\sim\simSet.h" />
     <ClInclude Include="..\..\source\string\findMatch.h" />
     <ClInclude Include="..\..\source\string\stringBuffer.h" />

+ 3 - 0
engine/compilers/VisualStudio 2010/Torque 2D.vcxproj.filters

@@ -2586,6 +2586,9 @@
     <ClInclude Include="..\..\source\persistence\taml\tamlCustom.h">
       <Filter>persistence\taml</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\source\sim\simObjectTimerEvent.h">
+      <Filter>sim</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">

+ 1 - 1
engine/compilers/VisualStudio 2012/Torque 2D.vcxproj

@@ -647,7 +647,6 @@
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectMoveToEvent.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectRotateToEvent.h" />
-    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectTimerEvent.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObject_ScriptBinding.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\Scroller.h" />
     <ClInclude Include="..\..\source\2d\sceneobject\Scroller_ScriptBinding.h" />
@@ -1009,6 +1008,7 @@
     <ClInclude Include="..\..\source\sim\simObject.h" />
     <ClInclude Include="..\..\source\sim\SimObjectList.h" />
     <ClInclude Include="..\..\source\sim\simObjectPtr.h" />
+    <ClInclude Include="..\..\source\sim\simObjectTimerEvent.h" />
     <ClInclude Include="..\..\source\sim\simSet.h" />
     <ClInclude Include="..\..\source\string\findMatch.h" />
     <ClInclude Include="..\..\source\string\stringBuffer.h" />

+ 3 - 3
engine/compilers/VisualStudio 2012/Torque 2D.vcxproj.filters

@@ -2348,9 +2348,6 @@
     <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectRotateToEvent.h">
       <Filter>2d\sceneobject</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\source\2d\sceneobject\SceneObjectTimerEvent.h">
-      <Filter>2d\sceneobject</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\source\2d\sceneobject\Scroller.h">
       <Filter>2d\sceneobject</Filter>
     </ClInclude>
@@ -2586,6 +2583,9 @@
     <ClInclude Include="..\..\source\persistence\taml\tamlCustom.h">
       <Filter>persistence\taml</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\source\sim\simObjectTimerEvent.h">
+      <Filter>sim</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">

+ 2 - 2
engine/compilers/Xcode/Torque2D.xcodeproj/project.pbxproj

@@ -459,6 +459,7 @@
 		2AC4404416B0142B00FC4091 /* ImageFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFont.h; sourceTree = "<group>"; };
 		2AC5C7E71667C85700A0D046 /* platformStringTests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = platformStringTests.cc; path = ../../../source/testing/tests/platformStringTests.cc; sourceTree = "<group>"; };
 		2ACFC0A7166CE1AB00FE7370 /* platformMemoryTests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = platformMemoryTests.cc; path = ../../../source/testing/tests/platformMemoryTests.cc; sourceTree = "<group>"; };
+		2AD07B2616D15F5A0070DC79 /* simObjectTimerEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simObjectTimerEvent.h; sourceTree = "<group>"; };
 		2AD35A541663608E00C75F30 /* platformFileIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platformFileIO.h; sourceTree = "<group>"; };
 		2ADCAC0E16A41E4400E07619 /* tamlChildren.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tamlChildren.h; sourceTree = "<group>"; };
 		2ADCAC1016A41E5500E07619 /* ParticleAsset_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticleAsset_ScriptBinding.h; sourceTree = "<group>"; };
@@ -746,7 +747,6 @@
 		86BC7EC516518D4600D96ADF /* SceneObject_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObject_ScriptBinding.h; sourceTree = "<group>"; };
 		86BC7EC816518D4600D96ADF /* SceneObjectMoveToEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectMoveToEvent.h; sourceTree = "<group>"; };
 		86BC7EC916518D4600D96ADF /* SceneObjectRotateToEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectRotateToEvent.h; sourceTree = "<group>"; };
-		86BC7ECD16518D4600D96ADF /* SceneObjectTimerEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectTimerEvent.h; sourceTree = "<group>"; };
 		86BC7ECE16518D4600D96ADF /* Scroller.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Scroller.cc; sourceTree = "<group>"; };
 		86BC7ECF16518D4600D96ADF /* Scroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scroller.h; sourceTree = "<group>"; };
 		86BC7ED016518D4600D96ADF /* Scroller_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scroller_ScriptBinding.h; sourceTree = "<group>"; };
@@ -1868,7 +1868,6 @@
 				86BC7EC516518D4600D96ADF /* SceneObject_ScriptBinding.h */,
 				86BC7EC816518D4600D96ADF /* SceneObjectMoveToEvent.h */,
 				86BC7EC916518D4600D96ADF /* SceneObjectRotateToEvent.h */,
-				86BC7ECD16518D4600D96ADF /* SceneObjectTimerEvent.h */,
 				86BC7ECE16518D4600D96ADF /* Scroller.cc */,
 				86BC7ECF16518D4600D96ADF /* Scroller.h */,
 				86BC7ED016518D4600D96ADF /* Scroller_ScriptBinding.h */,
@@ -2580,6 +2579,7 @@
 		86BC812C16518D4600D96ADF /* sim */ = {
 			isa = PBXGroup;
 			children = (
+				2AD07B2616D15F5A0070DC79 /* simObjectTimerEvent.h */,
 				86BC812D16518D4600D96ADF /* scriptGroup.cc */,
 				86BC812E16518D4600D96ADF /* scriptGroup.h */,
 				86BC812F16518D4600D96ADF /* scriptObject.cc */,

+ 2 - 2
engine/compilers/Xcode_iOS/Torque2D.xcodeproj/project.pbxproj

@@ -478,6 +478,7 @@
 		2AC4404B16B0144500FC4091 /* ImageFont_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFont_ScriptBinding.h; sourceTree = "<group>"; };
 		2AC4404C16B0144500FC4091 /* ImageFont.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFont.cc; sourceTree = "<group>"; };
 		2AC4404D16B0144500FC4091 /* ImageFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFont.h; sourceTree = "<group>"; };
+		2AD07B2716D15F8E0070DC79 /* simObjectTimerEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simObjectTimerEvent.h; sourceTree = "<group>"; };
 		2AED7D9216B70102003482CF /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
 		2AF1C54716B439D900C1CF3A /* declaredAssets.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = declaredAssets.cc; sourceTree = "<group>"; };
 		2AF1C54816B439D900C1CF3A /* declaredAssets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = declaredAssets.h; sourceTree = "<group>"; };
@@ -634,7 +635,6 @@
 		867BAD5416AEC9050033868F /* SceneObject_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObject_ScriptBinding.h; sourceTree = "<group>"; };
 		867BAD5716AEC9050033868F /* SceneObjectMoveToEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectMoveToEvent.h; sourceTree = "<group>"; };
 		867BAD5816AEC9050033868F /* SceneObjectRotateToEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectRotateToEvent.h; sourceTree = "<group>"; };
-		867BAD5C16AEC9050033868F /* SceneObjectTimerEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneObjectTimerEvent.h; sourceTree = "<group>"; };
 		867BAD5D16AEC9050033868F /* Scroller.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Scroller.cc; sourceTree = "<group>"; };
 		867BAD5E16AEC9050033868F /* Scroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scroller.h; sourceTree = "<group>"; };
 		867BAD5F16AEC9050033868F /* Scroller_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scroller_ScriptBinding.h; sourceTree = "<group>"; };
@@ -1620,7 +1620,6 @@
 				867BAD5416AEC9050033868F /* SceneObject_ScriptBinding.h */,
 				867BAD5716AEC9050033868F /* SceneObjectMoveToEvent.h */,
 				867BAD5816AEC9050033868F /* SceneObjectRotateToEvent.h */,
-				867BAD5C16AEC9050033868F /* SceneObjectTimerEvent.h */,
 				867BAD5D16AEC9050033868F /* Scroller.cc */,
 				867BAD5E16AEC9050033868F /* Scroller.h */,
 				867BAD5F16AEC9050033868F /* Scroller_ScriptBinding.h */,
@@ -2425,6 +2424,7 @@
 		867BAFB516AEC9050033868F /* sim */ = {
 			isa = PBXGroup;
 			children = (
+				2AD07B2716D15F8E0070DC79 /* simObjectTimerEvent.h */,
 				867BAFB616AEC9050033868F /* scriptGroup.cc */,
 				867BAFB716AEC9050033868F /* scriptGroup.h */,
 				867BAFB816AEC9050033868F /* scriptObject.cc */,

+ 0 - 5
engine/source/2d/sceneobject/SceneObject.cc

@@ -52,10 +52,6 @@
 #include "component/behaviors/behaviorTemplate.h"
 #endif
 
-#ifndef _SCENE_OBJECT_TIMER_EVENT_H_
-#include "2d/sceneobject/SceneObjectTimerEvent.h"
-#endif
-
 #ifndef _SCENE_OBJECT_MOVE_TO_EVENT_H_
 #include "2d/sceneobject/SceneObjectMoveToEvent.h"
 #endif
@@ -204,7 +200,6 @@ SceneObject::SceneObject() :
     mEditorTickAllowed(true),
     mPickingAllowed(true),
     mAlwaysInScope(false),
-    mPeriodicTimerID(0),
     mMoveToEventId(0),
     mRotateToEventId(0),
     mSerialId(0),

+ 0 - 3
engine/source/2d/sceneobject/SceneObject.h

@@ -220,7 +220,6 @@ protected:
     bool                    mEditorTickAllowed;
     bool                    mPickingAllowed;
     bool                    mAlwaysInScope;
-    S32                     mPeriodicTimerID;
     U32                     mMoveToEventId;
     U32                     mRotateToEventId;
     U32                     mSerialId;
@@ -564,8 +563,6 @@ public:
     inline const char*      scriptThis(void) const                      { return Con::getIntArg(getId()); }
     inline bool             getIsPickingAllowed(void) const             { return mPickingAllowed; }
     inline bool             getIsAlwaysInScope(void) const              { return mAlwaysInScope; }
-    inline void             setPeriodicTimerID( S32 timerID )           { mPeriodicTimerID = timerID; }
-    inline S32              getPeriodicTimerID( void ) const            { return mPeriodicTimerID; }
     inline void             setWorldQueryKey( const U32 key )           { mWorldQueryKey = key; }
     inline U32              getWorldQueryKey( void ) const              { return mWorldQueryKey; }
     BehaviorInstance*       behavior(const char *name);

+ 0 - 44
engine/source/2d/sceneobject/SceneObject_ScriptBinding.h

@@ -3730,50 +3730,6 @@ ConsoleMethod(SceneObject, safeDelete, void, 2, 2, "() - Safely deletes object.\
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod(SceneObject, setTimerOn, void, 3, 3, "(float timePeriod) - Starts a periodic timer for this object.\n"
-                                                      "Sets a timer on the object that, when it expires, will cause the object to execute the onTimer() callback.\n"
-                                                      "The timer event will continue to occur at regular intervals until setTimerOff() is called.\n"
-                                                      "@param timePeriod The period of time, in milliseconds, between each callback.\n"
-                                                    "@return No return Value.")
-{
-    // Is the periodic timer running?
-    if ( object->getPeriodicTimerID() != 0 )
-    {
-        // Yes, so cancel it.
-        Sim::cancelEvent( object->getPeriodicTimerID() );
-
-        // Reset Timer ID.
-        object->setPeriodicTimerID( 0 );
-    }
-
-    // Fetch Time-Delta.
-    U32 timeDelta = U32(dAtof(argv[2]));
-
-    // Create Timer Event.
-    SceneObjectTimerEvent* pEvent = new SceneObjectTimerEvent( timeDelta );
-
-    // Post Event.
-    object->setPeriodicTimerID( Sim::postEvent( object, pEvent, Sim::getCurrentTime() + timeDelta ) );
-}
-
-//-----------------------------------------------------------------------------
-
-ConsoleMethod(SceneObject, setTimerOff, void, 2, 2, "() - Stops the periodic timer for this object.\n"
-                                                                 "@return No return Value.")
-{
-    // Finish if the periodic timer isn't running.
-    if ( object->getPeriodicTimerID() == 0 )
-        return;
-
-    // Cancel It.
-    Sim::cancelEvent( object->getPeriodicTimerID() );
-
-    // Reset Timer ID.
-    object->setPeriodicTimerID( 0 );
-}
-
-//-----------------------------------------------------------------------------
-
 ConsoleMethod(SceneObject, behavior, S32, 3, 3, "(string behaviorName) - Gets the behavior instance ID off of the object based on the behavior name passed.\n"
                                                    "@param behaviorName The name of the behavior you want to get the instance ID of.\n"
                                                    "@return (integer behaviorID) The id of the behavior instance.")

+ 81 - 1
engine/source/sim/simObject.cc

@@ -21,6 +21,7 @@
 //-----------------------------------------------------------------------------
 
 #include "sim/simObject.h"
+#include "sim/simObjectTimerEvent.h"
 #include "console/consoleInternal.h"
 #include "console/codeBlock.h"
 #include "console/consoleInternal.h"
@@ -59,10 +60,11 @@ SimObject::SimObject( const U8 namespaceLinkMask ) : mNSLinkMask( namespaceLinkM
     mTypeMask                = 0;
     mScriptCallbackGuard     = 0;
     mFieldDictionary         = NULL;
-    mCanSaveFieldDictionary	=	true;
+    mCanSaveFieldDictionary	 = true;
     mClassName               = NULL;
     mSuperClassName          = NULL;
     mProgenitorFile          = CodeBlock::getCurrentCodeBlockFullPath();
+    mPeriodicTimerID         = 0;
 }
 
 //---------------------------------------------------------------------------
@@ -1696,3 +1698,81 @@ ConsoleMethod(SimObject, getProgenitorFile, const char*, 2, 2,  "() Gets the pro
     return object->getProgenitorFile();
 }
 
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SimObject, startTimer, bool, 4, 5,    "(callbackFunction, float timePeriod, [repeat]) - Starts a periodic timer for this object.\n"
+                                                    "Sets a timer on the object that, when it expires, will cause the object to execute the onTimer() callback.\n"
+                                                    "The timer event will continue to occur at regular intervals until setTimerOff() is called.\n"
+                                                    "@param callbackFunction The name of the callback function to call for each timer repetition.\n"
+                                                    "@param timePeriod The period of time (in milliseconds) between each callback.\n"
+                                                    "@param repeat The number of times the timer should repeat.  If not specified or zero then it will run infinitely\n"
+                                                    "@return No return Value.")
+{
+    // Is the periodic timer running?
+    if ( object->getPeriodicTimerID() != 0 )
+    {
+        // Yes, so cancel it.
+        Sim::cancelEvent( object->getPeriodicTimerID() );
+
+        // Reset Timer ID.
+        object->setPeriodicTimerID( 0 );
+    }
+
+    // Fetch the callback function.
+    StringTableEntry callbackFunction = StringTable->insert( argv[2] );
+
+    // Does the function exist?
+    if ( !object->isMethod( callbackFunction ) )
+    {
+        // No, so warn.
+        Con::warnf("SimObject::startTimer() - The callback function of '%s' does not exist.", callbackFunction );
+        return false;
+    }
+
+    // Fetch the time period.
+    const S32 timePeriod = dAtoi(argv[3]);
+
+    // Is the time period valid?
+    if ( timePeriod < 1 )
+    {
+        // No, so warn.
+        Con::warnf("SimObject::startTimer() - The time period of '%d' is invalid.", timePeriod );
+        return false;
+    }        
+
+    // Fetch the repeat count.
+    const S32 repeat = argc >= 5 ? dAtoi(argv[4]) : 0;
+
+    // Create Timer Event.
+    SimObjectTimerEvent* pEvent = new SimObjectTimerEvent( callbackFunction, (U32)timePeriod, (U32)repeat );
+
+    // Post Event.
+    object->setPeriodicTimerID( Sim::postEvent( object, pEvent, Sim::getCurrentTime() + timePeriod ) );
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SimObject, stopTimer, void, 2, 2, "() - Stops the periodic timer for this object.\n"
+                                                "@return No return Value.")
+{
+    // Finish if the periodic timer isn't running.
+    if ( object->getPeriodicTimerID() == 0 )
+        return;
+
+    // Cancel It.
+    Sim::cancelEvent( object->getPeriodicTimerID() );
+
+    // Reset Timer ID.
+    object->setPeriodicTimerID( 0 );
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleMethod(SimObject, isTimerActive, bool, 2, 2, "() - Checks whether the periodic timer is active for this object or not.\n"
+                                                    "@return Whether the periodic timer is active for this object or not.")
+{
+    return object->isPeriodicTimerActive();
+}

+ 7 - 0
engine/source/sim/simObject.h

@@ -284,6 +284,9 @@ private:
 
     StringTableEntry    mProgenitorFile;
 
+    S32 mPeriodicTimerID;
+
+
     /// @name Notification
     /// @{
     Notify*     mNotifyList;
@@ -621,6 +624,10 @@ public:
     inline void setProgenitorFile( const char* pFile ) { mProgenitorFile = StringTable->insert( pFile ); }
     inline StringTableEntry getProgenitorFile( void ) const { return mProgenitorFile; }
 
+    inline void setPeriodicTimerID( const S32 timerID )     { mPeriodicTimerID = timerID; }
+    inline S32 getPeriodicTimerID( void ) const             { return mPeriodicTimerID; }
+    inline bool isPeriodicTimerActive( void ) const         { return mPeriodicTimerID != 0; }
+
     /// @}
 
     /// @name Sets

+ 68 - 55
engine/source/2d/sceneobject/SceneObjectTimerEvent.h → engine/source/sim/simObjectTimerEvent.h

@@ -1,55 +1,68 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2013 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#ifndef _SCENE_OBJECT_TIMER_EVENT_H_
-#define _SCENE_OBJECT_TIMER_EVENT_H_
-
-#ifndef _SCENE_OBJECT_H_
-#include "2d/sceneobject/SceneObject.h"
-#endif
-
-//-----------------------------------------------------------------------------
-
-class SceneObjectTimerEvent : public SimEvent
-{
-public:
-    SceneObjectTimerEvent( U32 timerPeriod ) : mTimerPeriod(timerPeriod) {}
-    virtual  ~SceneObjectTimerEvent() {}
-
-    virtual void process(SimObject *object)
-    {
-        /// Create new Timer Event.
-        SceneObjectTimerEvent* pEvent = new SceneObjectTimerEvent( mTimerPeriod );
-
-        /// Post Event.
-        (dynamic_cast<SceneObject*>(object))->setPeriodicTimerID( Sim::postEvent( object, pEvent, Sim::getCurrentTime() + mTimerPeriod ) );
-
-        // Script callback.
-        /// This *must* be done here in-case the user turns off the timer which would be the one above!
-        Con::executef( object, 1, "onTimer" );
-    }
-
-private:
-    U32 mTimerPeriod;
-};
-
-#endif // _SCENE_OBJECT_TIMER_EVENT_H_
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _SIM_OBJECT_TIMER_EVENT_H_
+#define _SIM_OBJECT_TIMER_EVENT_H_
+
+#ifndef _SIMBASE_H_
+#include "sim/simBase.h"
+#endif
+
+//-----------------------------------------------------------------------------
+
+class SimObjectTimerEvent : public SimEvent
+{
+public:
+    SimObjectTimerEvent( StringTableEntry callbackFunction, const U32 period, const U32 repeat = 0 ) :
+        mCallbackFunction( callbackFunction ),
+        mPeriod( period ),
+        mRepeat( repeat )
+    {}
+    virtual ~SimObjectTimerEvent() {}
+
+    virtual void process(SimObject *object)
+    {
+        // Do we have a repeat count?
+        if ( mRepeat == 0 || mRepeat > 1 )
+        {
+            // Calculate the remaining repeats.
+            const U32 repeat = mRepeat == 0 ? 0 : mRepeat-1;
+
+            // Create new timer event.
+            SimObjectTimerEvent* pEvent = new SimObjectTimerEvent( mCallbackFunction, mPeriod, repeat );
+
+            // Post the event.
+            object->setPeriodicTimerID( Sim::postEvent( object, pEvent, Sim::getCurrentTime() + mPeriod ) );
+        }
+
+        // Script callback.
+        // This *must* be done here in-case the user turns off the timer which would be the one above!
+        Con::executef( object, 1, mCallbackFunction );
+    }
+
+private:
+    StringTableEntry mCallbackFunction;
+    U32 mPeriod;
+    U32 mRepeat;
+};
+
+#endif // _SIM_OBJECT_TIMER_EVENT_H_

+ 16 - 28
modules/AlphaBlendToy/1/main.cs

@@ -37,17 +37,12 @@ function AlphaBlendToy::create( %this )
 
 function AlphaBlendToy::destroy( %this )
 {
-    // Cancel any pending events.
-    AlphaBlendToy.cancelPendingEvents();
 }
 
 //-----------------------------------------------------------------------------
 
 function AlphaBlendToy::reset( %this )
 {
-    // Cancel any pending events.
-    AlphaBlendToy.cancelPendingEvents();
-    
     // Reset the sprite count.
     AlphaBlendToy.SpriteCount = 0;
     
@@ -66,24 +61,10 @@ function AlphaBlendToy::reset( %this )
     // Create background.
     %this.createBackground();
 
-    // Schedule to create a sprite.
-    %this.createSpriteScheduleId = %this.schedule( 50, "createBlendedSprites" );
+    // Start the timer.
+    AlphaBlendToy.startTimer( "createBlendedSprites", 50 );
 }
 
-//-----------------------------------------------------------------------------
-
-function AlphaBlendToy::cancelPendingEvents(%this)
-{
-    // Finish if there are not pending events.
-    if ( !isEventPending(%this.createSpriteScheduleId) )
-        return;
-        
-    // Cancel it.
-    cancel(%this.createSpriteScheduleId);
-    %this.createSpriteScheduleId = "";
-}
-
-
 //-----------------------------------------------------------------------------
 
 function AlphaBlendToy::createBackground( %this )
@@ -127,12 +108,14 @@ function AlphaBlendToy::createBackground( %this )
 
 function AlphaBlendToy::createBlendedSprites( %this )
 {
-    // Reset the event schedule.
-    %this.createSpriteScheduleId = "";
-    
     // Finish if count reached.
     if ( AlphaBlendToy.SpriteCount >= AlphaBlendToy.SpriteCountTotal )
+    {
+        // Stop the timer.
+        AlphaBlendToy.stopTimer();
+        
         return;
+    }
         
     // Create the sprite.
     %object = new Sprite();
@@ -164,7 +147,12 @@ function AlphaBlendToy::createBlendedSprites( %this )
         AlphaBlendToy.CursorX = 0;
         AlphaBlendToy.CursorY++;
         if ( AlphaBlendToy.CursorY == AlphaBlendToy.SpriteCountY )
+        {
+            // Stop the timer.
+            AlphaBlendToy.stopTimer();
+            
             return;
+        }
     }
     
     // Update the sprite cursor alpha.
@@ -175,10 +163,10 @@ function AlphaBlendToy::createBlendedSprites( %this )
     
     // Finish if count reached.
     if ( AlphaBlendToy.SpriteCount == AlphaBlendToy.SpriteCountTotal )
-        return;
-    
-    // Schedule to create a sprite.
-    %this.createSpriteScheduleId = %this.schedule( 50, "createBlendedSprites" );    
+    {
+        // Stop the timer.
+        AlphaBlendToy.stopTimer();       
+    }
 }
 
 //-----------------------------------------------------------------------------

+ 8 - 27
modules/AquariumToy/1/main.cs

@@ -25,7 +25,6 @@ function AquariumToy::create( %this )
     exec("./scripts/aquarium.cs");
 
     // Configure settings.
-    AquariumToy.createFishScheduleId = "";
     AquariumToy.maxFish = 10;
     AquariumToy.currentFish = 0;
     AquariumToy.selectedAnimation = "AquariumToy:angelfish1Anim";
@@ -51,8 +50,6 @@ function AquariumToy::create( %this )
 
 function AquariumToy::destroy( %this )
 {
-    // Cancel any pending events.
-    AquariumToy::cancelPendingEvents();
 }
 
 //-----------------------------------------------------------------------------
@@ -71,33 +68,14 @@ function AquariumToy::reset(%this)
     // Reset the ball count.
     %this.currentFish = 0;
 
-    // Cancel any pending events.
-    AquariumToy::cancelPendingEvents();
-
-    // Schedule to create a ball.
-    %this.createFishScheduleId = %this.schedule( 100, "spawnFish" );
-}
-
-//-----------------------------------------------------------------------------
-
-function AquariumToy::cancelPendingEvents(%this)
-{
-    // Finish if there are not pending events.
-    if ( !isEventPending(%this.createFishScheduleId) )
-        return;
-
-    // Cancel it.
-    cancel(%this.createFishScheduleId);
-    %this.createFishScheduleId = "";
+    // Start the timer.
+    AquariumToy.startTimer( "spawnFish", 100 );
 }
 
 //-----------------------------------------------------------------------------
 
 function AquariumToy::spawnFish(%this)
 {
-    // Reset the event schedule.
-    %this.createFishScheduleId = "";
-
     %position = getRandom(-55, 55) SPC getRandom(-20, 20);
     %index = getRandom(0, 5);
     %anim = getUnit(getFishAnimationList(), %index, ",");
@@ -125,9 +103,12 @@ function AquariumToy::spawnFish(%this)
 
     %this.currentFish++;
 
-    // Schedule to spawn a fish.
-    if ( %this.currentFish < %this.maxFish)
-        %this.createFishScheduleId = %this.schedule( 100, "spawnFish" );
+    // Have we reached the maximum number of fish?
+    if ( %this.currentFish >= %this.maxFish)
+    {
+        // Yes, so stop the timer.
+        AquariumToy.stopTimer();
+    }
 }
 
 //-----------------------------------------------------------------------------

+ 11 - 27
modules/SphereStackToy/1/main.cs

@@ -46,17 +46,12 @@ function SphereStackToy::create( %this )
 
 function SphereStackToy::destroy( %this )
 {   
-    // Cancel any pending events.
-    SphereStackToy::cancelPendingEvents();
 }
 
 //-----------------------------------------------------------------------------
 
 function SphereStackToy::reset(%this)
 {
-    // Cancel any pending events.
-    SphereStackToy::cancelPendingEvents();
-    
     // Clear the scene.
     SandboxScene.clear();
     
@@ -78,21 +73,8 @@ function SphereStackToy::reset(%this)
     // Create the ground.
     %this.createGround();
         
-    // Schedule to create a ball.
-    %this.createBallScheduleId = %this.schedule( 100, "createBall" );
-}
-
-//-----------------------------------------------------------------------------
-
-function SphereStackToy::cancelPendingEvents(%this)
-{
-    // Finish if there are not pending events.
-    if ( !isEventPending(%this.createBallScheduleId) )
-        return;
-        
-    // Cancel it.
-    cancel(%this.createBallScheduleId);
-    %this.createBallScheduleId = "";
+    // Start the timer.
+    SphereStackToy.startTimer( "createball", 100 );
 }
 
 //-----------------------------------------------------------------------------
@@ -150,12 +132,14 @@ function SphereStackToy::createGround( %this )
 
 function SphereStackToy::createBall(%this)
 {
-    // Reset the event schedule.
-    %this.createBallScheduleId = "";
-
     // Finish if exceeded the required number of balls.
     if ( %this.currentBalls >= %this.maxBalls)
+    {
+        // Stop the timer.
+        SphereStackToy.stopTimer();
+        
         return;
+    }
 
     // Set the ball size.
     %ballSize = 2;
@@ -188,10 +172,10 @@ function SphereStackToy::createBall(%this)
     
     // Finish if exceeded the required number of balls.
     if ( %this.currentBalls == %this.maxBalls)
-        return;
-
-    // Schedule to create a ball.
-    %this.createBallScheduleId = %this.schedule( 150, "createBall" );
+    {
+        // Stop the timer.
+        SphereStackToy.stopTimer();
+    }
 }
 
 //-----------------------------------------------------------------------------

+ 37 - 48
modules/SpriteStressToy/1/main.cs

@@ -60,17 +60,12 @@ function SpriteStressToy::create( %this )
 
 function SpriteStressToy::destroy( %this )
 {
-    // Cancel any pending events.
-    SpriteStressToy::cancelPendingEvents();    
 }
 
 //-----------------------------------------------------------------------------
 
 function SpriteStressToy::reset( %this )
 {
-    // Cancel any pending events.
-    SpriteStressToy::cancelPendingEvents();
-    
     // Reset sprite count.
     SpriteStressToy.SpriteCount = 0;
 
@@ -97,7 +92,7 @@ function SpriteStressToy::reset( %this )
     if ( SpriteStressToy.RenderMode $= "Static" )
     {
         // Create the static sprites.
-        %this.createStaticSprites();
+        SpriteStressToy.startTimer( "createStaticSprites", SpriteStressToy.SpriteCreatePeriod );
         
         return;
     }
@@ -106,7 +101,7 @@ function SpriteStressToy::reset( %this )
     if ( SpriteStressToy.RenderMode $= "Static (Composite)" )
     {
         // Create the static composite sprites.
-        %this.createStaticCompositeSprites();    
+        SpriteStressToy.startTimer( "createStaticCompositeSprites", SpriteStressToy.SpriteCreatePeriod );
     
         return;
     }
@@ -115,7 +110,7 @@ function SpriteStressToy::reset( %this )
     if ( SpriteStressToy.RenderMode $= "Animated" )
     {
         // Create the animated sprites.
-        %this.createAnimatedSprites();    
+        SpriteStressToy.startTimer( "createAnimatedSprites", SpriteStressToy.SpriteCreatePeriod );
     
         return;
     }    
@@ -124,7 +119,7 @@ function SpriteStressToy::reset( %this )
     if ( SpriteStressToy.RenderMode $= "Animated (Composite)" )
     {
         // Create the animated composite sprites.
-        %this.createAnimatedCompositeSprites();    
+        SpriteStressToy.startTimer( "createAnimatedCompositeSprites", SpriteStressToy.SpriteCreatePeriod );
     
         return;
     }    
@@ -133,20 +128,6 @@ function SpriteStressToy::reset( %this )
     error( "SpriteStressToy::reset() - Unknown render mode." );
 }
 
-
-//-----------------------------------------------------------------------------
-
-function SpriteStressToy::cancelPendingEvents(%this)
-{
-    // Finish if there are not pending events.
-    if ( !isEventPending(%this.createSpriteScheduleId) )
-        return;
-        
-    // Cancel it.
-    cancel(%this.createSpriteScheduleId);
-    %this.createSpriteScheduleId = "";
-}
-
 //-----------------------------------------------------------------------------
 
 function SpriteStressToy::createBackground( %this )
@@ -249,12 +230,14 @@ function SpriteStressToy::updateRenderSortMode( %this )
 
 function SpriteStressToy::createStaticSprites( %this )
 {
-    // Reset the event schedule.
-    %this.createSpriteScheduleId = "";
-    
     // Finish if max sprites reached.
     if ( SpriteStressToy.SpriteCount >= SpriteStressToy.MaxSprite )
+    {
+        // Stop the timer.
+        SpriteStressToy.stopTimer();
+        
         return;
+    }
         
     // Create the sprites at the specified rate.
     for( %n = 0; %n < SpriteStressToy.SpriteCreateRate; %n++ )
@@ -299,7 +282,10 @@ function SpriteStressToy::createStaticSprites( %this )
         if ( SpriteStressToy.SpriteCount == SpriteStressToy.MaxSprite )
         {
             // Update the overlay.
-            %this.updateSpriteCountOverlay();              
+            %this.updateSpriteCountOverlay();
+            
+            // Stop the timer.
+            SpriteStressToy.stopTimer();         
             
             return;
         }
@@ -307,21 +293,20 @@ function SpriteStressToy::createStaticSprites( %this )
     
     // Update the overlay.
     %this.updateSpriteCountOverlay();  
-        
-    // Schedule to create more sprites.
-    %this.createSpriteScheduleId = %this.schedule( SpriteStressToy.SpriteCreatePeriod, "createStaticSprites" );        
 }
 
 //-----------------------------------------------------------------------------
 
 function SpriteStressToy::createAnimatedSprites( %this )
 {
-    // Reset the event schedule.
-    %this.createSpriteScheduleId = "";
-    
     // Finish if max sprites reached.
     if ( SpriteStressToy.SpriteCount >= SpriteStressToy.MaxSprite )
+    {
+        // Stop the timer.
+        SpriteStressToy.stopTimer();
+        
         return;
+    }
 
     // Create the sprites at the specified rate.
     for( %n = 0; %n < SpriteStressToy.SpriteCreateRate; %n++ )
@@ -365,27 +350,29 @@ function SpriteStressToy::createAnimatedSprites( %this )
             // Update the overlay.
             %this.updateSpriteCountOverlay();              
             
+            // Stop the timer.
+            SpriteStressToy.stopTimer();         
+            
             return;
         }
     }
     
     // Update the overlay.
     %this.updateSpriteCountOverlay();  
-    
-    // Schedule to create more sprites.
-    %this.createSpriteScheduleId = %this.schedule( SpriteStressToy.SpriteCreatePeriod, "createAnimatedSprites" );        
 }
 
 //-----------------------------------------------------------------------------
 
 function SpriteStressToy::createStaticCompositeSprites( %this )
 {
-    // Reset the event schedule.
-    %this.createSpriteScheduleId = "";
-    
     // Finish if max sprites reached.
     if ( SpriteStressToy.SpriteCount >= SpriteStressToy.MaxSprite )
+    {
+        // Stop the timer.
+        SpriteStressToy.stopTimer();
+        
         return;
+    }
 
     // Do we have a composite sprite yet?
     if ( !isObject(SpriteStressToy.CompositeSpriteObject) )
@@ -450,27 +437,29 @@ function SpriteStressToy::createStaticCompositeSprites( %this )
             // Update the overlay.
             %this.updateSpriteCountOverlay();              
             
+            // Stop the timer.
+            SpriteStressToy.stopTimer();         
+            
             return;
         }
     }
     
     // Update the overlay.
     %this.updateSpriteCountOverlay();  
-        
-    // Schedule to create more sprites.
-    %this.createSpriteScheduleId = %this.schedule( SpriteStressToy.SpriteCreatePeriod, "createStaticCompositeSprites" );        
 }
 
 //-----------------------------------------------------------------------------
 
 function SpriteStressToy::createAnimatedCompositeSprites( %this )
 {
-    // Reset the event schedule.
-    %this.createSpriteScheduleId = "";
-    
     // Finish if max sprites reached.
     if ( SpriteStressToy.SpriteCount >= SpriteStressToy.MaxSprite )
+    {
+        // Stop the timer.
+        SpriteStressToy.stopTimer();
+        
         return;
+    }
 
     // Do we have a composite sprite yet?
     if ( !isObject(SpriteStressToy.CompositeSpriteObject) )
@@ -534,6 +523,9 @@ function SpriteStressToy::createAnimatedCompositeSprites( %this )
         {
             // Update the overlay.
             %this.updateSpriteCountOverlay();              
+
+            // Stop the timer.
+            SpriteStressToy.stopTimer();         
             
             return;
         }
@@ -541,9 +533,6 @@ function SpriteStressToy::createAnimatedCompositeSprites( %this )
     
     // Update the overlay.
     %this.updateSpriteCountOverlay();  
-        
-    // Schedule to create more sprites.
-    %this.createSpriteScheduleId = %this.schedule( SpriteStressToy.SpriteCreatePeriod, "createAnimatedCompositeSprites" );        
 }
 
 //-----------------------------------------------------------------------------

+ 4 - 32
modules/TruckToy/1/main.cs

@@ -25,8 +25,6 @@ function TruckToy::create( %this )
     // Activate the package.
     activatePackage( TruckToyPackage );
 
-    TruckToy.createProjectileScheduleId = "";
-    
     TruckToy.ObstacleFriction = 1.5;
     TruckToy.CameraWidth = 20;
     TruckToy.CameraHeight = 15;
@@ -68,28 +66,12 @@ function TruckToy::create( %this )
 
 function TruckToy::destroy( %this )
 {
-    // Cancel any pending events.
-    TruckToy.cancelPendingEvents();
-        
     // Deactivate the package.
     deactivatePackage( TruckToyPackage );
 }
 
 //-----------------------------------------------------------------------------
 
-function TruckToy::cancelPendingEvents(%this)
-{
-    // Finish if there are not pending events.
-    if ( !isEventPending(%this.createProjectileScheduleId) )
-        return;
-        
-    // Cancel it.
-    cancel(%this.createProjectileScheduleId);
-    %this.createProjectileScheduleId = "";
-}
-
-//-----------------------------------------------------------------------------
-
 function TruckToy::reset( %this )
 {   
     // Cancel any pending events.
@@ -194,8 +176,8 @@ function TruckToy::reset( %this )
     %truckStartY = 3;   
     %this.createTruck( %truckStartX, %truckStartY );    
     
-    // Schedule to create a projectile.
-    TruckToy.createProjectileScheduleId = %this.schedule( TruckToy.ProjectileRate, "createProjectile" );    
+    // Start the timer.
+    TruckToy.startTimer( "createProjectile", TruckToy.ProjectileRate );
 }
 
 // -----------------------------------------------------------------------------
@@ -665,15 +647,11 @@ function TruckToy::createBonfire(%this, %x, %y, %scale, %layer)
 
 function TruckToy::createProjectile(%this)
 {
-    // Reset the event schedule.
-    %this.createProjectileScheduleId = "";
-    
     // Fetch the truck position.
     %truckPositionX = TruckToy.TruckBody.getPositionX();
     
     %projectile = new Sprite() { class = "TruckProjectile"; };
     %projectile.Animation = "ToyAssets:Projectile_FireballAnim";
-    //%projectile.Image = "ToyAssets:Cannonball_projectile_1Sprite";
     %projectile.setPosition( getRandom( %truckPositionX - (TruckToy.CameraWidth * 0.2), %truckPositionX + (TruckToy.CameraWidth * 0.5) ), 12 );
     %projectile.setSceneLayer( TruckToy.BackgroundDomain-2 );
     %projectile.setSceneGroup( TruckToy.ProjectileDomain );
@@ -684,9 +662,6 @@ function TruckToy::createProjectile(%this)
     %projectile.setCollisionGroups( TruckToy.ObstacleDomain );
     %projectile.CollisionCallback = true;
     SandboxScene.add( %projectile ); 
-    
-    // Schedule to create a projectile.
-    %this.createProjectileScheduleId = %this.schedule( %this.ProjectileRate, "createProjectile" );         
 }
 
 // -----------------------------------------------------------------------------
@@ -938,11 +913,8 @@ function TruckToy::setProjectileRate( %this, %value )
 {
     %this.ProjectileRate = %value;
     
-    // Cancel any pending events.
-    %this.cancelPendingEvents();
-    
-    // Schedule to create a projectile.
-    %this.createProjectileScheduleId = %this.schedule( 1000, "createProjectile" );    
+    // Start the timer.
+    TruckToy.startTimer( "createProjectile", TruckToy.ProjectileRate );
 }
 
 //-----------------------------------------------------------------------------

+ 9 - 29
modules/TumblerToy/1/main.cs

@@ -30,7 +30,6 @@ function TumblerToy::create( %this )
     Sandbox.useManipulation( pull );
     
     // Initialize the toys settings.
-    TumblerToy.createBallScheduleId = "";
     TumblerToy.maxBalls = 100;
     TumblerToy.currentBalls = 0;
     TumblerToy.MotorSpeed = 10;
@@ -48,30 +47,12 @@ function TumblerToy::create( %this )
 
 function TumblerToy::destroy( %this )
 {
-    // Cancel any pending events.
-    TumblerToy.cancelPendingEvents();
-}
-
-//-----------------------------------------------------------------------------
-
-function TumblerToy::cancelPendingEvents(%this)
-{
-    // Finish if there are not pending events.
-    if ( !isEventPending(%this.createBallScheduleId) )
-        return;
-        
-    // Cancel it.
-    cancel(%this.createBallScheduleId);
-    %this.createBallScheduleId = "";
 }
 
 //-----------------------------------------------------------------------------
 
 function TumblerToy::reset(%this)
-{
-    // Cancel any pending events.
-    TumblerToy::cancelPendingEvents();
-    
+{   
     // Clear the scene.
     SandboxScene.clear();
     
@@ -87,8 +68,8 @@ function TumblerToy::reset(%this)
     // Reset the ball count.    
     %this.currentBalls = 0;
             
-    // Schedule to create a ball.
-    %this.createBallScheduleId = %this.schedule( 100, "createBall" );
+    // Start the timer.
+    TumblerToy.startTimer( "createBall", 100 );
 }
 
 //-----------------------------------------------------------------------------
@@ -132,9 +113,6 @@ function TumblerToy::createTumbler(%this)
 
 function TumblerToy::createBall(%this)
 {
-    // Reset the event schedule.
-    %this.createBallScheduleId = "";
-
     // Fetch the stock color count.
     %stockColorCount = getStockColorCount();
     
@@ -153,13 +131,15 @@ function TumblerToy::createBall(%this)
         // Increase ball count.
         %this.currentBalls++;
         
-        // Finish if exceeded the required number of balls.
+        // Stop the timer if exceeded the required number of balls.
         if ( %this.currentBalls >= %this.maxBalls)
+        {
+            // Cancel the timer.
+            TumblerToy.stopTimer();
+
             return;
+        }
     }
-
-    // Schedule to create a ball.
-    %this.createBallScheduleId = %this.schedule( 100, "createBall" );
 }