-------------------------------------------------------------- -- Includes -------------------------------------------------------------- require('o_mis') require('c_AvantGardens') -------------------------------------------------------------- -- constants -------------------------------------------------------------- -- which of the two NPC's is associated with the transformations named 1_ and which with the transformations named 2_ -- this is determined by a flag set in scripts \ c_AvantGardens.lua if ( CONSTANTS["KIPPER_DUEL_SENTINEL_GOES_FIRST"] == true ) then CONSTANTS["ORDER_SENTINEL"] = 1 CONSTANTS["ORDER_PARADOX"] = 2 else CONSTANTS["ORDER_PARADOX"] = 1 CONSTANTS["ORDER_SENTINEL"] = 2 end -- after sending the load request for a model, how long to wait before the assemly NPC comments on the transformation CONSTANTS["COMMENTARY_DELAY"] = 0.25 -- the order in which the different models get transformed -- the number in front represents which of the 2 competing objects changes TRANSFORMATIONS = {} TRANSFORMATIONS["1_NOTHING_TO_MOUSE"] = 1 TRANSFORMATIONS["2_NOTHING_TO_CAT"] = 2 TRANSFORMATIONS["1_MOUSE_TO_DOG"] = 3 TRANSFORMATIONS["2_CAT_TO_DRAGON"] = 4 TRANSFORMATIONS["1_DOG_TO_FIRE_ENGINE"] = 5 TRANSFORMATIONS["2_DRAGON_TO_SUBMARINE"] = 6 TRANSFORMATIONS["1_FIRE_ENGINE_TO_KIPPER"] = 7 TRANSFORMATIONS["2_SUBMARINE_TO_ELEPHANT"] = 8 TRANSFORMATIONS["1_KIPPER_TO_MOUSE"] = 9 TRANSFORMATIONS["2_ELEPHANT_TO_CAT"] = 10 TRANSFORMATIONS["NOT_STARTED"] = 0 TRANSFORMATIONS["START_FROM_NOTHING"] = TRANSFORMATIONS["1_NOTHING_TO_MOUSE"] TRANSFORMATIONS["FIRST_LOOPING"] = TRANSFORMATIONS["1_MOUSE_TO_DOG"] TRANSFORMATIONS["LAST_LOOPING"] = TRANSFORMATIONS["2_ELEPHANT_TO_CAT"] -- the LOT for the model produced by each transformation MODEL_LOTS = {} MODEL_LOTS[TRANSFORMATIONS["1_NOTHING_TO_MOUSE"] ] = CONSTANTS["KIPPER_DUEL_MOUSE_LOT"] MODEL_LOTS[TRANSFORMATIONS["2_NOTHING_TO_CAT"]] = CONSTANTS["KIPPER_DUEL_CAT_LOT"] MODEL_LOTS[TRANSFORMATIONS["1_MOUSE_TO_DOG"]] = CONSTANTS["KIPPER_DUEL_DOG_LOT"] MODEL_LOTS[TRANSFORMATIONS["2_CAT_TO_DRAGON"]] = CONSTANTS["KIPPER_DUEL_DRAGON_LOT"] MODEL_LOTS[TRANSFORMATIONS["1_DOG_TO_FIRE_ENGINE"]] = CONSTANTS["KIPPER_DUEL_FIRE_ENGINE_LOT"] MODEL_LOTS[TRANSFORMATIONS["2_DRAGON_TO_SUBMARINE"]] = CONSTANTS["KIPPER_DUEL_SUBMARINE_LOT"] MODEL_LOTS[TRANSFORMATIONS["1_FIRE_ENGINE_TO_KIPPER"]] = CONSTANTS["KIPPER_DUEL_KIPPER_LOT"] MODEL_LOTS[TRANSFORMATIONS["2_SUBMARINE_TO_ELEPHANT"]] = CONSTANTS["KIPPER_DUEL_ELEPHANT_LOT"] MODEL_LOTS[TRANSFORMATIONS["1_KIPPER_TO_MOUSE"]] = CONSTANTS["KIPPER_DUEL_MOUSE_LOT"] MODEL_LOTS[TRANSFORMATIONS["2_ELEPHANT_TO_CAT"]] = CONSTANTS["KIPPER_DUEL_CAT_LOT"] -- which NPC ( 1 or 2 ) is the owner of each transformation OWNER_NUMS = {} OWNER_NUMS[TRANSFORMATIONS["1_NOTHING_TO_MOUSE"]] = 1 OWNER_NUMS[TRANSFORMATIONS["2_NOTHING_TO_CAT"]] = 2 OWNER_NUMS[TRANSFORMATIONS["1_MOUSE_TO_DOG"]] = 1 OWNER_NUMS[TRANSFORMATIONS["2_CAT_TO_DRAGON"]] = 2 OWNER_NUMS[TRANSFORMATIONS["1_DOG_TO_FIRE_ENGINE"]] = 1 OWNER_NUMS[TRANSFORMATIONS["2_DRAGON_TO_SUBMARINE"]] = 2 OWNER_NUMS[TRANSFORMATIONS["1_FIRE_ENGINE_TO_KIPPER"]] = 1 OWNER_NUMS[TRANSFORMATIONS["2_SUBMARINE_TO_ELEPHANT"]] = 2 OWNER_NUMS[TRANSFORMATIONS["1_KIPPER_TO_MOUSE"]] = 1 OWNER_NUMS[TRANSFORMATIONS["2_ELEPHANT_TO_CAT"]] = 2 -- how close to the corresponding NPC to place the model -- (compared to the whole distance between the 2 NPC's ) CONSTANTS["DISTANCE_FACTOR"] = 7.0 / 24.0 -- half way between 1/3 of the way and 1/4 of the way -- the LOTs for the dummy models used to start us off -- these transform into the mouse and cat CONSTANTS["KIPPER_DUEL_DUMMY_MOUSE_LOT"] = 4966 CONSTANTS["KIPPER_DUEL_DUMMY_CAT_LOT"] = 4973 -------------------------------------------------------------- -- Startup -------------------------------------------------------------- function onStartupKipperDuel(self) self:SetVar("currentTransformation", TRANSFORMATIONS["NOT_STARTED"] ) end -------------------------------------------------------------- -- Called when zone object gets an onFireEvent for "ActorReadyKipperDuel" -- because the render component just became ready for one of the NPC's -------------------------------------------------------------- function ActorReadyKipperDuel( self, actor ) local LOT = actor:GetLOT().objtemplate if ( LOT == CONSTANTS["KIPPER_DUEL_SENTINEL_NPC_LOT"] ) then storeObjectByName( self, "KipperDuelSentinelNPC", actor ) elseif ( LOT == CONSTANTS["KIPPER_DUEL_PARADOX_NPC_LOT"] ) then storeObjectByName( self, "KipperDuelParadoxNPC", actor ) elseif( LOT == CONSTANTS["KIPPER_SPECTATOR_ASSEMBLY_NPC_LOT"] ) then storeObjectByName( self, "KipperSpectatorAssemblyNPC", actor ) end -- if all 3 actors are now ready, set up a timer to set off the whole duel if ( VerifyActors( self ) ) then SpawnInvisibleStartingModels( self ) GAMEOBJ:GetTimer():AddTimerWithCancel( CONSTANTS["KIPPER_DUEL_TIME_BETWEEN_MODELS"], "nextTransformation", self ) end end -------------------------------------------------------------- -- Timers -------------------------------------------------------------- function onTimerDoneKipperDuel( self, msg ) if ( msg.name == "nextTransformation" ) then DoNextTransformation( self ) elseif ( msg.name == "commentary" ) then ShowSpectatorCommentary( self ) end end -------------------------------------------------------------- -- set the transformation number to the next one in the progression -------------------------------------------------------------- function IncrementTransformationNum( self ) -- check which transformation was done last local curTransformation = self:GetVar( "currentTransformation" ) -- if we haven't started yet, start with the 2 transformations from nothing if ( curTransformation == TRANSFORMATIONS["NOT_STARTED"] ) then self:SetVar("currentTransformation", TRANSFORMATIONS["START_FROM_NOTHING"] ) -- if both objects have cycled back to their first incarnations, then restart the chain of transformations -- (skip the transformations from nothing) elseif ( curTransformation == TRANSFORMATIONS["LAST_LOOPING"] ) then self:SetVar("currentTransformation", TRANSFORMATIONS["FIRST_LOOPING"] ) -- otherwise, just go to the next transformation in order else self:SetVar("currentTransformation", curTransformation + 1 ) end end -------------------------------------------------------------- -- returns whether the stored objects are valid actors for the kipper duel -------------------------------------------------------------- function VerifyActors(self) if ( getObjectByName( self, "KipperDuelSentinelNPC" ) and getObjectByName( self, "KipperDuelParadoxNPC" ) and getObjectByName( self, "KipperSpectatorAssemblyNPC" ) ) then if ( getObjectByName( self, "KipperDuelSentinelNPC" ):Exists() and getObjectByName( self, "KipperDuelParadoxNPC" ):Exists() and getObjectByName( self, "KipperSpectatorAssemblyNPC" ):Exists() ) then return true end end return false end -------------------------------------------------------------- -- transform the existing model into a new one -------------------------------------------------------------- function DoNextTransformation( self ) if ( VerifyActors(self) == false ) then return end -- figure out which transformation is due IncrementTransformationNum( self ) --print( "incremented transformation num to " .. self:GetVar("currentTransformation" ) ) -- ask the LWOChangelingComponent to transform it into the one we need now TransformModel( self ) end -------------------------------------------------------------- -- returns whether the current transformation number affects model 1 or 2 -------------------------------------------------------------- function GetChanglingModelNum( self ) -- return whether the current transformation is on the 1st or 2nd model local curTransformation = self:GetVar( "currentTransformation" ) return OWNER_NUMS[curTransformation] end -------------------------------------------------------------- -- returns whether or not the current model belongs to the Sentinel NPC -- if not, it belongs to the Paradox NPC -------------------------------------------------------------- function ModelBelongsToSentinelNPC( self ) local modelNum = GetChanglingModelNum( self ) if ( modelNum == CONSTANTS["ORDER_SENTINEL"] ) then return true else return false end end -------------------------------------------------------------- -- have the Assembly NPC comment on the last transformation -------------------------------------------------------------- function ShowSpectatorCommentary( self ) if ( VerifyActors(self) == false ) then return end MakeSpectatorWatch( self ) local assemblyObj = getObjectByName( self, "KipperSpectatorAssemblyNPC" ) local modelLOT = GetCurrentModelLOT( self ) -- send a notification to the assembly NPC with the LOT for the model to comment on -- this goes through to scripts \ client \ ai \ AG \ L_AG_KIPPER_SPECTATOR_NPC_CLIENT.lua assemblyObj:NotifyObject{ name = "CommentOnTransformation", param1 = modelLOT } end -------------------------------------------------------------- -- get the LOT for the model produced by the current transformation -------------------------------------------------------------- function GetCurrentModelLOT( self ) local curTransformation = self:GetVar( "currentTransformation" ) return MODEL_LOTS[curTransformation] end -------------------------------------------------------------- -- ask the LWOChangelingBuildComponent to transform the current model into the one we want now -------------------------------------------------------------- function TransformModel( self ) local SentinelObj = getObjectByName( self, "KipperDuelSentinelNPC" ) local ParadoxObj = getObjectByName( self, "KipperDuelParadoxNPC" ) local targetLOT = GetCurrentModelLOT( self ) if ( GetChanglingModelNum( self ) == 1 ) then if ( getObjectByName( self, "changlingModel_1" ) ) then if ( ModelBelongsToSentinelNPC( self ) == true ) then --print( "sentinel transforms model 1 into " .. targetLOT ) getObjectByName( self, "changlingModel_1" ):TransformChangelingBuild{ listenerID = self, miniFigID = SentinelObj, newModelLOT = targetLOT, fAdditionalBricksDelay = CONSTANTS["KIPPER_DUEL_ADDITIONAL_BRICKS_DELAY"], fMaxPositionOffset = CONSTANTS["KIPPER_DUEL_MAX_BRICK_POS_OFFSET"] } else --print( "paradox transforms model 1 into " .. targetLOT ) getObjectByName( self, "changlingModel_1" ):TransformChangelingBuild{ listenerID = self, miniFigID = ParadoxObj, newModelLOT = targetLOT, fAdditionalBricksDelay = CONSTANTS["KIPPER_DUEL_ADDITIONAL_BRICKS_DELAY"], fMaxPositionOffset = CONSTANTS["KIPPER_DUEL_MAX_BRICK_POS_OFFSET"] } end end else -- model 2 if ( getObjectByName( self, "changlingModel_2" ) ) then if ( ModelBelongsToSentinelNPC( self ) == true ) then --print( "sentinel transforms model 2 into " .. targetLOT ) getObjectByName( self, "changlingModel_2" ):TransformChangelingBuild{ listenerID = self, miniFigID = SentinelObj, newModelLOT = targetLOT, fAdditionalBricksDelay = CONSTANTS["KIPPER_DUEL_ADDITIONAL_BRICKS_DELAY"], fMaxPositionOffset = CONSTANTS["KIPPER_DUEL_MAX_BRICK_POS_OFFSET"] } else --print( "paradox transforms model 2 into " .. targetLOT ) getObjectByName( self, "changlingModel_2" ):TransformChangelingBuild{ listenerID = self, miniFigID = ParadoxObj, newModelLOT = targetLOT, fAdditionalBricksDelay = CONSTANTS["KIPPER_DUEL_ADDITIONAL_BRICKS_DELAY"], fMaxPositionOffset = CONSTANTS["KIPPER_DUEL_MAX_BRICK_POS_OFFSET"] } end end end end -------------------------------------------------------------- -- Called when zone object gets an onFireEvent for "ModelReadyKipperDuel" -- because the render component became ready for the newest changeling model -------------------------------------------------------------- function ModelReadyKipperDuel( self, model ) local LOT = model:GetLOT().objtemplate if ( IsValidModel( LOT ) == false ) then return end local modelNum = GetChanglingModelNum( self ) if ( modelNum == 1 ) then storeObjectByName( self, "changlingModel_1", model ) else storeObjectByName( self, "changlingModel_2", model ) end StartTimersUponNewModelLoaded( self ) DuelingNPCsReactToNewModel( self ) end -------------------------------------------------------------- -- return if template is a valid changeling model -------------------------------------------------------------- function IsValidModel( LOT ) for index = 1, #MODEL_LOTS do if ( LOT == MODEL_LOTS[index] ) then return true end end return false end -------------------------------------------------------------- -- return if template is a valid changeling model -------------------------------------------------------------- function StartTimersUponNewModelLoaded( self ) -- start a timer for having the Assembly NPC spectator comment on the transformation GAMEOBJ:GetTimer():AddTimerWithCancel( CONSTANTS["COMMENTARY_DELAY"], "commentary", self ) -- start a new timer to initiate the next transformation GAMEOBJ:GetTimer():AddTimerWithCancel( CONSTANTS["KIPPER_DUEL_TIME_BETWEEN_MODELS"], "nextTransformation", self ) end -------------------------------------------------------------- -- a transformation just finished and the old object sent a msg with the new model's object ID -------------------------------------------------------------- function onReturnChangelingBuildID( self, msg ) -- test only -- not actually using this because we want to wait until we know the render component is ready for the new model -- that is caught in L_AG_KIPPER_MODEL_CLIENT.lua and passed through to the zone script -- (the return msg is sent is sent at the same time as the spawn msg for the new model) --print( "kipper duel received new model ID" ) end -------------------------------------------------------------- -- spawn hidden models that can be changed into the mouse and cat to start with -------------------------------------------------------------- function SpawnInvisibleStartingModels( self ) if ( VerifyActors(self) == false ) then return end -- how close to the corresponding NPC to place the model -- (compared to the whole distance between the 2 NPC's ) local distanceFactor = CONSTANTS["DISTANCE_FACTOR"] -- get the NPC's positions local posSentinel = getObjectByName(self, "KipperDuelSentinelNPC"):GetPosition().pos local posParadox = getObjectByName(self, "KipperDuelParadoxNPC"):GetPosition().pos -- calculate the model positions local posForSentinelModel = { x = ( ( posParadox.x - posSentinel.x ) * distanceFactor ) + posSentinel.x, y = ( ( posParadox.y - posSentinel.y ) * distanceFactor ) + posSentinel.y, z = ( ( posParadox.z - posSentinel.z ) * distanceFactor ) + posSentinel.z } local posForParadoxModel = { x = ( ( posSentinel.x - posParadox.x ) * distanceFactor ) + posParadox.x, y = ( ( posSentinel.y - posParadox.y ) * distanceFactor ) + posParadox.y, z = ( ( posSentinel.z - posParadox.z ) * distanceFactor ) + posParadox.z } -- get the NPC's rotations -- we'll use the same rotations for the models local rotMsg1 = getObjectByName(self, "KipperDuelSentinelNPC"):GetRotation() local rotForSentinelModel = { w = rotMsg1.w, x = rotMsg1.x, y = rotMsg1.y, z = rotMsg1.z } local rotMsg2 = getObjectByName(self, "KipperDuelParadoxNPC"):GetRotation() local rotForParadoxModel = { w = rotMsg2.w, x = rotMsg2.x, y = rotMsg2.y, z = rotMsg2.z } -- spawn the new models RESMGR:LoadObject { objectTemplate = CONSTANTS["KIPPER_DUEL_DUMMY_MOUSE_LOT"], bIsSmashable = true, x = posForSentinelModel.x, y = posForSentinelModel.y, z = posForSentinelModel.z, rw = rotForSentinelModel.w, rx = rotForSentinelModel.x, ry = rotForSentinelModel.y, rz = rotForSentinelModel.z, owner = self } RESMGR:LoadObject { objectTemplate = CONSTANTS["KIPPER_DUEL_DUMMY_CAT_LOT"], bIsSmashable = true, x = posForParadoxModel.x, y = posForParadoxModel.y, z = posForParadoxModel.z, rw = rotForParadoxModel.w, rx = rotForParadoxModel.x, ry = rotForParadoxModel.y, rz = rotForParadoxModel.z, owner = self } end -------------------------------------------------------------- -- loaded up one of the invisible starting models -------------------------------------------------------------- function onChildLoadedKipperDuel(self, msg) if ( msg.templateID == 4966 ) then storeObjectByName( self, "changlingModel_1", msg.childID ) elseif ( msg.templateID == 4973 ) then storeObjectByName( self, "changlingModel_2", msg.childID ) end end -------------------------------------------------------------- -- after a new model is loaded up, the dueling NPC that created it talks it up -------------------------------------------------------------- function ModelsOwnerTalksSmack( self ) if ( VerifyActors(self) == false ) then return end local duelObj = getObjectByName( self, "KipperDuelParadoxNPC" ) if ( ModelBelongsToSentinelNPC( self ) == true ) then duelObj = getObjectByName( self, "KipperDuelSentinelNPC" ) end local modelLOT = GetCurrentModelLOT( self ) -- send a notification to the NPC with the LOT for the model to comment on -- this goes through to scripts \ client \ ai \ AG \ L_AG_KIPPER_DUEL_NPC_CLIENT.lua duelObj:NotifyObject{ name = "TalkSmack", param1 = modelLOT } end -------------------------------------------------------------- -- after a new model is loaded up, the dueling NPCs react to it -------------------------------------------------------------- function DuelingNPCsReactToNewModel( self ) if ( VerifyActors(self) == false ) then return end local sentinelObj = getObjectByName( self, "KipperDuelSentinelNPC" ) local paradoxObj = getObjectByName( self, "KipperDuelParadoxNPC" ) local ownerObj = sentinelObj local opponentObj = paradoxObj if ( ModelBelongsToSentinelNPC( self ) == false ) then ownerObj = paradoxObj opponentObj = sentinelObj end -- the owner of the new model plays a gloating anim ownerObj:PlayAnimation{ animationID = CONSTANTS["KIPPER_ANIM_GLOAT"], fPriority = 1.5 } -- the other dueling NPC plays a pouting anim opponentObj:PlayAnimation{ animationID = CONSTANTS["KIPPER_ANIM_POUT"], fPriority = 1.5 } -- the new model's owner comments on it ModelsOwnerTalksSmack( self ) end -------------------------------------------------------------- -- make the spectator animate in the direction of the transformation -------------------------------------------------------------- function MakeSpectatorWatch( self ) if ( VerifyActors(self) == false ) then return end local spectatorObj = getObjectByName( self, "KipperSpectatorAssemblyNPC" ) local bSentinelIsOwner = ModelBelongsToSentinelNPC( self ) local bSentinelIsToTheLeft = CONSTANTS["SENTINEL_IS_LEFT_OF_SPECTATOR"] local bLookLeft = true if ( bSentinelIsOwner == true and bSentinelIsToTheLeft == false ) then bLookLeft = false elseif ( bSentinelIsOwner == false and bSentinelIsToTheLeft == true ) then bLookLeft = false end if ( bLookLeft ) then spectatorObj:PlayAnimation{ animationID = CONSTANTS["KIPPER_ANIM_LEFT"], fPriority = 1.5 } else spectatorObj:PlayAnimation{ animationID = CONSTANTS["KIPPER_ANIM_RIGHT"], fPriority = 1.5 } end end