From b691c43c44ff180d10e7d4a9afc83b98551ff586 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Sun, 1 Mar 2026 12:16:08 +0800 Subject: Initial commit --- .../Common/Tutorial/AreaConstraint.cpp | 52 + Minecraft.Client/Common/Tutorial/AreaConstraint.h | 24 + Minecraft.Client/Common/Tutorial/AreaHint.cpp | 49 + Minecraft.Client/Common/Tutorial/AreaHint.h | 25 + Minecraft.Client/Common/Tutorial/AreaTask.cpp | 69 + Minecraft.Client/Common/Tutorial/AreaTask.h | 24 + .../Common/Tutorial/ChangeStateConstraint.cpp | 136 ++ .../Common/Tutorial/ChangeStateConstraint.h | 37 + Minecraft.Client/Common/Tutorial/ChoiceTask.cpp | 135 ++ Minecraft.Client/Common/Tutorial/ChoiceTask.h | 27 + .../Common/Tutorial/CompleteUsingItemTask.cpp | 37 + .../Common/Tutorial/CompleteUsingItemTask.h | 20 + .../Common/Tutorial/ControllerTask.cpp | 123 ++ Minecraft.Client/Common/Tutorial/ControllerTask.h | 24 + Minecraft.Client/Common/Tutorial/CraftTask.cpp | 66 + Minecraft.Client/Common/Tutorial/CraftTask.h | 25 + .../Common/Tutorial/DiggerItemHint.cpp | 76 + Minecraft.Client/Common/Tutorial/DiggerItemHint.h | 18 + .../Common/Tutorial/EffectChangedTask.cpp | 31 + .../Common/Tutorial/EffectChangedTask.h | 19 + Minecraft.Client/Common/Tutorial/FullTutorial.cpp | 653 ++++++ Minecraft.Client/Common/Tutorial/FullTutorial.h | 21 + .../Common/Tutorial/FullTutorialActiveTask.cpp | 26 + .../Common/Tutorial/FullTutorialActiveTask.h | 18 + .../Common/Tutorial/FullTutorialMode.cpp | 16 + .../Common/Tutorial/FullTutorialMode.h | 12 + Minecraft.Client/Common/Tutorial/InfoTask.cpp | 137 ++ Minecraft.Client/Common/Tutorial/InfoTask.h | 25 + .../Common/Tutorial/InputConstraint.cpp | 18 + Minecraft.Client/Common/Tutorial/InputConstraint.h | 15 + .../Common/Tutorial/LookAtEntityHint.cpp | 25 + .../Common/Tutorial/LookAtEntityHint.h | 20 + .../Common/Tutorial/LookAtTileHint.cpp | 68 + Minecraft.Client/Common/Tutorial/LookAtTileHint.h | 22 + Minecraft.Client/Common/Tutorial/PickupTask.cpp | 17 + Minecraft.Client/Common/Tutorial/PickupTask.h | 26 + .../Common/Tutorial/ProcedureCompoundTask.cpp | 263 +++ .../Common/Tutorial/ProcedureCompoundTask.h | 36 + .../Common/Tutorial/ProgressFlagTask.cpp | 17 + .../Common/Tutorial/ProgressFlagTask.h | 25 + Minecraft.Client/Common/Tutorial/StatTask.cpp | 25 + Minecraft.Client/Common/Tutorial/StatTask.h | 18 + Minecraft.Client/Common/Tutorial/StateChangeTask.h | 27 + Minecraft.Client/Common/Tutorial/TakeItemHint.cpp | 45 + Minecraft.Client/Common/Tutorial/TakeItemHint.h | 19 + Minecraft.Client/Common/Tutorial/Tutorial | Bin 0 -> 8973500 bytes Minecraft.Client/Common/Tutorial/Tutorial.cpp | 2160 ++++++++++++++++++++ Minecraft.Client/Common/Tutorial/Tutorial.h | 199 ++ .../Common/Tutorial/TutorialConstraint.h | 41 + .../Common/Tutorial/TutorialConstraints.h | 4 + Minecraft.Client/Common/Tutorial/TutorialEnum.h | 329 +++ Minecraft.Client/Common/Tutorial/TutorialHint.cpp | 128 ++ Minecraft.Client/Common/Tutorial/TutorialHint.h | 53 + Minecraft.Client/Common/Tutorial/TutorialHints.h | 7 + .../Common/Tutorial/TutorialMessage.cpp | 23 + Minecraft.Client/Common/Tutorial/TutorialMessage.h | 20 + Minecraft.Client/Common/Tutorial/TutorialMode.cpp | 124 ++ Minecraft.Client/Common/Tutorial/TutorialMode.h | 28 + Minecraft.Client/Common/Tutorial/TutorialTask.cpp | 78 + Minecraft.Client/Common/Tutorial/TutorialTask.h | 63 + Minecraft.Client/Common/Tutorial/TutorialTasks.h | 16 + Minecraft.Client/Common/Tutorial/UseItemTask.cpp | 25 + Minecraft.Client/Common/Tutorial/UseItemTask.h | 20 + Minecraft.Client/Common/Tutorial/UseTileTask.cpp | 40 + Minecraft.Client/Common/Tutorial/UseTileTask.h | 24 + .../Common/Tutorial/XuiCraftingTask.cpp | 42 + Minecraft.Client/Common/Tutorial/XuiCraftingTask.h | 36 + 67 files changed, 6071 insertions(+) create mode 100644 Minecraft.Client/Common/Tutorial/AreaConstraint.cpp create mode 100644 Minecraft.Client/Common/Tutorial/AreaConstraint.h create mode 100644 Minecraft.Client/Common/Tutorial/AreaHint.cpp create mode 100644 Minecraft.Client/Common/Tutorial/AreaHint.h create mode 100644 Minecraft.Client/Common/Tutorial/AreaTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/AreaTask.h create mode 100644 Minecraft.Client/Common/Tutorial/ChangeStateConstraint.cpp create mode 100644 Minecraft.Client/Common/Tutorial/ChangeStateConstraint.h create mode 100644 Minecraft.Client/Common/Tutorial/ChoiceTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/ChoiceTask.h create mode 100644 Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.h create mode 100644 Minecraft.Client/Common/Tutorial/ControllerTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/ControllerTask.h create mode 100644 Minecraft.Client/Common/Tutorial/CraftTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/CraftTask.h create mode 100644 Minecraft.Client/Common/Tutorial/DiggerItemHint.cpp create mode 100644 Minecraft.Client/Common/Tutorial/DiggerItemHint.h create mode 100644 Minecraft.Client/Common/Tutorial/EffectChangedTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/EffectChangedTask.h create mode 100644 Minecraft.Client/Common/Tutorial/FullTutorial.cpp create mode 100644 Minecraft.Client/Common/Tutorial/FullTutorial.h create mode 100644 Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.h create mode 100644 Minecraft.Client/Common/Tutorial/FullTutorialMode.cpp create mode 100644 Minecraft.Client/Common/Tutorial/FullTutorialMode.h create mode 100644 Minecraft.Client/Common/Tutorial/InfoTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/InfoTask.h create mode 100644 Minecraft.Client/Common/Tutorial/InputConstraint.cpp create mode 100644 Minecraft.Client/Common/Tutorial/InputConstraint.h create mode 100644 Minecraft.Client/Common/Tutorial/LookAtEntityHint.cpp create mode 100644 Minecraft.Client/Common/Tutorial/LookAtEntityHint.h create mode 100644 Minecraft.Client/Common/Tutorial/LookAtTileHint.cpp create mode 100644 Minecraft.Client/Common/Tutorial/LookAtTileHint.h create mode 100644 Minecraft.Client/Common/Tutorial/PickupTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/PickupTask.h create mode 100644 Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.h create mode 100644 Minecraft.Client/Common/Tutorial/ProgressFlagTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/ProgressFlagTask.h create mode 100644 Minecraft.Client/Common/Tutorial/StatTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/StatTask.h create mode 100644 Minecraft.Client/Common/Tutorial/StateChangeTask.h create mode 100644 Minecraft.Client/Common/Tutorial/TakeItemHint.cpp create mode 100644 Minecraft.Client/Common/Tutorial/TakeItemHint.h create mode 100644 Minecraft.Client/Common/Tutorial/Tutorial create mode 100644 Minecraft.Client/Common/Tutorial/Tutorial.cpp create mode 100644 Minecraft.Client/Common/Tutorial/Tutorial.h create mode 100644 Minecraft.Client/Common/Tutorial/TutorialConstraint.h create mode 100644 Minecraft.Client/Common/Tutorial/TutorialConstraints.h create mode 100644 Minecraft.Client/Common/Tutorial/TutorialEnum.h create mode 100644 Minecraft.Client/Common/Tutorial/TutorialHint.cpp create mode 100644 Minecraft.Client/Common/Tutorial/TutorialHint.h create mode 100644 Minecraft.Client/Common/Tutorial/TutorialHints.h create mode 100644 Minecraft.Client/Common/Tutorial/TutorialMessage.cpp create mode 100644 Minecraft.Client/Common/Tutorial/TutorialMessage.h create mode 100644 Minecraft.Client/Common/Tutorial/TutorialMode.cpp create mode 100644 Minecraft.Client/Common/Tutorial/TutorialMode.h create mode 100644 Minecraft.Client/Common/Tutorial/TutorialTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/TutorialTask.h create mode 100644 Minecraft.Client/Common/Tutorial/TutorialTasks.h create mode 100644 Minecraft.Client/Common/Tutorial/UseItemTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/UseItemTask.h create mode 100644 Minecraft.Client/Common/Tutorial/UseTileTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/UseTileTask.h create mode 100644 Minecraft.Client/Common/Tutorial/XuiCraftingTask.cpp create mode 100644 Minecraft.Client/Common/Tutorial/XuiCraftingTask.h (limited to 'Minecraft.Client/Common/Tutorial') diff --git a/Minecraft.Client/Common/Tutorial/AreaConstraint.cpp b/Minecraft.Client/Common/Tutorial/AreaConstraint.cpp new file mode 100644 index 00000000..f133d604 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaConstraint.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" + +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "AreaConstraint.h" +#include "..\..\..\Minecraft.World\AABB.h" + +AreaConstraint::AreaConstraint( int descriptionId, double x0, double y0, double z0, double x1, double y1, double z1, bool contains /*= true*/, bool restrictsMovement /*=true*/ ) + : TutorialConstraint( descriptionId ) +{ + messageArea = AABB::newPermanent(x0+2, y0+2, z0+2, x1-2, y1-2, z1-2); + movementArea = AABB::newPermanent(x0, y0, z0, x1, y1, z1); + + this->contains = contains; + m_restrictsMovement = restrictsMovement; +} + +AreaConstraint::~AreaConstraint() +{ + delete messageArea; + delete movementArea; +} + +bool AreaConstraint::isConstraintSatisfied(int iPad) +{ + Minecraft *minecraft = Minecraft::GetInstance(); + return messageArea->contains( minecraft->localplayers[iPad]->getPos(1) ) == contains; +} + +bool AreaConstraint::isConstraintRestrictive(int iPad) +{ + return m_restrictsMovement; +} + + +bool AreaConstraint::canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt) +{ + if(!m_restrictsMovement) return true; + + Vec3 *targetPos = Vec3::newTemp(xt, yt, zt); + Minecraft *minecraft = Minecraft::GetInstance(); + + if(movementArea->contains( targetPos ) == contains) + { + return true; + } + Vec3 *origPos = Vec3::newTemp(xo, yo, zo); + + double currDist = origPos->distanceTo(movementArea); + double targetDist = targetPos->distanceTo(movementArea); + return targetDist < currDist; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaConstraint.h b/Minecraft.Client/Common/Tutorial/AreaConstraint.h new file mode 100644 index 00000000..f98945e1 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaConstraint.h @@ -0,0 +1,24 @@ +#pragma once + +#include "TutorialConstraint.h" + +class AABB; + +class AreaConstraint : public TutorialConstraint +{ +private: + AABB *movementArea; + AABB *messageArea; + bool contains; // If true we must stay in this area, if false must stay out of this area + bool m_restrictsMovement; + +public: + virtual ConstraintType getType() { return e_ConstraintArea; } + + AreaConstraint( int descriptionId, double x0, double y0, double z0, double x1, double y1, double z1, bool contains = true, bool restrictsMovement =true ); + ~AreaConstraint(); + + virtual bool isConstraintSatisfied(int iPad); + virtual bool isConstraintRestrictive(int iPad); + virtual bool canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaHint.cpp b/Minecraft.Client/Common/Tutorial/AreaHint.cpp new file mode 100644 index 00000000..8b711c88 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaHint.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" + +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "AreaHint.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "Tutorial.h" + +AreaHint::AreaHint(eTutorial_Hint id, Tutorial *tutorial, eTutorial_State displayState, eTutorial_State completeState, + int descriptionId, double x0, double y0, double z0, double x1, double y1, double z1, bool allowFade /*= false*/, bool contains /*= true*/ ) + : TutorialHint( id, tutorial, descriptionId, e_Hint_Area, allowFade ) +{ + area = AABB::newPermanent(x0, y0, z0, x1, y1, z1); + + this->contains = contains; + + m_displayState = displayState; + m_completeState = completeState; +} + +AreaHint::~AreaHint() +{ + delete area; +} + +int AreaHint::tick() +{ + Minecraft *minecraft = Minecraft::GetInstance(); + if( (m_displayState == e_Tutorial_State_Any || m_tutorial->getCurrentState() == m_displayState) && + m_hintNeeded && + area->contains( minecraft->player->getPos(1) ) == contains ) + { + if( m_completeState == e_Tutorial_State_None ) + { + m_hintNeeded = false; + } + else if ( m_tutorial->isStateCompleted( m_completeState ) ) + { + m_hintNeeded = false; + return -1; + } + + return m_descriptionId; + } + else + { + return -1; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaHint.h b/Minecraft.Client/Common/Tutorial/AreaHint.h new file mode 100644 index 00000000..12ef8977 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaHint.h @@ -0,0 +1,25 @@ +#pragma once + +#include "TutorialHint.h" + +class AABB; + +class AreaHint : public TutorialHint +{ +private: + AABB *area; + bool contains; // If true we must stay in this area, if false must stay out of this area + + // Only display the hint if the game is in this state + eTutorial_State m_displayState; + + // Only display the hint if this state is not completed + eTutorial_State m_completeState; + +public: + AreaHint(eTutorial_Hint id, Tutorial *tutorial, eTutorial_State displayState, eTutorial_State completeState, + int descriptionId, double x0, double y0, double z0, double x1, double y1, double z1, bool allowFade = true, bool contains = true ); + ~AreaHint(); + + virtual int tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaTask.cpp b/Minecraft.Client/Common/Tutorial/AreaTask.cpp new file mode 100644 index 00000000..de29ab1b --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaTask.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "Tutorial.h" +#include "AreaTask.h" + +AreaTask::AreaTask(eTutorial_State state, Tutorial *tutorial, vector *inConstraints, int descriptionId, EAreaTaskCompletionStates completionState) + : TutorialTask( tutorial, descriptionId, false, inConstraints, false, false, false ) +{ + m_tutorialState = state; + if(m_tutorialState == e_Tutorial_State_Gameplay) + { + enableConstraints(true); + } + m_completionState = completionState; +} + +bool AreaTask::isCompleted() +{ + if(bIsCompleted) return true; + + bool complete = false; + switch(m_completionState) + { + case eAreaTaskCompletion_CompleteOnConstraintsSatisfied: + { + bool allSatisfied = true; + for(AUTO_VAR(it, constraints.begin()); it != constraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + if(!constraint->isConstraintSatisfied(tutorial->getPad())) + { + allSatisfied = false; + break; + } + } + complete = allSatisfied; + } + break; + case eAreaTaskCompletion_CompleteOnActivation: + complete = bHasBeenActivated; + break; + }; + bIsCompleted = complete; + return complete; +} + +void AreaTask::setAsCurrentTask(bool active) +{ + TutorialTask::setAsCurrentTask(active); + + if(m_completionState == eAreaTaskCompletion_CompleteOnConstraintsSatisfied) + { + enableConstraints(active); + } +} + +void AreaTask::onStateChange(eTutorial_State newState) +{ + if(m_completionState == eAreaTaskCompletion_CompleteOnActivation) + { + if(m_tutorialState == newState) + { + enableConstraints(true); + } + else if(m_tutorialState != e_Tutorial_State_Gameplay) + { + //enableConstraints(false); + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaTask.h b/Minecraft.Client/Common/Tutorial/AreaTask.h new file mode 100644 index 00000000..0d20bd7a --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaTask.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +// A task that creates an maintains an area constraint until it is activated +class AreaTask : public TutorialTask +{ +public: + enum EAreaTaskCompletionStates + { + eAreaTaskCompletion_CompleteOnActivation, + eAreaTaskCompletion_CompleteOnConstraintsSatisfied, + }; +private: + EAreaTaskCompletionStates m_completionState; + eTutorial_State m_tutorialState; +public: + AreaTask(eTutorial_State state, Tutorial *tutorial, vector *inConstraints, int descriptionId = -1, EAreaTaskCompletionStates completionState = eAreaTaskCompletion_CompleteOnActivation); + virtual bool isCompleted(); + virtual void setAsCurrentTask(bool active = true); + virtual void onStateChange(eTutorial_State newState); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.cpp b/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.cpp new file mode 100644 index 00000000..f01db84e --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" + +#include "Tutorial.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "ChangeStateConstraint.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\ClientConnection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" + +ChangeStateConstraint::ChangeStateConstraint( Tutorial *tutorial, eTutorial_State targetState, eTutorial_State sourceStates[], DWORD sourceStatesCount, + double x0, double y0, double z0, double x1, double y1, double z1, bool contains /*= true*/, bool changeGameMode /*= false*/, GameType *targetGameMode /*= 0*/ ) + : TutorialConstraint( -1 ) +{ + movementArea = AABB::newPermanent(x0, y0, z0, x1, y1, z1); + + this->contains = contains; + + m_changeGameMode = changeGameMode; + m_targetGameMode = targetGameMode; + m_changedFromGameMode = 0; + + m_tutorial = tutorial; + m_targetState = targetState; + m_sourceStatesCount = sourceStatesCount; + + m_bHasChanged = false; + m_changedFromState = e_Tutorial_State_None; + + m_bComplete = false; + + m_sourceStates = new eTutorial_State [m_sourceStatesCount]; + for(unsigned int i=0;i0) delete [] m_sourceStates; +} + +void ChangeStateConstraint::tick(int iPad) +{ + if(m_bComplete) return; + + if(m_tutorial->isStateCompleted(m_targetState)) + { + Minecraft *minecraft = Minecraft::GetInstance(); + if(m_changeGameMode) + { + unsigned int playerPrivs = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + Player::setPlayerGamePrivilege(playerPrivs,Player::ePlayerGamePrivilege_CreativeMode,m_changedFromGameMode == GameType::CREATIVE); + + unsigned int originalPrivileges = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + if(originalPrivileges != playerPrivs) + { + // Send update settings packet to server + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = minecraft->localplayers[iPad]; + if(player != NULL && player->connection && player->connection->getNetworkPlayer() != NULL) + { + player->connection->send( shared_ptr( new PlayerInfoPacket( player->connection->getNetworkPlayer()->GetSmallId(), -1, playerPrivs) ) ); + } + } + } + m_bComplete = true; + return; + } + + bool inASourceState = false; + Minecraft *minecraft = Minecraft::GetInstance(); + for(DWORD i = 0; i < m_sourceStatesCount; ++i) + { + if(m_sourceStates[i] == m_tutorial->getCurrentState()) + { + inASourceState = true; + break; + } + } + if( !m_bHasChanged && inASourceState && movementArea->contains( minecraft->localplayers[iPad]->getPos(1) ) == contains ) + { + m_bHasChanged = true; + m_changedFromState = m_tutorial->getCurrentState(); + m_tutorial->changeTutorialState(m_targetState); + + if(m_changeGameMode) + { + if(minecraft->localgameModes[iPad] != NULL) + { + m_changedFromGameMode = minecraft->localplayers[iPad]->abilities.instabuild ? GameType::CREATIVE : GameType::SURVIVAL; + + unsigned int playerPrivs = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + Player::setPlayerGamePrivilege(playerPrivs,Player::ePlayerGamePrivilege_CreativeMode,m_targetGameMode == GameType::CREATIVE); + + unsigned int originalPrivileges = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + if(originalPrivileges != playerPrivs) + { + // Send update settings packet to server + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = minecraft->localplayers[iPad]; + if(player != NULL && player->connection && player->connection->getNetworkPlayer() != NULL) + { + player->connection->send( shared_ptr( new PlayerInfoPacket( player->connection->getNetworkPlayer()->GetSmallId(), -1, playerPrivs) ) ); + } + } + } + } + } + else if( m_bHasChanged && movementArea->contains( minecraft->localplayers[iPad]->getPos(1) ) != contains ) + { + m_bHasChanged = false; + m_tutorial->changeTutorialState(m_changedFromState); + + if(m_changeGameMode) + { + unsigned int playerPrivs = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + Player::setPlayerGamePrivilege(playerPrivs,Player::ePlayerGamePrivilege_CreativeMode,m_changedFromGameMode == GameType::CREATIVE); + + unsigned int originalPrivileges = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + if(originalPrivileges != playerPrivs) + { + // Send update settings packet to server + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = minecraft->localplayers[iPad]; + if(player != NULL && player->connection && player->connection->getNetworkPlayer() != NULL) + { + player->connection->send( shared_ptr( new PlayerInfoPacket( player->connection->getNetworkPlayer()->GetSmallId(), -1, playerPrivs) ) ); + } + } + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.h b/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.h new file mode 100644 index 00000000..2156870d --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.h @@ -0,0 +1,37 @@ +#pragma once + +#include "TutorialEnum.h" +#include "TutorialConstraint.h" + +class AABB; +class Tutorial; +class GameType; + +class ChangeStateConstraint : public TutorialConstraint +{ +private: + AABB *movementArea; + bool contains; // If true we must stay in this area, if false must stay out of this area + bool m_changeGameMode; + GameType *m_targetGameMode; + GameType *m_changedFromGameMode; + + eTutorial_State m_targetState; + eTutorial_State *m_sourceStates; + DWORD m_sourceStatesCount; + + bool m_bHasChanged; + eTutorial_State m_changedFromState; + + bool m_bComplete; + + Tutorial *m_tutorial; + +public: + virtual ConstraintType getType() { return e_ConstraintChangeState; } + + ChangeStateConstraint( Tutorial *tutorial, eTutorial_State targetState, eTutorial_State sourceStates[], DWORD sourceStatesCount, double x0, double y0, double z0, double x1, double y1, double z1, bool contains = true, bool changeGameMode = false, GameType *targetGameMode = NULL ); + ~ChangeStateConstraint(); + + virtual void tick(int iPad); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp new file mode 100644 index 00000000..c03166b5 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp @@ -0,0 +1,135 @@ +#include "stdafx.h" +#include +#include +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "Tutorial.h" +#include "TutorialConstraints.h" +#include "ChoiceTask.h" +#include "..\..\..\Minecraft.World\Material.h" + +ChoiceTask::ChoiceTask(Tutorial *tutorial, int descriptionId, int promptId /*= -1*/, bool requiresUserInput /*= false*/, + int iConfirmMapping /*= 0*/, int iCancelMapping /*= 0*/, + eTutorial_CompletionAction cancelAction /*= e_Tutorial_Completion_None*/, ETelemetryChallenges telemetryEvent /*= eTelemetryTutorial_NoEvent*/) + : TutorialTask( tutorial, descriptionId, false, NULL, true, false, false ) +{ + if(requiresUserInput == true) + { + constraints.push_back( new InputConstraint( iConfirmMapping ) ); + constraints.push_back( new InputConstraint( iCancelMapping ) ); + } + m_iConfirmMapping = iConfirmMapping; + m_iCancelMapping = iCancelMapping; + m_bConfirmMappingComplete = false; + m_bCancelMappingComplete = false; + + m_cancelAction = cancelAction; + + m_promptId = promptId; + tutorial->addMessage( m_promptId ); + + m_eTelemetryEvent = telemetryEvent; +} + +bool ChoiceTask::isCompleted() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( m_bConfirmMappingComplete || m_bCancelMappingComplete ) + { + sendTelemetry(); + enableConstraints(false, true); + return true; + } + + if(ui.GetMenuDisplayed(tutorial->getPad())) + { + // If a menu is displayed, then we use the handleUIInput to complete the task + } + else + { + // If the player is under water then allow all keypresses so they can jump out + if( pMinecraft->localplayers[tutorial->getPad()]->isUnderLiquid(Material::water) ) return false; + + if(!m_bConfirmMappingComplete && InputManager.GetValue(pMinecraft->player->GetXboxPad(), m_iConfirmMapping) > 0 ) + { + m_bConfirmMappingComplete = true; + } + if(!m_bCancelMappingComplete && InputManager.GetValue(pMinecraft->player->GetXboxPad(), m_iCancelMapping) > 0 ) + { + m_bCancelMappingComplete = true; + } + } + + if(m_bConfirmMappingComplete || m_bCancelMappingComplete) + { + sendTelemetry(); + enableConstraints(false, true); + } + return m_bConfirmMappingComplete || m_bCancelMappingComplete; +} + +eTutorial_CompletionAction ChoiceTask::getCompletionAction() +{ + if(m_bCancelMappingComplete) + { + return m_cancelAction; + } + else + { + return e_Tutorial_Completion_None; + } +} + +int ChoiceTask::getPromptId() +{ + if( m_bShownForMinimumTime ) + return m_promptId; + else + return -1; +} + +void ChoiceTask::setAsCurrentTask(bool active /*= true*/) +{ + enableConstraints( active ); + TutorialTask::setAsCurrentTask(active); +} + +void ChoiceTask::handleUIInput(int iAction) +{ + if(bHasBeenActivated && m_bShownForMinimumTime) + { + if( iAction == m_iConfirmMapping ) + { + m_bConfirmMappingComplete = true; + } + else if(iAction == m_iCancelMapping ) + { + m_bCancelMappingComplete = true; + } + } +} + +void ChoiceTask::sendTelemetry() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( m_eTelemetryEvent != eTelemetryChallenges_Unknown ) + { + bool firstPlay = true; + // We only store first play for some of the events + switch(m_eTelemetryEvent) + { + case eTelemetryTutorial_TrialStart: + firstPlay = !tutorial->getCompleted( eTutorial_Telemetry_TrialStart ); + tutorial->setCompleted( eTutorial_Telemetry_TrialStart ); + break; + case eTelemetryTutorial_Halfway: + firstPlay = !tutorial->getCompleted( eTutorial_Telemetry_Halfway ); + tutorial->setCompleted( eTutorial_Telemetry_Halfway ); + break; + }; + + TelemetryManager->RecordEnemyKilledOrOvercome(pMinecraft->player->GetXboxPad(), 0, 0, 0, 0, 0, 0, m_eTelemetryEvent); + } +} diff --git a/Minecraft.Client/Common/Tutorial/ChoiceTask.h b/Minecraft.Client/Common/Tutorial/ChoiceTask.h new file mode 100644 index 00000000..79c2ba42 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ChoiceTask.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +// Information messages with a choice +class ChoiceTask : public TutorialTask +{ +private: + int m_iConfirmMapping, m_iCancelMapping; + bool m_bConfirmMappingComplete, m_bCancelMappingComplete; + eTutorial_CompletionAction m_cancelAction; + + ETelemetryChallenges m_eTelemetryEvent; + + bool CompletionMaskIsValid(); +public: + ChoiceTask(Tutorial *tutorial, int descriptionId, int promptId = -1, bool requiresUserInput = false, int iConfirmMapping = 0, int iCancelMapping = 0, eTutorial_CompletionAction cancelAction = e_Tutorial_Completion_None, ETelemetryChallenges telemetryEvent = eTelemetryChallenges_Unknown); + virtual bool isCompleted(); + virtual eTutorial_CompletionAction getCompletionAction(); + virtual int getPromptId(); + virtual void setAsCurrentTask(bool active = true); + virtual void handleUIInput(int iAction); + +private: + void sendTelemetry(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.cpp b/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.cpp new file mode 100644 index 00000000..43b2f7f3 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" +#include "CompleteUsingItemTask.h" + +CompleteUsingItemTask::CompleteUsingItemTask(Tutorial *tutorial, int descriptionId, int itemIds[], unsigned int itemIdsLength, bool enablePreCompletion) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, NULL) +{ + m_iValidItemsA= new int [itemIdsLength]; + for(int i=0;i item) +{ + if(!hasBeenActivated() && !isPreCompletionEnabled()) return; + for(int i=0;iid == m_iValidItemsA[i] ) + { + bIsCompleted = true; + break; + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.h b/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.h new file mode 100644 index 00000000..a905bead --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.h @@ -0,0 +1,20 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class Level; + +class CompleteUsingItemTask : public TutorialTask +{ +private: + int *m_iValidItemsA; + int m_iValidItemsCount; + bool completed; + +public: + CompleteUsingItemTask(Tutorial *tutorial, int descriptionId, int itemIds[], unsigned int itemIdsLength, bool enablePreCompletion = false); + virtual ~CompleteUsingItemTask(); + virtual bool isCompleted(); + virtual void completeUsingItem(shared_ptr item); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ControllerTask.cpp b/Minecraft.Client/Common/Tutorial/ControllerTask.cpp new file mode 100644 index 00000000..c5fe071b --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ControllerTask.cpp @@ -0,0 +1,123 @@ +#include "stdafx.h" +#include +#include +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "Tutorial.h" +#include "TutorialConstraints.h" +#include "ControllerTask.h" + +ControllerTask::ControllerTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, bool showMinimumTime, + int mappings[], unsigned int mappingsLength, int iCompletionMaskA[], int iCompletionMaskACount, int iSouthpawMappings[], unsigned int uiSouthpawMappingsCount) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, NULL, showMinimumTime ) +{ + for(unsigned int i = 0; i < mappingsLength; ++i) + { + constraints.push_back( new InputConstraint( mappings[i] ) ); + completedMappings[mappings[i]] = false; + } + if(uiSouthpawMappingsCount > 0 ) m_bHasSouthpaw = true; + for(unsigned int i = 0; i < uiSouthpawMappingsCount; ++i) + { + southpawCompletedMappings[iSouthpawMappings[i]] = false; + } + + m_iCompletionMaskA= new int [iCompletionMaskACount]; + for(int i=0;iplayer->GetXboxPad(),eGameSetting_ControlSouthPaw)) + { + for(AUTO_VAR(it, southpawCompletedMappings.begin()); it != southpawCompletedMappings.end(); ++it) + { + bool current = (*it).second; + if(!current) + { + // TODO Use a different pad + if( InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 ) + { + (*it).second = true; + m_uiCompletionMask|=1<player->GetXboxPad(), (*it).first) > 0 ) + { + (*it).second = true; + m_uiCompletionMask|=1< completedMappings; + unordered_map southpawCompletedMappings; + bool m_bHasSouthpaw; + unsigned int m_uiCompletionMask; + int *m_iCompletionMaskA; + int m_iCompletionMaskACount; + bool CompletionMaskIsValid(); +public: + ControllerTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, bool showMinimumTime, + int mappings[], unsigned int mappingsLength, int iCompletionMaskA[]=NULL, int iCompletionMaskACount=0, int iSouthpawMappings[]=NULL, unsigned int uiSouthpawMappingsCount=0); + ~ControllerTask(); + virtual bool isCompleted(); + virtual void setAsCurrentTask(bool active = true); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/CraftTask.cpp b/Minecraft.Client/Common/Tutorial/CraftTask.cpp new file mode 100644 index 00000000..6749d030 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/CraftTask.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "CraftTask.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" + +CraftTask::CraftTask( int itemId, int auxValue, int quantity, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion /*= true*/, vector *inConstraints /*= NULL*/, + bool bShowMinimumTime /*=false*/, bool bAllowFade /*=true*/, bool m_bTaskReminders /*=true*/ ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_quantity( quantity ), + m_count( 0 ) +{ + m_numItems = 1; + m_items = new int[1]; + m_items[0] = itemId; + m_auxValues = new int[1]; + m_auxValues[0] = auxValue; +} + +CraftTask::CraftTask( int *items, int *auxValues, int numItems, int quantity, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion /*= true*/, vector *inConstraints /*= NULL*/, + bool bShowMinimumTime /*=false*/, bool bAllowFade /*=true*/, bool m_bTaskReminders /*=true*/ ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_quantity( quantity ), + m_count( 0 ) +{ + m_numItems = numItems; + m_items = new int[m_numItems]; + m_auxValues = new int[m_numItems]; + + for(int i = 0; i < m_numItems; ++i) + { + m_items[i] = items[i]; + m_auxValues[i] = auxValues[i]; + } +} + +CraftTask::~CraftTask() +{ + delete[] m_items; + delete[] m_auxValues; +} + +void CraftTask::onCrafted(shared_ptr item) +{ +#ifndef _CONTENT_PACKAGE + wprintf(L"CraftTask::onCrafted - %ls\n", item->toString().c_str() ); +#endif + bool itemFound = false; + for(int i = 0; i < m_numItems; ++i) + { + if(m_items[i] == item->id && (m_auxValues[i] == -1 || m_auxValues[i] == item->getAuxValue())) + { + itemFound = true; + break; + } + } + + if(itemFound) + { + ++m_count; + } + if( m_count >= m_quantity) + { + bIsCompleted = true; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/CraftTask.h b/Minecraft.Client/Common/Tutorial/CraftTask.h new file mode 100644 index 00000000..1496f07a --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/CraftTask.h @@ -0,0 +1,25 @@ +#pragma once +#include "TutorialTask.h" + +class CraftTask : public TutorialTask +{ +public: + CraftTask( int itemId, int auxValue, int quantity, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion = true, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ); + CraftTask( int *items, int *auxValues, int numItems, int quantity, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion = true, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ); + + ~CraftTask(); + + virtual bool isCompleted() { return bIsCompleted; } + virtual void onCrafted(shared_ptr item); + +private: + int *m_items; + int *m_auxValues; + int m_numItems; + int m_quantity; + int m_count; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/DiggerItemHint.cpp b/Minecraft.Client/Common/Tutorial/DiggerItemHint.cpp new file mode 100644 index 00000000..1367f411 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/DiggerItemHint.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.h" +#include "Tutorial.h" +#include "DiggerItemHint.h" + + +DiggerItemHint::DiggerItemHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int items[], unsigned int itemsLength) + : TutorialHint(id, tutorial, descriptionId, e_Hint_DiggerItem) +{ + m_iItemsCount = itemsLength; + + m_iItems= new int [m_iItemsCount]; + for(unsigned int i=0;iaddMessage(IDS_TUTORIAL_HINT_ATTACK_WITH_TOOL, true); +} + +int DiggerItemHint::startDestroyBlock(shared_ptr item, Tile *tile) +{ + if(item != NULL) + { + bool itemFound = false; + for(unsigned int i=0;iid == m_iItems[i]) + { + itemFound = true; + break; + } + } + if(itemFound) + { + float speed = item->getDestroySpeed(tile); + if(speed == 1) + { + // Display hint + return m_descriptionId; + } + } + } + return -1; +} + +int DiggerItemHint::attack(shared_ptr item, shared_ptr entity) +{ + if(item != NULL) + { + bool itemFound = false; + for(unsigned int i=0;iid == m_iItems[i]) + { + itemFound = true; + break; + } + } + if(itemFound) + { + // It's also possible that we could hit TileEntities (eg falling sand) so don't want to give this hint then + if( dynamic_pointer_cast( entity ) != NULL ) + { + return IDS_TUTORIAL_HINT_ATTACK_WITH_TOOL; + } + else + { + return -1; + } + } + } + return -1; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/DiggerItemHint.h b/Minecraft.Client/Common/Tutorial/DiggerItemHint.h new file mode 100644 index 00000000..cb71742e --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/DiggerItemHint.h @@ -0,0 +1,18 @@ +#pragma once + +#include "TutorialHint.h" + +class DiggerItem; +class Level; + +class DiggerItemHint : public TutorialHint +{ +private: + int *m_iItems; + unsigned int m_iItemsCount; + +public: + DiggerItemHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int items[], unsigned int itemsLength); + virtual int startDestroyBlock(shared_ptr item, Tile *tile); + virtual int attack(shared_ptr item, shared_ptr entity); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/EffectChangedTask.cpp b/Minecraft.Client/Common/Tutorial/EffectChangedTask.cpp new file mode 100644 index 00000000..5f1b5b20 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/EffectChangedTask.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.effect.h" +#include "EffectChangedTask.h" + +EffectChangedTask::EffectChangedTask(Tutorial *tutorial, int descriptionId, MobEffect *effect, bool apply, + bool enablePreCompletion, bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders ) + : TutorialTask(tutorial,descriptionId,enablePreCompletion,NULL,bShowMinimumTime,bAllowFade,bTaskReminders) +{ + m_effect = effect; + m_apply = apply; +} + +bool EffectChangedTask::isCompleted() +{ + return bIsCompleted; +} + +void EffectChangedTask::onEffectChanged(MobEffect *effect, bool bRemoved /*=false*/) +{ + if(effect == m_effect) + { + if(m_apply == !bRemoved) + { + bIsCompleted = true; + } + else + { + bIsCompleted = false; + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/EffectChangedTask.h b/Minecraft.Client/Common/Tutorial/EffectChangedTask.h new file mode 100644 index 00000000..23563f39 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/EffectChangedTask.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class MobEffect; + +class EffectChangedTask : public TutorialTask +{ +private: + MobEffect *m_effect; + bool m_apply; + +public: + EffectChangedTask(Tutorial *tutorial, int descriptionId, MobEffect *effect, bool apply = true, + bool enablePreCompletion = true, bool bShowMinimumTime = false, bool bAllowFade = true, bool bTaskReminders = true ); + virtual bool isCompleted(); + virtual void onEffectChanged(MobEffect *effect, bool bRemoved=false); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorial.cpp b/Minecraft.Client/Common/Tutorial/FullTutorial.cpp new file mode 100644 index 00000000..123ab9de --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorial.cpp @@ -0,0 +1,653 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\GameRules\ConsoleGameRules.h" +#include "DiggerItemHint.h" +#include "TutorialTasks.h" +#include "AreaHint.h" +#include "FullTutorial.h" +#include "TutorialConstraints.h" + +FullTutorial::FullTutorial(int iPad, bool isTrial /*= false*/) + : Tutorial(iPad, true) +{ + m_isTrial = isTrial; + m_freezeTime = true; + m_progressFlags = 0; + + for(unsigned int i = 0; i < e_Tutorial_State_Max; ++i) + { + m_completedStates[i] = false; + } + + addMessage(IDS_TUTORIAL_COMPLETED); + + /* + * + * + * GAMEPLAY + * + */ + // START OF BASIC TUTORIAL + if( m_isTrial ) + { + addTask(e_Tutorial_State_Gameplay, new ChoiceTask(this, IDS_TUTORIAL_TASK_OVERVIEW, IDS_TUTORIAL_PROMPT_START_TUTORIAL, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_TrialStart) ); + } + else + { +#ifdef _XBOX + if(getCompleted(eTutorial_Telemetry_Halfway) && !isStateCompleted(e_Tutorial_State_Redstone_And_Piston) ) + { + addTask(e_Tutorial_State_Gameplay, new ChoiceTask(this, IDS_TUTORIAL_NEW_FEATURES_CHOICE, IDS_TUTORIAL_PROMPT_NEW_FEATURES_CHOICE, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_TrialStart) ); + } + + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_OVERVIEW, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); +#else + if(getCompleted(eTutorial_Telemetry_Halfway)) + { + addTask(e_Tutorial_State_Gameplay, new ChoiceTask(this, IDS_TUTORIAL_TASK_OVERVIEW, IDS_TUTORIAL_PROMPT_START_TUTORIAL, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_TrialStart) ); + } + else + { + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_OVERVIEW, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } +#endif + } + + int lookMappings[] = {MINECRAFT_ACTION_LOOK_UP, MINECRAFT_ACTION_LOOK_DOWN, MINECRAFT_ACTION_LOOK_LEFT, MINECRAFT_ACTION_LOOK_RIGHT}; + int moveMappings[] = {MINECRAFT_ACTION_FORWARD, MINECRAFT_ACTION_BACKWARD, MINECRAFT_ACTION_LEFT, MINECRAFT_ACTION_RIGHT}; + int iLookCompletionMaskA[]= { 10, // 1010 + 9, // 1001 + 6, // 0110 + 5 // 0101 + }; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_LOOK, false, false, lookMappings, 4, iLookCompletionMaskA, 4, moveMappings, 4) ); + + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_MOVE, false, false, moveMappings, 4, iLookCompletionMaskA, 4, lookMappings, 4) ); + + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_SPRINT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + int jumpMappings[] = {MINECRAFT_ACTION_JUMP}; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_JUMP, false, true, jumpMappings, 1) ); + + int mineMappings[] = {MINECRAFT_ACTION_ACTION}; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_MINE, false, true, mineMappings, 1) ); + addTask(e_Tutorial_State_Gameplay, new PickupTask( Tile::treeTrunk_Id, 4, -1, this, IDS_TUTORIAL_TASK_CHOP_WOOD ) ); + + int scrollMappings[] = {MINECRAFT_ACTION_LEFT_SCROLL,MINECRAFT_ACTION_RIGHT_SCROLL}; + //int scrollMappings[] = {ACTION_MENU_LEFT_SCROLL,ACTION_MENU_RIGHT_SCROLL}; + int iScrollCompletionMaskA[]= { 2, // 10 + 1};// 01 + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_SCROLL, false, false, scrollMappings, 2,iScrollCompletionMaskA,2) ); + + int invMappings[] = {MINECRAFT_ACTION_INVENTORY}; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_INVENTORY, false, false, invMappings, 1) ); + addTask(e_Tutorial_State_Gameplay, new StateChangeTask( e_Tutorial_State_Inventory_Menu, this) ); + + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_DEPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_HEAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_FEED, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + // While they should only eat the item we give them, includ the ability to complete this task with different items + int foodItems[] = {Item::mushroomStew_Id, Item::apple_Id, Item::bread_Id, Item::porkChop_raw_Id, Item::porkChop_cooked_Id, + Item::apple_gold_Id, Item::fish_raw_Id, Item::fish_cooked_Id, Item::cookie_Id, Item::beef_cooked_Id, + Item::beef_raw_Id, Item::chicken_cooked_Id, Item::chicken_raw_Id, Item::melon_Id, Item::rotten_flesh_Id}; + addTask(e_Tutorial_State_Gameplay, new CompleteUsingItemTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_EAT_STEAK, foodItems, 15, true) ); + + int crftMappings[] = {MINECRAFT_ACTION_CRAFTING}; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_CRAFTING, false, false, crftMappings, 1) ); + + addTask(e_Tutorial_State_Gameplay, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_2_X_2_Crafting, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Gameplay, new StateChangeTask( e_Tutorial_State_2x2Crafting_Menu, this) ); + + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::wood_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_PLANKS) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::workBench_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_CRAFTING_TABLE) ); + + //int useMappings[] = {MINECRAFT_ACTION_USE}; + //addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_USE, false, false, useMappings, 1) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_USE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new UseItemTask( Tile::workBench_Id, this, IDS_TUTORIAL_TASK_PLACE_WORKBENCH, true ) ); + + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_NIGHT_DANGER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_NEARBY_SHELTER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_COLLECT_RESOURCES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + // END OF BASIC TUTORIAL + + addTask(e_Tutorial_State_Gameplay, new ChoiceTask(this, IDS_TUTORIAL_TASK_BASIC_COMPLETE, IDS_TUTORIAL_PROMPT_BASIC_COMPLETE, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_Halfway) ); + + // START OF FULL TUTORIAL + + addTask(e_Tutorial_State_Gameplay, new UseTileTask( Tile::workBench_Id, this, IDS_TUTORIAL_TASK_OPEN_WORKBENCH, false ) ); + + addTask(e_Tutorial_State_Gameplay, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_3_X_3_Crafting, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Gameplay, new StateChangeTask( e_Tutorial_State_3x3Crafting_Menu, this) ); + + addTask(e_Tutorial_State_Gameplay, new CraftTask( Item::stick->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_STICKS) ); + + int shovelItems[] = {Item::shovel_wood->id, Item::shovel_stone->id, Item::shovel_iron->id, Item::shovel_gold->id, Item::shovel_diamond->id}; + int shovelAuxVals[] = {-1,-1,-1,-1,-1}; + addTask(e_Tutorial_State_Gameplay, new CraftTask( shovelItems, shovelAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_SHOVEL) ); + + int hatchetItems[] = {Item::hatchet_wood->id, Item::hatchet_stone->id, Item::hatchet_iron->id, Item::hatchet_gold->id, Item::hatchet_diamond->id}; + int hatchetAuxVals[] = {-1,-1,-1,-1,-1}; + addTask(e_Tutorial_State_Gameplay, new CraftTask( hatchetItems, hatchetAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_HATCHET) ); + + int pickaxeItems[] = {Item::pickAxe_wood->id, Item::pickAxe_stone->id, Item::pickAxe_iron->id, Item::pickAxe_gold->id, Item::pickAxe_diamond->id}; + int pickaxeAuxVals[] = {-1,-1,-1,-1,-1}; + addTask(e_Tutorial_State_Gameplay, new CraftTask( pickaxeItems, pickaxeAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_PICKAXE) ); + + addTask(e_Tutorial_State_Gameplay, new PickupTask( Tile::stoneBrick_Id, 8, -1, this, IDS_TUTORIAL_TASK_MINE_STONE ) ); + + addTask(e_Tutorial_State_Gameplay, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_CRAFT_FURNACE, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::furnace_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_FURNACE ) ); + addTask(e_Tutorial_State_Gameplay, new UseTileTask(Tile::furnace_Id, this, IDS_TUTORIAL_TASK_PLACE_AND_OPEN_FURNACE) ); + + addTask(e_Tutorial_State_Gameplay, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_USE_FURNACE, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Gameplay, new StateChangeTask( e_Tutorial_State_Furnace_Menu, this) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Item::coal->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_CHARCOAL) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::glass_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_GLASS) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Item::door_wood->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_DOOR) ); + addTask(e_Tutorial_State_Gameplay, new UseItemTask(Item::door_wood->id, this, IDS_TUTORIAL_TASK_PLACE_DOOR) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::torch_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_TORCH) ); + + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"tutorialArea"); + if(area != NULL) + { + vector *areaConstraints = new vector(); + areaConstraints->push_back( new AreaConstraint( IDS_TUTORIAL_CONSTRAINT_TUTORIAL_AREA, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + addTask(e_Tutorial_State_Gameplay, new AreaTask(e_Tutorial_State_Gameplay,this, areaConstraints) ); + } + } + + // This MUST be the last task in the e_Tutorial_State_Gameplay state. Some of the earlier tasks will skip to the last + // task when complete, and this is the one that we want the player to see. + ProcedureCompoundTask *finalTask = new ProcedureCompoundTask( this ); + finalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_COMPLETED, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A, eTelemetryTutorial_Complete) ); + // 4J Stu - Remove this string as it refers to things that don't exist in the current tutorial world! + //finalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_FEATURES_IN_THIS_AREA, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + finalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_FEATURES_OUTSIDE_THIS_AREA, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + finalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_COMPLETED_EXPLORE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, finalTask); + // END OF FULL TUTORIAL + + + /* + * + * + * INVENTORY + * + */ + // Some tasks already added in the super class ctor + addTask(e_Tutorial_State_Inventory_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + addTask(e_Tutorial_State_Inventory_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_INV_EXIT, -1, false, ACTION_MENU_B) ); + + /* + * + * + * CRAFTING + * + */ + // Some tasks already added in the super class ctor + + addTask(e_Tutorial_State_2x2Crafting_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + // To block progress + addTask(e_Tutorial_State_2x2Crafting_Menu, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_2_X_2_Crafting, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + addTask(e_Tutorial_State_2x2Crafting_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + + addTask(e_Tutorial_State_2x2Crafting_Menu, new CraftTask( Tile::wood_Id, -1, 1, this, IDS_TUTORIAL_TASK_CRAFT_CREATE_PLANKS) ); + + ProcedureCompoundTask *workbenchCompound = new ProcedureCompoundTask( this ); + workbenchCompound->AddTask( new XuiCraftingTask( this, IDS_TUTORIAL_TASK_CRAFT_SELECT_STRUCTURES, Recipy::eGroupType_Structure) ); + workbenchCompound->AddTask( new XuiCraftingTask( this, IDS_TUTORIAL_TASK_CRAFT_SELECT_CRAFTING_TABLE, Tile::workBench_Id) ); + workbenchCompound->AddTask( new CraftTask( Tile::workBench_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_CRAFTING_TABLE) ); + addTask(e_Tutorial_State_2x2Crafting_Menu, workbenchCompound ); + addTask(e_Tutorial_State_2x2Crafting_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_EXIT_AND_PLACE_TABLE, -1, false, ACTION_MENU_B) ); + + // 3x3 Crafting + addTask(e_Tutorial_State_3x3Crafting_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + + addTask(e_Tutorial_State_3x3Crafting_Menu, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_3_X_3_Crafting, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( Item::stick->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_STICKS) ); + + ProcedureCompoundTask *shovelCompound = new ProcedureCompoundTask( this ); + shovelCompound->AddTask( new XuiCraftingTask( this, IDS_TUTORIAL_TASK_CRAFT_SELECT_TOOLS, Recipy::eGroupType_Tool) ); + shovelCompound->AddTask( new XuiCraftingTask( this, IDS_TUTORIAL_TASK_CRAFT_SELECT_WOODEN_SHOVEL, Item::shovel_wood->id) ); + shovelCompound->AddTask( new CraftTask( shovelItems, shovelAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_SHOVEL) ); + addTask(e_Tutorial_State_3x3Crafting_Menu, shovelCompound ); + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( hatchetItems, hatchetAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_HATCHET) ); + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( pickaxeItems, pickaxeAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_PICKAXE) ); + + addTask(e_Tutorial_State_3x3Crafting_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_TOOLS_BUILT, -1, false, ACTION_MENU_B) ); + + // To block progress + addTask(e_Tutorial_State_3x3Crafting_Menu, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_CRAFT_FURNACE, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( Tile::furnace_Id, -1, 1, this, IDS_TUTORIAL_TASK_CRAFT_CREATE_FURNACE) ); + addTask(e_Tutorial_State_3x3Crafting_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_EXIT_AND_PLACE_FURNACE, -1, false, ACTION_MENU_B) ); + + // No need to block here, as it's fine if the player wants to do this out of order + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( Item::door_wood->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_DOOR) ); + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( Tile::torch_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_TORCH) ); + + /* + * + * + * FURNACE + * + */ + // Some tasks already added in the super class ctor + + addTask(e_Tutorial_State_Furnace_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + + // Blocking + addTask(e_Tutorial_State_Furnace_Menu, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_USE_FURNACE, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + addTask(e_Tutorial_State_Furnace_Menu, new CraftTask( Item::coal->id, -1, 1, this, IDS_TUTORIAL_TASK_FURNACE_CREATE_CHARCOAL) ); + addTask(e_Tutorial_State_Furnace_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_FURNACE_CHARCOAL_USES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Furnace_Menu, new CraftTask( Tile::glass_Id, -1, 1, this, IDS_TUTORIAL_TASK_FURNACE_CREATE_GLASS) ); + + /* + * + * + * BREWING + * + */ + + // To block progress + addTask(e_Tutorial_State_Brewing_Menu, new ProgressFlagTask( &m_progressFlags, EXTENDED_TUTORIAL_PROGRESS_USE_BREWING_STAND, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + int potionItems[] = {Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id}; + int potionAuxVals[] = { MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_FIRE_RESISTANCE) + }; + addTask(e_Tutorial_State_Brewing_Menu, new CraftTask( potionItems, potionAuxVals, 8, 1, this, IDS_TUTORIAL_TASK_BREWING_MENU_CREATE_FIRE_POTION) ); + addTask(e_Tutorial_State_Brewing_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_EXIT, -1, false, ACTION_MENU_B) ); + + /* + * + * + * MINECART + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"minecartArea"); + if(area != NULL) + { + addHint(e_Tutorial_State_Gameplay, new AreaHint(e_Tutorial_Hint_Always_On, this, e_Tutorial_State_Gameplay, e_Tutorial_State_Riding_Minecart, IDS_TUTORIAL_HINT_MINECART, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1 ) ); + } + } + + /* + * + * + * BOAT + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"boatArea"); + if(area != NULL) + { + addHint(e_Tutorial_State_Gameplay, new AreaHint(e_Tutorial_Hint_Always_On, this, e_Tutorial_State_Gameplay, e_Tutorial_State_Riding_Boat, IDS_TUTORIAL_HINT_BOAT, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1 ) ); + } + } + + /* + * + * + * FISHING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"fishingArea"); + if(area != NULL) + { + addHint(e_Tutorial_State_Gameplay, new AreaHint(e_Tutorial_Hint_Always_On, this, e_Tutorial_State_Gameplay, e_Tutorial_State_Fishing, IDS_TUTORIAL_HINT_FISHING, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1 ) ); + } + } + + /* + * + * + * PISTON - SELF-REPAIRING BRIDGE + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"pistonBridgeArea"); + if(area != NULL) + { + addHint(e_Tutorial_State_Gameplay, new AreaHint(e_Tutorial_Hint_Always_On, this, e_Tutorial_State_Gameplay, e_Tutorial_State_None, IDS_TUTORIAL_HINT_PISTON_SELF_REPAIRING_BRIDGE, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1, true ) ); + } + } + + /* + * + * + * PISTON - PISTON AND REDSTONE CIRCUITS + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"pistonArea"); + if(area != NULL) + { + eTutorial_State redstoneAndPistonStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Redstone_And_Piston, redstoneAndPistonStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Redstone_And_Piston, new ChoiceTask(this, IDS_TUTORIAL_REDSTONE_OVERVIEW, IDS_TUTORIAL_PROMPT_REDSTONE_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Redstone_And_Pistons) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_POWER_SOURCES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_TRIPWIRE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_POWER_SOURCES_POSITION, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_DUST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_REPEATER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_PISTONS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_TRY_IT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * PORTAL + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"portalArea"); + if(area != NULL) + { + eTutorial_State portalStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Portal, portalStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Portal, new ChoiceTask(this, IDS_TUTORIAL_PORTAL_OVERVIEW, IDS_TUTORIAL_PROMPT_PORTAL_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Portal) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_BUILD_PORTAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_ACTIVATE_PORTAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_USE_PORTAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_NETHER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_NETHER_FAST_TRAVEL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * CREATIVE + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"creativeArea"); + if(area != NULL) + { + eTutorial_State creativeStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_CreativeMode, creativeStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1,true,true,GameType::CREATIVE) ); + + addTask(e_Tutorial_State_CreativeMode, new ChoiceTask(this, IDS_TUTORIAL_CREATIVE_OVERVIEW, IDS_TUTORIAL_PROMPT_CREATIVE_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_CreativeMode) ); + addTask(e_Tutorial_State_CreativeMode, new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_MODE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_CreativeMode, new InfoTask(this, IDS_TUTORIAL_TASK_FLY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + int crftMappings[] = {MINECRAFT_ACTION_CRAFTING}; + addTask(e_Tutorial_State_CreativeMode, new ControllerTask( this, IDS_TUTORIAL_TASK_OPEN_CREATIVE_INVENTORY, false, false, crftMappings, 1) ); + addTask(e_Tutorial_State_CreativeMode, new StateChangeTask( e_Tutorial_State_Creative_Inventory_Menu, this) ); + + // This last task ensures that the player is still in creative mode until they exit the area (but could skip the previous instructional stuff) + ProcedureCompoundTask *creativeFinalTask = new ProcedureCompoundTask( this ); + + AABB *exitArea = app.getGameRuleDefinitions()->getNamedArea(L"creativeExitArea"); + if(exitArea != NULL) + { + vector *creativeExitAreaConstraints = new vector(); + creativeExitAreaConstraints->push_back( new AreaConstraint( -1, exitArea->x0,exitArea->y0,exitArea->z0,exitArea->x1,exitArea->y1,exitArea->z1,true,false) ); + creativeFinalTask->AddTask( new AreaTask(e_Tutorial_State_CreativeMode, this, creativeExitAreaConstraints,IDS_TUTORIAL_TASK_CREATIVE_EXIT,AreaTask::eAreaTaskCompletion_CompleteOnConstraintsSatisfied) ); + } + + vector *creativeAreaConstraints = new vector(); + creativeAreaConstraints->push_back( new AreaConstraint( IDS_TUTORIAL_CONSTRAINT_TUTORIAL_AREA, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + creativeFinalTask->AddTask( new AreaTask(e_Tutorial_State_CreativeMode, this, creativeAreaConstraints) ); + + creativeFinalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_COMPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + addTask(e_Tutorial_State_CreativeMode,creativeFinalTask); + } + } + + /* + * + * + * BREWING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"brewingArea"); + if(area != NULL) + { + eTutorial_State brewingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Brewing, brewingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Brewing, new ChoiceTask(this, IDS_TUTORIAL_TASK_BREWING_OVERVIEW, IDS_TUTORIAL_PROMPT_BREWING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Brewing) ); + + ProcedureCompoundTask *fillWaterBottleTask = new ProcedureCompoundTask( this ); + fillWaterBottleTask->AddTask( new PickupTask( Item::glassBottle_Id, 1, -1, this, IDS_TUTORIAL_TASK_BREWING_GET_GLASS_BOTTLE ) ); + fillWaterBottleTask->AddTask( new PickupTask( Item::potion_Id, 1, 0, this, IDS_TUTORIAL_TASK_BREWING_FILL_GLASS_BOTTLE ) ); + addTask(e_Tutorial_State_Brewing, fillWaterBottleTask); + + addTask(e_Tutorial_State_Brewing, new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_FILL_CAULDRON, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + + addTask(e_Tutorial_State_Brewing, new ProgressFlagTask( &m_progressFlags, EXTENDED_TUTORIAL_PROGRESS_USE_BREWING_STAND, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Brewing, new CraftTask( potionItems, potionAuxVals, 8, 1, this, IDS_TUTORIAL_TASK_BREWING_CREATE_FIRE_POTION) ); + + addTask(e_Tutorial_State_Brewing, new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_USE_POTION, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Brewing, new EffectChangedTask(this, IDS_TUTORIAL_TASK_BREWING_DRINK_FIRE_POTION, MobEffect::fireResistance) ); + addTask(e_Tutorial_State_Brewing, new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_USE_EFFECTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * ENCHANTING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"enchantingArea"); + if(area != NULL) + { + eTutorial_State enchantingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Enchanting, enchantingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Enchanting, new ChoiceTask(this, IDS_TUTORIAL_TASK_ENCHANTING_OVERVIEW, IDS_TUTORIAL_PROMPT_ENCHANTING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Enchanting) ); + + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_SUMMARY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_BOOKS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_BOOKCASES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_EXPERIENCE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_BOTTLE_O_ENCHANTING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_USE_CHESTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * ANVIL + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"anvilArea"); + if(area != NULL) + { + eTutorial_State enchantingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Anvil, enchantingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Anvil, new ChoiceTask(this, IDS_TUTORIAL_TASK_ANVIL_OVERVIEW, IDS_TUTORIAL_PROMPT_ANVIL_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Anvil) ); + + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_SUMMARY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_ENCHANTED_BOOKS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_COST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_COST2, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_RENAMING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_USE_CHESTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * TRADING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"tradingArea"); + if(area != NULL) + { + eTutorial_State enchantingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Trading, enchantingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Trading, new ChoiceTask(this, IDS_TUTORIAL_TASK_TRADING_OVERVIEW, IDS_TUTORIAL_PROMPT_TRADING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Trading) ); + + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_SUMMARY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_TRADES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_INCREASE_TRADES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_DECREASE_TRADES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_USE_CHESTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * ENDERCHEST + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"enderchestArea"); + if(area != NULL) + { + eTutorial_State enchantingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Enderchests, enchantingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Enderchests, new ChoiceTask(this, IDS_TUTORIAL_TASK_ENDERCHEST_OVERVIEW, IDS_TUTORIAL_PROMPT_ENDERCHEST_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Enderchest) ); + + addTask(e_Tutorial_State_Enderchests, new InfoTask(this, IDS_TUTORIAL_TASK_ENDERCHEST_SUMMARY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enderchests, new InfoTask(this, IDS_TUTORIAL_TASK_ENDERCHEST_PLAYERS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enderchests, new InfoTask(this, IDS_TUTORIAL_TASK_ENDERCHEST_FUNCTION, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * FARMING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"farmingArea"); + if(area != NULL) + { + eTutorial_State farmingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Farming, farmingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Farming, new ChoiceTask(this, IDS_TUTORIAL_FARMING_OVERVIEW, IDS_TUTORIAL_PROMPT_FARMING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Farming) ); + + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_SEEDS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_FARMLAND, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_WHEAT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_PUMPKIN_AND_MELON, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_CARROTS_AND_POTATOES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_SUGARCANE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_CACTUS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_MUSHROOM, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_BONEMEAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_COMPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * BREEDING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"breedingArea"); + if(area != NULL) + { + eTutorial_State breedingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Breeding, breedingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Breeding, new ChoiceTask(this, IDS_TUTORIAL_BREEDING_OVERVIEW, IDS_TUTORIAL_PROMPT_BREEDING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Breeding) ); + + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_FEED, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_FEED_FOOD, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_BABY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_DELAY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_FOLLOW, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_RIDING_PIGS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_WOLF_TAMING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_WOLF_COLLAR, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_COMPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * SNOW AND IRON GOLEM + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"golemArea"); + if(area != NULL) + { + eTutorial_State golemStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Golem, golemStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Golem, new ChoiceTask(this, IDS_TUTORIAL_GOLEM_OVERVIEW, IDS_TUTORIAL_PROMPT_GOLEM_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Golem) ); + + addTask(e_Tutorial_State_Golem, new InfoTask(this, IDS_TUTORIAL_TASK_GOLEM_PUMPKIN, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Golem, new InfoTask(this, IDS_TUTORIAL_TASK_GOLEM_SNOW, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Golem, new InfoTask(this, IDS_TUTORIAL_TASK_GOLEM_IRON, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Golem, new InfoTask(this, IDS_TUTORIAL_TASK_GOLEM_IRON_VILLAGE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + } + } + +} + +// 4J Stu - All tutorials are onby default in the full tutorial whether the player has previously completed them or not +bool FullTutorial::isStateCompleted( eTutorial_State state ) +{ + return m_completedStates[state]; +} + +void FullTutorial::setStateCompleted( eTutorial_State state ) +{ + m_completedStates[state] = true; + Tutorial::setStateCompleted(state); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorial.h b/Minecraft.Client/Common/Tutorial/FullTutorial.h new file mode 100644 index 00000000..da2641d2 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorial.h @@ -0,0 +1,21 @@ +#pragma once +#include "Tutorial.h" + +#define FULL_TUTORIAL_PROGRESS_2_X_2_Crafting 1 +#define FULL_TUTORIAL_PROGRESS_3_X_3_Crafting 2 +#define FULL_TUTORIAL_PROGRESS_CRAFT_FURNACE 4 +#define FULL_TUTORIAL_PROGRESS_USE_FURNACE 8 +#define EXTENDED_TUTORIAL_PROGRESS_USE_BREWING_STAND 16 + +class FullTutorial : public Tutorial +{ +private: + bool m_isTrial; + char m_progressFlags; + bool m_completedStates[e_Tutorial_State_Max]; +public: + FullTutorial(int iPad, bool isTrial = false); + + virtual bool isStateCompleted( eTutorial_State state ); + virtual void setStateCompleted( eTutorial_State state ); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.cpp b/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.cpp new file mode 100644 index 00000000..54985d21 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "Tutorial.h" +#include "FullTutorialActiveTask.h" + +FullTutorialActiveTask::FullTutorialActiveTask(Tutorial *tutorial, eTutorial_CompletionAction completeAction /*= e_Tutorial_Completion_None*/) + : TutorialTask( tutorial, -1, false, NULL, false, false, false ) +{ + m_completeAction = completeAction; +} + +bool FullTutorialActiveTask::isCompleted() +{ + return bHasBeenActivated; +} + +eTutorial_CompletionAction FullTutorialActiveTask::getCompletionAction() +{ + if( tutorial->m_fullTutorialComplete ) + { + return m_completeAction; + } + else + { + return e_Tutorial_Completion_None; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.h b/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.h new file mode 100644 index 00000000..5aa05610 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.h @@ -0,0 +1,18 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +// Information messages with a choice +class FullTutorialActiveTask : public TutorialTask +{ +private: + eTutorial_CompletionAction m_completeAction; + + bool CompletionMaskIsValid(); +public: + FullTutorialActiveTask(Tutorial *tutorial, eTutorial_CompletionAction completeAction = e_Tutorial_Completion_None); + virtual bool isCompleted(); + virtual eTutorial_CompletionAction getCompletionAction(); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorialMode.cpp b/Minecraft.Client/Common/Tutorial/FullTutorialMode.cpp new file mode 100644 index 00000000..a5ee85b8 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorialMode.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "FullTutorial.h" +#include "FullTutorialMode.h" + +FullTutorialMode::FullTutorialMode(int iPad, Minecraft *minecraft, ClientConnection *connection) + : TutorialMode(iPad, minecraft, connection) +{ + tutorial = new FullTutorial( iPad ); + minecraft->playerStartedTutorial( iPad ); +} + +bool FullTutorialMode::isTutorial() +{ + return !tutorial->m_fullTutorialComplete; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorialMode.h b/Minecraft.Client/Common/Tutorial/FullTutorialMode.h new file mode 100644 index 00000000..ce6f1819 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorialMode.h @@ -0,0 +1,12 @@ +#pragma once +#include "TutorialMode.h" + +class FullTutorialMode : public TutorialMode +{ +public: + FullTutorialMode(int iPad, Minecraft *minecraft, ClientConnection *connection); + + virtual bool isImplemented() { return true; } + + virtual bool isTutorial(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/InfoTask.cpp b/Minecraft.Client/Common/Tutorial/InfoTask.cpp new file mode 100644 index 00000000..5330841f --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/InfoTask.cpp @@ -0,0 +1,137 @@ +#include "stdafx.h" +#include +#include +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "Tutorial.h" +#include "TutorialConstraints.h" +#include "InfoTask.h" +#include "..\..\..\Minecraft.World\Material.h" + +InfoTask::InfoTask(Tutorial *tutorial, int descriptionId, int promptId /*= -1*/, bool requiresUserInput /*= false*/, + int iMapping /*= 0*/, ETelemetryChallenges telemetryEvent /*= eTelemetryTutorial_NoEvent*/) + : TutorialTask( tutorial, descriptionId, false, NULL, true, false, false ) +{ + if(requiresUserInput == true) + { + constraints.push_back( new InputConstraint( iMapping ) ); + } + completedMappings[iMapping]=false; + + m_promptId = promptId; + tutorial->addMessage( m_promptId ); + + m_eTelemetryEvent = telemetryEvent; +} + +bool InfoTask::isCompleted() +{ + if( bIsCompleted ) + return true; + + if( tutorial->m_hintDisplayed ) + return false; + + if( !bHasBeenActivated || !m_bShownForMinimumTime ) + return false; + + bool bAllComplete = true; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + // If the player is under water then allow all keypresses so they can jump out + if( pMinecraft->localplayers[tutorial->getPad()]->isUnderLiquid(Material::water) ) return false; + + if(ui.GetMenuDisplayed(tutorial->getPad())) + { + // If a menu is displayed, then we use the handleUIInput to complete the task + bAllComplete = true; + for(AUTO_VAR(it, completedMappings.begin()); it != completedMappings.end(); ++it) + { + bool current = (*it).second; + if(!current) + { + bAllComplete = false; + break; + } + } + } + else + { + int iCurrent=0; + + for(AUTO_VAR(it, completedMappings.begin()); it != completedMappings.end(); ++it) + { + bool current = (*it).second; + if(!current) + { + if( InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 ) + { + (*it).second = true; + bAllComplete=true; + } + else + { + bAllComplete = false; + } + } + iCurrent++; + } + } + + if(bAllComplete==true) + { + sendTelemetry(); + enableConstraints(false, true); + } + bIsCompleted = bAllComplete; + return bAllComplete; +} + +int InfoTask::getPromptId() +{ + if( m_bShownForMinimumTime ) + return m_promptId; + else + return -1; +} + +void InfoTask::setAsCurrentTask(bool active /*= true*/) +{ + enableConstraints( active ); + TutorialTask::setAsCurrentTask(active); +} + +void InfoTask::handleUIInput(int iAction) +{ + if(bHasBeenActivated) + { + for(AUTO_VAR(it, completedMappings.begin()); it != completedMappings.end(); ++it) + { + if( iAction == (*it).first ) + { + (*it).second = true; + } + } + } +} + + +void InfoTask::sendTelemetry() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( m_eTelemetryEvent != eTelemetryChallenges_Unknown ) + { + bool firstPlay = true; + // We only store first play for some of the events + switch(m_eTelemetryEvent) + { + case eTelemetryTutorial_Complete: + firstPlay = !tutorial->getCompleted( eTutorial_Telemetry_Complete ); + tutorial->setCompleted( eTutorial_Telemetry_Complete ); + break; + }; + TelemetryManager->RecordEnemyKilledOrOvercome(pMinecraft->player->GetXboxPad(), 0, 0, 0, 0, 0, 0, m_eTelemetryEvent); + } +} diff --git a/Minecraft.Client/Common/Tutorial/InfoTask.h b/Minecraft.Client/Common/Tutorial/InfoTask.h new file mode 100644 index 00000000..e072038b --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/InfoTask.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +// Information messages +class InfoTask : public TutorialTask +{ +private: + unordered_map completedMappings; + + ETelemetryChallenges m_eTelemetryEvent; + + bool CompletionMaskIsValid(); +public: + InfoTask(Tutorial *tutorial, int descriptionId, int promptId = -1, bool requiresUserInput = false, int iMapping = 0, ETelemetryChallenges telemetryEvent = eTelemetryChallenges_Unknown); + virtual bool isCompleted(); + virtual int getPromptId(); + virtual void setAsCurrentTask(bool active = true); + virtual void handleUIInput(int iAction); + +private: + void sendTelemetry(); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/InputConstraint.cpp b/Minecraft.Client/Common/Tutorial/InputConstraint.cpp new file mode 100644 index 00000000..a26d3eb1 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/InputConstraint.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "InputConstraint.h" + +bool InputConstraint::isMappingConstrained(int iPad, int mapping) +{ + // If it's a menu button, then we ignore all inputs + if((m_inputMapping == mapping) || (mapping < ACTION_MAX_MENU)) + { + return true; + } + + // Otherwise see if they map to the same actual button + unsigned char layoutMapping = InputManager.GetJoypadMapVal( iPad ); + + // 4J HEG - Replaced the equivalance test with bitwise AND, important in some mapping configurations + // (e.g. when comparing two action map values and one has extra buttons mapped) + return (InputManager.GetGameJoypadMaps(layoutMapping,m_inputMapping) & InputManager.GetGameJoypadMaps(layoutMapping,mapping)) > 0; +} diff --git a/Minecraft.Client/Common/Tutorial/InputConstraint.h b/Minecraft.Client/Common/Tutorial/InputConstraint.h new file mode 100644 index 00000000..3d6bee61 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/InputConstraint.h @@ -0,0 +1,15 @@ +#pragma once + +#include "TutorialConstraint.h" + +class InputConstraint : public TutorialConstraint +{ +private: + int m_inputMapping; // Should be one of the EControllerActions +public: + virtual ConstraintType getType() { return e_ConstraintInput; } + + InputConstraint(int mapping) : TutorialConstraint(-1), m_inputMapping( mapping ) {} + + virtual bool isMappingConstrained(int iPad, int mapping); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/LookAtEntityHint.cpp b/Minecraft.Client/Common/Tutorial/LookAtEntityHint.cpp new file mode 100644 index 00000000..3b4680ce --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/LookAtEntityHint.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "Tutorial.h" +#include "LookAtEntityHint.h" + + +LookAtEntityHint::LookAtEntityHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int titleId, eINSTANCEOF type) + : TutorialHint(id, tutorial, descriptionId, e_Hint_LookAtEntity) +{ + m_type = type; + m_titleId = titleId; +} + +bool LookAtEntityHint::onLookAtEntity(eINSTANCEOF type) +{ + if(m_type == type) + { + // Display hint + Tutorial::PopupMessageDetails *message = new Tutorial::PopupMessageDetails(); + message->m_messageId = m_descriptionId; + message->m_titleId = m_titleId; + message->m_delay = true; + return m_tutorial->setMessage(this, message); + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/LookAtEntityHint.h b/Minecraft.Client/Common/Tutorial/LookAtEntityHint.h new file mode 100644 index 00000000..99136691 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/LookAtEntityHint.h @@ -0,0 +1,20 @@ +#pragma once +using namespace std; + +#include "..\..\..\Minecraft.World\Class.h" +#include "TutorialHint.h" + +class ItemInstance; + +class LookAtEntityHint : public TutorialHint +{ +private: + eINSTANCEOF m_type; + int m_titleId; + +public: + LookAtEntityHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int titleId, eINSTANCEOF type); + ~LookAtEntityHint(); + + virtual bool onLookAtEntity(eINSTANCEOF type); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/LookAtTileHint.cpp b/Minecraft.Client/Common/Tutorial/LookAtTileHint.cpp new file mode 100644 index 00000000..0a953a7b --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/LookAtTileHint.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "Tutorial.h" +#include "LookAtTileHint.h" + + +LookAtTileHint::LookAtTileHint(eTutorial_Hint id, Tutorial *tutorial, int tiles[], unsigned int tilesLength, int iconOverride /*= -1*/, int iData /* = -1 */, int iDataOverride /*= -1*/) + : TutorialHint(id, tutorial, -1, e_Hint_LookAtTile) +{ + m_iTilesCount = tilesLength; + + m_iTiles= new int [m_iTilesCount]; + for(unsigned int i=0;i 0 && id < 256 && (m_iData == -1 || m_iData == iData) ) + { + bool itemFound = false; + for(unsigned int i=0;im_delay = true; + if( m_iconOverride >= 0 ) + { + message->m_icon = m_iconOverride; + } + else if(m_iconOverride == -2) + { + message->m_icon = TUTORIAL_NO_ICON; + } + else + { + message->m_icon = id; + if(m_iDataOverride > -1) + { + message->m_iAuxVal = m_iDataOverride; + } + else + { + message->m_iAuxVal = iData; + } + } + message->m_messageId = Item::items[id]->getUseDescriptionId(); + message->m_titleId = Item::items[id]->getDescriptionId(message->m_iAuxVal); + return m_tutorial->setMessage(this, message); + } + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/LookAtTileHint.h b/Minecraft.Client/Common/Tutorial/LookAtTileHint.h new file mode 100644 index 00000000..34ec1b95 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/LookAtTileHint.h @@ -0,0 +1,22 @@ +#pragma once +using namespace std; + +#include "TutorialHint.h" + +class ItemInstance; + +class LookAtTileHint : public TutorialHint +{ +private: + int *m_iTiles; + unsigned int m_iTilesCount; + int m_iconOverride; + int m_iData; + int m_iDataOverride; + +public: + LookAtTileHint(eTutorial_Hint id, Tutorial *tutorial, int tiles[], unsigned int tilesLength, int iconOverride = -1, int iData=-1, int iDataOverride = -1); + ~LookAtTileHint(); + + virtual bool onLookAt(int id, int iData=0); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/PickupTask.cpp b/Minecraft.Client/Common/Tutorial/PickupTask.cpp new file mode 100644 index 00000000..00bc9d1f --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/PickupTask.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "PickupTask.h" + +void PickupTask::onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux) +{ + if(item->id == m_itemId) + { + if(m_auxValue == -1 && invItemCountAnyAux >= m_quantity) + { + bIsCompleted = true; + } + else if( m_auxValue == item->getAuxValue() && invItemCountThisAux >= m_quantity) + { + bIsCompleted = true; + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/PickupTask.h b/Minecraft.Client/Common/Tutorial/PickupTask.h new file mode 100644 index 00000000..68e1d479 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/PickupTask.h @@ -0,0 +1,26 @@ +#pragma once +using namespace std; +#include "TutorialTask.h" + +class ItemInstance; + +class PickupTask : public TutorialTask +{ +public: + PickupTask( int itemId, unsigned int quantity, int auxValue, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion = true, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_itemId( itemId), + m_quantity( quantity ), + m_auxValue( auxValue ) + {} + + virtual bool isCompleted() { return bIsCompleted; } + virtual void onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux); + +private: + int m_itemId; + unsigned int m_quantity; + int m_auxValue; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.cpp b/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.cpp new file mode 100644 index 00000000..8603f765 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.cpp @@ -0,0 +1,263 @@ +#include "stdafx.h" +#include "ProcedureCompoundTask.h" + +ProcedureCompoundTask::~ProcedureCompoundTask() +{ + for(AUTO_VAR(it, m_taskSequence.begin()); it < m_taskSequence.end(); ++it) + { + delete (*it); + } +} + +void ProcedureCompoundTask::AddTask(TutorialTask *task) +{ + if(task != NULL) + { + m_taskSequence.push_back(task); + } +} + +int ProcedureCompoundTask::getDescriptionId() +{ + if(bIsCompleted) + return -1; + + // Return the id of the first task not completed + int descriptionId = -1; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + task->setAsCurrentTask(true); + descriptionId = task->getDescriptionId(); + break; + } + else if(task->getCompletionAction() == e_Tutorial_Completion_Complete_State) + { + bIsCompleted = true; + break; + } + } + return descriptionId; +} + +int ProcedureCompoundTask::getPromptId() +{ + if(bIsCompleted) + return -1; + + // Return the id of the first task not completed + int promptId = -1; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + promptId = task->getPromptId(); + break; + } + } + return promptId; +} + +bool ProcedureCompoundTask::isCompleted() +{ + // Return whether all tasks are completed + + bool allCompleted = true; + bool isCurrentTask = true; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + + if(allCompleted && isCurrentTask) + { + if(task->isCompleted()) + { + if(task->getCompletionAction() == e_Tutorial_Completion_Complete_State) + { + allCompleted = true; + break; + } + } + else + { + task->setAsCurrentTask(true); + allCompleted = false; + isCurrentTask = false; + } + } + else if (!allCompleted) + { + task->setAsCurrentTask(false); + } + } + + if(allCompleted) + { + //Disable all constraints + itEnd = m_taskSequence.end(); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->enableConstraints(false); + } + } + bIsCompleted = allCompleted; + return allCompleted; +} + +void ProcedureCompoundTask::onCrafted(shared_ptr item) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->onCrafted(item); + } +} + +void ProcedureCompoundTask::handleUIInput(int iAction) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->handleUIInput(iAction); + } +} + + +void ProcedureCompoundTask::setAsCurrentTask(bool active /*= true*/) +{ + bool allCompleted = true; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(allCompleted && !task->isCompleted()) + { + task->setAsCurrentTask(true); + allCompleted = false; + } + else if (!allCompleted) + { + task->setAsCurrentTask(false); + } + } +} + +bool ProcedureCompoundTask::ShowMinimumTime() +{ + if(bIsCompleted) + return false; + + bool showMinimumTime = false; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + showMinimumTime = task->ShowMinimumTime(); + break; + } + } + return showMinimumTime; +} + +bool ProcedureCompoundTask::hasBeenActivated() +{ + if(bIsCompleted) + return true; + + bool hasBeenActivated = false; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + hasBeenActivated = task->hasBeenActivated(); + break; + } + } + return hasBeenActivated; +} + +void ProcedureCompoundTask::setShownForMinimumTime() +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + task->setShownForMinimumTime(); + break; + } + } +} + +bool ProcedureCompoundTask::AllowFade() +{ + if(bIsCompleted) + return true; + + bool allowFade = true; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + allowFade = task->AllowFade(); + break; + } + } + return allowFade; +} + +void ProcedureCompoundTask::useItemOn(Level *level, shared_ptr item, int x, int y, int z,bool bTestUseOnly) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->useItemOn(level, item, x, y, z, bTestUseOnly); + } +} + +void ProcedureCompoundTask::useItem(shared_ptr item, bool bTestUseOnly) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->useItem(item, bTestUseOnly); + } +} + +void ProcedureCompoundTask::onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->onTake(item, invItemCountAnyAux, invItemCountThisAux); + } +} + +void ProcedureCompoundTask::onStateChange(eTutorial_State newState) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->onStateChange(newState); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.h b/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.h new file mode 100644 index 00000000..36b32798 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.h @@ -0,0 +1,36 @@ +#pragma once + +#include "TutorialTask.h" + +// A tutorial task that requires each of the task to be completed in order until the last one is complete. +// If an earlier task that was complete is now not complete then it's hint should be shown. +class ProcedureCompoundTask : public TutorialTask +{ +public: + ProcedureCompoundTask(Tutorial *tutorial ) + : TutorialTask(tutorial, -1, false, NULL, false, true, false ) + {} + + ~ProcedureCompoundTask(); + + void AddTask(TutorialTask *task); + + virtual int getDescriptionId(); + virtual int getPromptId(); + virtual bool isCompleted(); + virtual void onCrafted(shared_ptr item); + virtual void handleUIInput(int iAction); + virtual void setAsCurrentTask(bool active = true); + virtual bool ShowMinimumTime(); + virtual bool hasBeenActivated(); + virtual void setShownForMinimumTime(); + virtual bool AllowFade(); + + virtual void useItemOn(Level *level, shared_ptr item, int x, int y, int z, bool bTestUseOnly=false); + virtual void useItem(shared_ptr item, bool bTestUseOnly=false); + virtual void onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux); + virtual void onStateChange(eTutorial_State newState); + +private: + vector m_taskSequence; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ProgressFlagTask.cpp b/Minecraft.Client/Common/Tutorial/ProgressFlagTask.cpp new file mode 100644 index 00000000..ea224ca3 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ProgressFlagTask.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "ProgressFlagTask.h" + +bool ProgressFlagTask::isCompleted() +{ + switch( m_type ) + { + case e_Progress_Set_Flag: + (*flags) |= m_mask; + bIsCompleted = true; + break; + case e_Progress_Flag_On: + bIsCompleted = ((*flags) & m_mask) == m_mask; + break; + } + return bIsCompleted; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ProgressFlagTask.h b/Minecraft.Client/Common/Tutorial/ProgressFlagTask.h new file mode 100644 index 00000000..b96e1bc0 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ProgressFlagTask.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; +#include "Tutorial.h" +#include "TutorialTask.h" + +class ProgressFlagTask : public TutorialTask +{ +public: + enum EProgressFlagType + { + e_Progress_Set_Flag, + e_Progress_Flag_On, + }; +private: + char *flags; // Not a member of this object + char m_mask; + EProgressFlagType m_type; +public: + ProgressFlagTask(char *flags, char mask, EProgressFlagType type, Tutorial *tutorial ) : + TutorialTask(tutorial, -1, false, NULL ), + flags( flags ), m_mask( mask ), m_type( type ) + {} + + virtual bool isCompleted(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/StatTask.cpp b/Minecraft.Client/Common/Tutorial/StatTask.cpp new file mode 100644 index 00000000..5f8b215e --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/StatTask.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "..\..\LocalPlayer.h" +#include "..\..\StatsCounter.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "StatTask.h" + +StatTask::StatTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, Stat *stat, int variance /*= 1*/) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, NULL ) +{ + this->stat = stat; + + Minecraft *minecraft = Minecraft::GetInstance(); + targetValue = minecraft->stats[ProfileManager.GetPrimaryPad()]->getTotalValue( stat ) + variance; +} + +bool StatTask::isCompleted() +{ + if( bIsCompleted ) + return true; + + Minecraft *minecraft = Minecraft::GetInstance(); + bIsCompleted = minecraft->stats[ProfileManager.GetPrimaryPad()]->getTotalValue( stat ) >= (unsigned int)targetValue; + return bIsCompleted; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/StatTask.h b/Minecraft.Client/Common/Tutorial/StatTask.h new file mode 100644 index 00000000..ba38f00a --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/StatTask.h @@ -0,0 +1,18 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class Stat; + +// 4J Stu - Tutorial tasks that can use the current stat trackin code. This is things like blocks mined/items crafted. +class StatTask : public TutorialTask +{ +private: + Stat *stat; + int targetValue; + +public: + StatTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, Stat *stat, int variance = 1); + virtual bool isCompleted(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/StateChangeTask.h b/Minecraft.Client/Common/Tutorial/StateChangeTask.h new file mode 100644 index 00000000..fb9e6396 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/StateChangeTask.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; +#include "Tutorial.h" +#include "TutorialTask.h" + +class StateChangeTask : public TutorialTask +{ +private: + eTutorial_State m_state; +public: + StateChangeTask(eTutorial_State state, + Tutorial *tutorial, int descriptionId = -1, bool enablePreCompletion = false, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ) : + TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_state( state ) + {} + + virtual bool isCompleted() { return bIsCompleted; } + + virtual void onStateChange(eTutorial_State newState) + { + if(newState == m_state) + { + bIsCompleted = true; + } + } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TakeItemHint.cpp b/Minecraft.Client/Common/Tutorial/TakeItemHint.cpp new file mode 100644 index 00000000..a1a5c37a --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TakeItemHint.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "Tutorial.h" +#include "TakeItemHint.h" + + +TakeItemHint::TakeItemHint(eTutorial_Hint id, Tutorial *tutorial, int items[], unsigned int itemsLength) + : TutorialHint(id, tutorial, -1, e_Hint_TakeItem) +{ + m_iItemsCount = itemsLength; + + m_iItems= new int [m_iItemsCount]; + for(unsigned int i=0;i item) +{ + if(item != NULL) + { + bool itemFound = false; + for(unsigned int i=0;iid == m_iItems[i]) + { + itemFound = true; + break; + } + } + if(itemFound) + { + // Display hint + Tutorial::PopupMessageDetails *message = new Tutorial::PopupMessageDetails(); + message->m_messageId = item->getUseDescriptionId(); + message->m_titleId = item->getDescriptionId(); + message->m_icon = item->id; + message->m_delay = true; + return m_tutorial->setMessage(this, message); + } + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TakeItemHint.h b/Minecraft.Client/Common/Tutorial/TakeItemHint.h new file mode 100644 index 00000000..f001d4c7 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TakeItemHint.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +#include "TutorialHint.h" + +class ItemInstance; + +class TakeItemHint : public TutorialHint +{ +private: + int *m_iItems; + unsigned int m_iItemsCount; + +public: + TakeItemHint(eTutorial_Hint id, Tutorial *tutorial, int items[], unsigned int itemsLength); + ~TakeItemHint(); + + virtual bool onTake( shared_ptr item ); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/Tutorial b/Minecraft.Client/Common/Tutorial/Tutorial new file mode 100644 index 00000000..db585813 Binary files /dev/null and b/Minecraft.Client/Common/Tutorial/Tutorial differ diff --git a/Minecraft.Client/Common/Tutorial/Tutorial.cpp b/Minecraft.Client/Common/Tutorial/Tutorial.cpp new file mode 100644 index 00000000..b0a0d665 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/Tutorial.cpp @@ -0,0 +1,2160 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\LocalPlayer.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\MinecraftServer.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\MultiPlayerLevel.h" +#include "..\..\SurvivalMode.h" +#include "Tutorial.h" +#include "TutorialMessage.h" +#include "TutorialTasks.h" +#include "TutorialConstraints.h" +#include "TutorialHints.h" + +vector Tutorial::s_completableTasks; + + +int Tutorial::m_iTutorialHintDelayTime = 14000; +int Tutorial::m_iTutorialDisplayMessageTime = 7000; +int Tutorial::m_iTutorialMinimumDisplayMessageTime = 2000; +int Tutorial::m_iTutorialExtraReminderTime = 13000; +int Tutorial::m_iTutorialReminderTime = m_iTutorialDisplayMessageTime + m_iTutorialExtraReminderTime; +int Tutorial::m_iTutorialConstraintDelayRemoveTicks = 15; +int Tutorial::m_iTutorialFreezeTimeValue = 8000; + +bool Tutorial::PopupMessageDetails::isSameContent(PopupMessageDetails *other) +{ + if(other == NULL) return false; + + bool textTheSame = (m_messageId == other->m_messageId) && (m_messageString.compare(other->m_messageString) == 0); + bool titleTheSame = (m_titleId == other->m_titleId) && (m_titleString.compare(other->m_titleString) == 0); + bool promptTheSame = (m_promptId == other->m_promptId) && (m_promptString.compare(other->m_promptString) == 0); + return textTheSame && titleTheSame && promptTheSame; +} + +void Tutorial::staticCtor() +{ + // + /* + ***** + ***** + THE ORDERING OF THESE SHOULD NOT CHANGE - Although the ordering may not be totally logical due to the order tasks were added, these map + to bits in the profile data in this order. New tasks/hints should be added at the end. + ***** + ***** + */ + s_completableTasks.push_back( e_Tutorial_State_Inventory_Menu ); + s_completableTasks.push_back( e_Tutorial_State_2x2Crafting_Menu ); + s_completableTasks.push_back( e_Tutorial_State_3x3Crafting_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Furnace_Menu ); + + s_completableTasks.push_back( e_Tutorial_State_Riding_Minecart ); + s_completableTasks.push_back( e_Tutorial_State_Riding_Boat ); + s_completableTasks.push_back( e_Tutorial_State_Fishing ); + s_completableTasks.push_back( e_Tutorial_State_Bed ); + + s_completableTasks.push_back( e_Tutorial_State_Container_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Trap_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Redstone_And_Piston ); + s_completableTasks.push_back( e_Tutorial_State_Portal ); + s_completableTasks.push_back( e_Tutorial_State_Creative_Inventory_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Food_Bar ); + s_completableTasks.push_back( e_Tutorial_State_CreativeMode ); + s_completableTasks.push_back( e_Tutorial_State_Brewing ); + s_completableTasks.push_back( e_Tutorial_State_Brewing_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Enchanting ); + + s_completableTasks.push_back( e_Tutorial_Hint_Hold_To_Mine ); + s_completableTasks.push_back( e_Tutorial_Hint_Tool_Damaged ); + s_completableTasks.push_back( e_Tutorial_Hint_Swim_Up ); + + s_completableTasks.push_back( e_Tutorial_Hint_Unused_2 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_3 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_4 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_5 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_6 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_7 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_8 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_9 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_10 ); + + s_completableTasks.push_back( e_Tutorial_Hint_Rock ); + s_completableTasks.push_back( e_Tutorial_Hint_Stone ); + s_completableTasks.push_back( e_Tutorial_Hint_Planks ); + s_completableTasks.push_back( e_Tutorial_Hint_Sapling ); + s_completableTasks.push_back( e_Tutorial_Hint_Unbreakable ); + s_completableTasks.push_back( e_Tutorial_Hint_Water ); + s_completableTasks.push_back( e_Tutorial_Hint_Lava ); + s_completableTasks.push_back( e_Tutorial_Hint_Sand ); + s_completableTasks.push_back( e_Tutorial_Hint_Gravel ); + s_completableTasks.push_back( e_Tutorial_Hint_Gold_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Iron_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Coal_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Tree_Trunk ); + s_completableTasks.push_back( e_Tutorial_Hint_Glass ); + s_completableTasks.push_back( e_Tutorial_Hint_Leaves ); + s_completableTasks.push_back( e_Tutorial_Hint_Lapis_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Lapis_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Dispenser ); + s_completableTasks.push_back( e_Tutorial_Hint_Sandstone ); + s_completableTasks.push_back( e_Tutorial_Hint_Note_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Powered_Rail ); + s_completableTasks.push_back( e_Tutorial_Hint_Detector_Rail ); + s_completableTasks.push_back( e_Tutorial_Hint_Tall_Grass ); + s_completableTasks.push_back( e_Tutorial_Hint_Wool ); + s_completableTasks.push_back( e_Tutorial_Hint_Flower ); + s_completableTasks.push_back( e_Tutorial_Hint_Mushroom ); + s_completableTasks.push_back( e_Tutorial_Hint_Gold_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Iron_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Stone_Slab ); + s_completableTasks.push_back( e_Tutorial_Hint_Red_Brick ); + s_completableTasks.push_back( e_Tutorial_Hint_Tnt ); + s_completableTasks.push_back( e_Tutorial_Hint_Bookshelf ); + s_completableTasks.push_back( e_Tutorial_Hint_Moss_Stone ); + s_completableTasks.push_back( e_Tutorial_Hint_Obsidian ); + s_completableTasks.push_back( e_Tutorial_Hint_Torch ); + s_completableTasks.push_back( e_Tutorial_Hint_MobSpawner ); + s_completableTasks.push_back( e_Tutorial_Hint_Chest ); + s_completableTasks.push_back( e_Tutorial_Hint_Redstone ); + s_completableTasks.push_back( e_Tutorial_Hint_Diamond_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Diamond_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Crafting_Table ); + s_completableTasks.push_back( e_Tutorial_Hint_Crops ); + s_completableTasks.push_back( e_Tutorial_Hint_Farmland ); + s_completableTasks.push_back( e_Tutorial_Hint_Furnace ); + s_completableTasks.push_back( e_Tutorial_Hint_Sign ); + s_completableTasks.push_back( e_Tutorial_Hint_Door_Wood ); + s_completableTasks.push_back( e_Tutorial_Hint_Ladder ); + s_completableTasks.push_back( e_Tutorial_Hint_Rail ); + s_completableTasks.push_back( e_Tutorial_Hint_Stairs_Stone ); + s_completableTasks.push_back( e_Tutorial_Hint_Lever ); + s_completableTasks.push_back( e_Tutorial_Hint_PressurePlate ); + s_completableTasks.push_back( e_Tutorial_Hint_Door_Iron ); + s_completableTasks.push_back( e_Tutorial_Hint_Redstone_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Redstone_Torch ); + s_completableTasks.push_back( e_Tutorial_Hint_Button ); + s_completableTasks.push_back( e_Tutorial_Hint_Snow ); + s_completableTasks.push_back( e_Tutorial_Hint_Ice ); + s_completableTasks.push_back( e_Tutorial_Hint_Cactus ); + s_completableTasks.push_back( e_Tutorial_Hint_Clay ); + s_completableTasks.push_back( e_Tutorial_Hint_Sugarcane ); + s_completableTasks.push_back( e_Tutorial_Hint_Record_Player ); + s_completableTasks.push_back( e_Tutorial_Hint_Pumpkin ); + s_completableTasks.push_back( e_Tutorial_Hint_Hell_Rock ); + s_completableTasks.push_back( e_Tutorial_Hint_Hell_Sand ); + s_completableTasks.push_back( e_Tutorial_Hint_Glowstone ); + s_completableTasks.push_back( e_Tutorial_Hint_Portal ); + s_completableTasks.push_back( e_Tutorial_Hint_Pumpkin_Lit ); + s_completableTasks.push_back( e_Tutorial_Hint_Cake ); + s_completableTasks.push_back( e_Tutorial_Hint_Redstone_Repeater ); + s_completableTasks.push_back( e_Tutorial_Hint_Trapdoor ); + s_completableTasks.push_back( e_Tutorial_Hint_Piston ); + s_completableTasks.push_back( e_Tutorial_Hint_Sticky_Piston ); + s_completableTasks.push_back( e_Tutorial_Hint_Monster_Stone_Egg ); + s_completableTasks.push_back( e_Tutorial_Hint_Stone_Brick_Smooth ); + s_completableTasks.push_back( e_Tutorial_Hint_Huge_Mushroom ); + s_completableTasks.push_back( e_Tutorial_Hint_Iron_Fence ); + s_completableTasks.push_back( e_Tutorial_Hint_Thin_Glass ); + s_completableTasks.push_back( e_Tutorial_Hint_Melon ); + s_completableTasks.push_back( e_Tutorial_Hint_Vine ); + s_completableTasks.push_back( e_Tutorial_Hint_Fence_Gate ); + s_completableTasks.push_back( e_Tutorial_Hint_Mycel ); + s_completableTasks.push_back( e_Tutorial_Hint_Water_Lily ); + s_completableTasks.push_back( e_Tutorial_Hint_Nether_Brick ); + s_completableTasks.push_back( e_Tutorial_Hint_Nether_Fence ); + s_completableTasks.push_back( e_Tutorial_Hint_Nether_Stalk ); + s_completableTasks.push_back( e_Tutorial_Hint_Enchant_Table ); + s_completableTasks.push_back( e_Tutorial_Hint_Brewing_Stand ); + s_completableTasks.push_back( e_Tutorial_Hint_Cauldron ); + s_completableTasks.push_back( e_Tutorial_Hint_End_Portal ); + s_completableTasks.push_back( e_Tutorial_Hint_End_Portal_Frame ); + + s_completableTasks.push_back( e_Tutorial_Hint_Squid ); + s_completableTasks.push_back( e_Tutorial_Hint_Cow ); + s_completableTasks.push_back( e_Tutorial_Hint_Sheep ); + s_completableTasks.push_back( e_Tutorial_Hint_Chicken ); + s_completableTasks.push_back( e_Tutorial_Hint_Pig ); + s_completableTasks.push_back( e_Tutorial_Hint_Wolf ); + s_completableTasks.push_back( e_Tutorial_Hint_Creeper ); + s_completableTasks.push_back( e_Tutorial_Hint_Skeleton ); + s_completableTasks.push_back( e_Tutorial_Hint_Spider ); + s_completableTasks.push_back( e_Tutorial_Hint_Zombie ); + s_completableTasks.push_back( e_Tutorial_Hint_Pig_Zombie ); + s_completableTasks.push_back( e_Tutorial_Hint_Ghast ); + s_completableTasks.push_back( e_Tutorial_Hint_Slime ); + s_completableTasks.push_back( e_Tutorial_Hint_Enderman ); + s_completableTasks.push_back( e_Tutorial_Hint_Silverfish ); + s_completableTasks.push_back( e_Tutorial_Hint_Cave_Spider ); + s_completableTasks.push_back( e_Tutorial_Hint_MushroomCow ); + s_completableTasks.push_back( e_Tutorial_Hint_SnowMan ); + s_completableTasks.push_back( e_Tutorial_Hint_IronGolem ); + s_completableTasks.push_back( e_Tutorial_Hint_EnderDragon ); + s_completableTasks.push_back( e_Tutorial_Hint_Blaze ); + s_completableTasks.push_back( e_Tutorial_Hint_Lava_Slime ); + + s_completableTasks.push_back( e_Tutorial_Hint_Ozelot ); + s_completableTasks.push_back( e_Tutorial_Hint_Villager ); + + s_completableTasks.push_back( e_Tutorial_Hint_Item_Shovel ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Hatchet ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Pickaxe ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Flint_And_Steel ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Apple ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bow ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Arrow ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Coal ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Diamond ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Iron_Ingot ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Gold_Ingot ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Sword ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Stick ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bowl ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Mushroom_Stew ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_String ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Feather ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Sulphur ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Hoe ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Seeds ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Wheat ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bread ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Helmet ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Chestplate ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Leggings ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Boots ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Flint ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Porkchop_Raw ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Porkchop_Cooked ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Painting ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Apple_Gold ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Sign ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Door_Wood ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bucket_Empty ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bucket_Water ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bucket_Lava ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Minecart ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Saddle ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Door_Iron ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Redstone ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Snowball ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Boat ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Leather ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Milk ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Brick ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Clay ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Reeds ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Paper ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Book ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Slimeball ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Minecart_Chest ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Minecart_Furnace ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Egg ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Compass ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Clock ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Yellow_Dust ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Fish_Raw ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Fish_Cooked ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Dye_Powder ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bone ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Sugar ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Cake ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Diode ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Cookie ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Map ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Record ); + + s_completableTasks.push_back( e_Tutorial_Hint_White_Stone ); + s_completableTasks.push_back( e_Tutorial_Hint_Dragon_Egg ); + s_completableTasks.push_back( e_Tutorial_Hint_RedstoneLamp ); + s_completableTasks.push_back( e_Tutorial_Hint_Cocoa); + + s_completableTasks.push_back( e_Tutorial_Hint_EmeraldOre ); + s_completableTasks.push_back( e_Tutorial_Hint_EmeraldBlock ); + s_completableTasks.push_back( e_Tutorial_Hint_EnderChest ); + s_completableTasks.push_back( e_Tutorial_Hint_TripwireSource ); + s_completableTasks.push_back( e_Tutorial_Hint_Tripwire ); + s_completableTasks.push_back( e_Tutorial_Hint_CobblestoneWall ); + s_completableTasks.push_back( e_Tutorial_Hint_Flowerpot ); + s_completableTasks.push_back( e_Tutorial_Hint_Anvil ); + s_completableTasks.push_back( e_Tutorial_Hint_QuartzOre ); + s_completableTasks.push_back( e_Tutorial_Hint_QuartzBlock ); + s_completableTasks.push_back( e_Tutorial_Hint_WoolCarpet ); + + s_completableTasks.push_back( e_Tutorial_Hint_Potato ); + s_completableTasks.push_back( e_Tutorial_Hint_Carrot ); + + s_completableTasks.push_back( e_Tutorial_Hint_Item_Unused_18 ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Unused_19 ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Unused_20 ); + + s_completableTasks.push_back( eTutorial_Telemetry_TrialStart ); + s_completableTasks.push_back( eTutorial_Telemetry_Halfway ); + s_completableTasks.push_back( eTutorial_Telemetry_Complete ); + + s_completableTasks.push_back( eTutorial_Telemetry_Unused_1 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_2 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_3 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_4 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_5 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_6 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_7 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_8 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_9 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_10 ); + + s_completableTasks.push_back( e_Tutorial_State_Enchanting_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Farming ); + s_completableTasks.push_back( e_Tutorial_State_Breeding ); + s_completableTasks.push_back( e_Tutorial_State_Golem ); + s_completableTasks.push_back( e_Tutorial_State_Trading ); + s_completableTasks.push_back( e_Tutorial_State_Trading_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Anvil ); + s_completableTasks.push_back( e_Tutorial_State_Anvil_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Enderchests ); + + s_completableTasks.push_back( e_Tutorial_State_Unused_9 ); + s_completableTasks.push_back( e_Tutorial_State_Unused_10 ); + + if( s_completableTasks.size() > TUTORIAL_PROFILE_STORAGE_BITS ) + { + app.DebugPrintf("Warning: Too many tutorial completable tasks added, not enough bits allocated to stored them in the profile data"); + assert(false); + } +} + +Tutorial::Tutorial(int iPad, bool isFullTutorial /*= false*/) : m_iPad( iPad ) +{ + m_isFullTutorial = isFullTutorial; + m_fullTutorialComplete = false; + m_allTutorialsComplete = false; + hasRequestedUI = false; + uiTempDisabled = false; + m_hintDisplayed = false; + m_freezeTime = false; + m_timeFrozen = false; + m_UIScene = NULL; + m_allowShow = true; + m_bHasTickedOnce = false; + m_firstTickTime = 0; + + m_lastMessage = NULL; + + lastMessageTime = 0; + m_iTaskReminders = 0; + m_lastMessageState = e_Tutorial_State_Gameplay; + + m_CurrentState = e_Tutorial_State_Gameplay; + m_hasStateChanged = false; +#ifdef _XBOX + m_hTutorialScene=NULL; +#endif + + for(unsigned int i = 0; i < e_Tutorial_State_Max; ++i) + { + currentTask[i] = NULL; + currentFailedConstraint[i] = NULL; + } + + // DEFAULT TASKS THAT ALL TUTORIALS SHARE + /* + * + * + * GAMEPLAY + * + */ + + if(!isHintCompleted(e_Tutorial_Hint_Hold_To_Mine)) addHint(e_Tutorial_State_Gameplay, new TutorialHint(e_Tutorial_Hint_Hold_To_Mine, this, IDS_TUTORIAL_HINT_HOLD_TO_MINE, TutorialHint::e_Hint_HoldToMine) ); + if(!isHintCompleted(e_Tutorial_Hint_Tool_Damaged)) addHint(e_Tutorial_State_Gameplay, new TutorialHint(e_Tutorial_Hint_Tool_Damaged, this, IDS_TUTORIAL_HINT_TOOL_DAMAGED, TutorialHint::e_Hint_ToolDamaged) ); + if(!isHintCompleted(e_Tutorial_Hint_Swim_Up)) addHint(e_Tutorial_State_Gameplay, new TutorialHint(e_Tutorial_Hint_Swim_Up, this, IDS_TUTORIAL_HINT_SWIM_UP, TutorialHint::e_Hint_SwimUp) ); + + /* + * TILE HINTS + */ + int rockItems[] = {Tile::rock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Rock)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Rock, this, rockItems, 1 ) ); + + int stoneItems[] = {Tile::stoneBrick_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone, this, stoneItems, 1 ) ); + + int plankItems[] = {Tile::wood_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Planks)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Planks, this, plankItems, 1 ) ); + + int saplingItems[] = {Tile::sapling_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sapling)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sapling, this, saplingItems, 1 ) ); + + int unbreakableItems[] = {Tile::unbreakable_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Unbreakable)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Unbreakable, this, unbreakableItems, 1 ) ); + + int waterItems[] = {Tile::water_Id, Tile::calmWater_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Water)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Water, this, waterItems, 2 ) ); + + int lavaItems[] = {Tile::lava_Id, Tile::calmLava_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Lava)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Lava, this, lavaItems, 2 ) ); + + int sandItems[] = {Tile::sand_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sand)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sand, this, sandItems, 1 ) ); + + int gravelItems[] = {Tile::gravel_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Gravel)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Gravel, this, gravelItems, 1 ) ); + + int goldOreItems[] = {Tile::goldOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Gold_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Gold_Ore, this, goldOreItems, 1 ) ); + + int ironOreItems[] = {Tile::ironOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Iron_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Iron_Ore, this, ironOreItems, 1 ) ); + + int coalOreItems[] = {Tile::coalOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Coal_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Coal_Ore, this, coalOreItems, 1 ) ); + + int treeTrunkItems[] = {Tile::treeTrunk_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Tree_Trunk)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tree_Trunk, this, treeTrunkItems, 1 ) ); + + int leavesItems[] = {Tile::leaves_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Leaves)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Leaves, this, leavesItems, 1 ) ); + + int glassItems[] = {Tile::glass_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Glass)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Glass, this, glassItems, 1 ) ); + + int lapisOreItems[] = {Tile::lapisOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Lapis_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Lapis_Ore, this, lapisOreItems, 1 ) ); + + int lapisBlockItems[] = {Tile::lapisBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Lapis_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Lapis_Block, this, lapisBlockItems, 1 ) ); + + int dispenserItems[] = {Tile::dispenser_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Dispenser)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Dispenser, this, dispenserItems, 1 ) ); + + int sandstoneItems[] = {Tile::sandStone_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sandstone)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sandstone, this, sandstoneItems, 1, -1, SandStoneTile::TYPE_DEFAULT ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sandstone, this, sandstoneItems, 1, -1, SandStoneTile::TYPE_HEIROGLYPHS ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sandstone, this, sandstoneItems, 1, -1, SandStoneTile::TYPE_SMOOTHSIDE ) ); + } + + int noteBlockItems[] = {Tile::musicBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Note_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Note_Block, this, noteBlockItems, 1 ) ); + + int poweredRailItems[] = {Tile::goldenRail_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Powered_Rail)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Powered_Rail, this, poweredRailItems, 1 ) ); + + int detectorRailItems[] = {Tile::detectorRail_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Detector_Rail)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Detector_Rail, this, detectorRailItems, 1 ) ); + + int tallGrassItems[] = {Tile::tallgrass_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Tall_Grass)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tall_Grass, this, tallGrassItems, 1, -1, TallGrass::DEAD_SHRUB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tall_Grass, this, tallGrassItems, 1, -1, TallGrass::TALL_GRASS ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tall_Grass, this, tallGrassItems, 1, -1, TallGrass::FERN ) ); + } + + int woolItems[] = {Tile::cloth_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Wool)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Wool, this, woolItems, 1 ) ); + + int flowerItems[] = {Tile::flower_Id, Tile::rose_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Flower)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Flower, this, flowerItems, 2 ) ); + + int mushroomItems[] = {Tile::mushroom1_Id, Tile::mushroom2_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Mushroom)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Mushroom, this, mushroomItems, 2 ) ); + + int goldBlockItems[] = {Tile::goldBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Gold_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Gold_Block, this, goldBlockItems, 1 ) ); + + int ironBlockItems[] = {Tile::ironBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Iron_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Iron_Block, this, ironBlockItems, 1 ) ); + + int stoneSlabItems[] = {Tile::stoneSlabHalf_Id, Tile::stoneSlab_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stone_Slab)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::STONE_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::SAND_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::WOOD_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::COBBLESTONE_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::BRICK_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::SMOOTHBRICK_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::NETHERBRICK_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::QUARTZ_SLAB ) ); + } + + int woodSlabItems[] = {Tile::woodSlabHalf_Id, Tile::woodSlab_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stone_Slab)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, woodSlabItems, 2, -1, TreeTile::BIRCH_TRUNK ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, woodSlabItems, 2, -1, TreeTile::DARK_TRUNK ) ); + } + + int redBrickItems[] = {Tile::redBrick_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Red_Brick)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Red_Brick, this, redBrickItems, 1 ) ); + + int tntItems[] = {Tile::tnt_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Tnt)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tnt, this, tntItems, 1 ) ); + + int bookshelfItems[] = {Tile::bookshelf_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Bookshelf)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Bookshelf, this, bookshelfItems, 1 ) ); + + int mossStoneItems[] = {Tile::mossStone_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Moss_Stone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Moss_Stone, this, mossStoneItems, 1 ) ); + + int obsidianItems[] = {Tile::obsidian_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Obsidian)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Obsidian, this, obsidianItems, 1 ) ); + + int torchItems[] = {Tile::torch_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Torch)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Torch, this, torchItems, 1 ) ); + + int mobSpawnerItems[] = {Tile::mobSpawner_Id}; + if(!isHintCompleted(e_Tutorial_Hint_MobSpawner)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_MobSpawner, this, mobSpawnerItems, 1 ) ); + + int chestItems[] = {Tile::chest_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Chest)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Chest, this, chestItems, 1 ) ); + + int redstoneItems[] = {Tile::redStoneDust_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Redstone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Redstone, this, redstoneItems, 1, Item::redStone_Id ) ); + + int diamondOreItems[] = {Tile::diamondOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Diamond_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Diamond_Ore, this, diamondOreItems, 1 ) ); + + int diamondBlockItems[] = {Tile::diamondBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Diamond_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Diamond_Block, this, diamondBlockItems, 1 ) ); + + int craftingTableItems[] = {Tile::workBench_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Crafting_Table)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Crafting_Table, this, craftingTableItems, 1 ) ); + + int cropsItems[] = {Tile::crops_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Crops)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Crops, this, cropsItems, 1, -1, -1, 7 ) ); + + int farmlandItems[] = {Tile::farmland_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Farmland)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Farmland, this, farmlandItems, 1 ) ); + + int furnaceItems[] = {Tile::furnace_Id, Tile::furnace_lit_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Furnace)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Furnace, this, furnaceItems, 2 ) ); + + int signItems[] = {Tile::sign_Id, Tile::wallSign_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sign)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sign, this, signItems, 2, Item::sign_Id ) ); + + int doorWoodItems[] = {Tile::door_wood_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Door_Wood)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Door_Wood, this, doorWoodItems, 1, Item::door_wood->id ) ); + + int ladderItems[] = {Tile::ladder_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Ladder)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Ladder, this, ladderItems, 1 ) ); + + int stairsStoneItems[] = {Tile::stairs_stone_Id,Tile::stairs_bricks_Id,Tile::stairs_stoneBrickSmooth_Id,Tile::stairs_wood_Id,Tile::stairs_sprucewood_Id,Tile::stairs_birchwood_Id,Tile::stairs_netherBricks_Id,Tile::stairs_sandstone_Id,Tile::stairs_quartz_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stairs_Stone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stairs_Stone, this, stairsStoneItems, 9 ) ); + + int railItems[] = {Tile::rail_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Rail)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Rail, this, railItems, 1 ) ); + + int leverItems[] = {Tile::lever_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Lever)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Lever, this, leverItems, 1 ) ); + + int pressurePlateItems[] = {Tile::pressurePlate_stone_Id, Tile::pressurePlate_wood_Id}; + if(!isHintCompleted(e_Tutorial_Hint_PressurePlate)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_PressurePlate, this, pressurePlateItems, 2 ) ); + + int doorIronItems[] = {Tile::door_iron_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Door_Iron)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Door_Iron, this, doorIronItems, 1, Item::door_iron->id ) ); + + int redstoneOreItems[] = {Tile::redStoneOre_Id, Tile::redStoneOre_lit_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Redstone_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Redstone_Ore, this, redstoneOreItems, 2 ) ); + + int redstoneTorchItems[] = {Tile::notGate_off_Id, Tile::notGate_on_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Redstone_Torch)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Redstone_Torch, this, redstoneTorchItems, 2 ) ); + + int buttonItems[] = {Tile::button_stone_Id, Tile::button_wood_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Button)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Button, this, buttonItems, 2 ) ); + + int snowItems[] = {Tile::snow_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Snow)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Snow, this, snowItems, 1 ) ); + + int iceItems[] = {Tile::ice_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Ice)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Ice, this, iceItems, 1 ) ); + + int cactusItems[] = {Tile::cactus_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Cactus)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Cactus, this, cactusItems, 1 ) ); + + int clayItems[] = {Tile::clay_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Clay)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Clay, this, clayItems, 1 ) ); + + int sugarCaneItems[] = {Tile::reeds_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sugarcane)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sugarcane, this, sugarCaneItems, 1 ) ); + + int recordPlayerItems[] = {Tile::recordPlayer_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Record_Player)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Record_Player, this, recordPlayerItems, 1 ) ); + + int pumpkinItems[] = {Tile::pumpkin_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Pumpkin)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Pumpkin, this, pumpkinItems, 1, -1, -1, 0 ) ); + + int hellRockItems[] = {Tile::hellRock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Hell_Rock)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Hell_Rock, this, hellRockItems, 1 ) ); + + int hellSandItems[] = {Tile::hellSand_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Hell_Sand)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Hell_Sand, this, hellSandItems, 1 ) ); + + int glowstoneItems[] = {Tile::lightGem_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Glowstone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Glowstone, this, glowstoneItems, 1 ) ); + + int portalItems[] = {Tile::portalTile_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Portal)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Portal, this, portalItems, 1 ) ); + + int pumpkinLitItems[] = {Tile::litPumpkin_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Pumpkin_Lit)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Pumpkin_Lit, this, pumpkinLitItems, 1, -1, -1, 0 ) ); + + int cakeItems[] = {Tile::cake_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Cake)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Cake, this, cakeItems, 1 ) ); + + int redstoneRepeaterItems[] = {Tile::diode_on_Id, Tile::diode_off_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Redstone_Repeater)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Redstone_Repeater, this, redstoneRepeaterItems, 2, Item::diode_Id ) ); + + int trapdoorItems[] = {Tile::trapdoor_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Trapdoor)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Trapdoor, this, trapdoorItems, 1 ) ); + + int pistonItems[] = {Tile::pistonBase_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Piston)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Piston, this, pistonItems, 1 ) ); + + int stickyPistonItems[] = {Tile::pistonStickyBase_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sticky_Piston)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sticky_Piston, this, stickyPistonItems, 1 ) ); + + int monsterStoneEggItems[] = {Tile::monsterStoneEgg_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Monster_Stone_Egg)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Monster_Stone_Egg, this, monsterStoneEggItems, 1 ) ); + + int stoneBrickSmoothItems[] = {Tile::stoneBrickSmooth_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stone_Brick_Smooth)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Brick_Smooth, this, stoneBrickSmoothItems, 1 ) ); + + int hugeMushroomItems[] = {Tile::hugeMushroom1_Id,Tile::hugeMushroom2_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Huge_Mushroom)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Huge_Mushroom, this, hugeMushroomItems, 2 ) ); + + int ironFenceItems[] = {Tile::ironFence_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Iron_Fence)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Iron_Fence, this, ironFenceItems, 1 ) ); + + int thisGlassItems[] = {Tile::thinGlass_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Thin_Glass)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Thin_Glass, this, thisGlassItems, 1 ) ); + + int melonItems[] = {Tile::melon_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Melon)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Melon, this, melonItems, 1 ) ); + + int vineItems[] = {Tile::vine_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Vine)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Vine, this, vineItems, 1 ) ); + + int fenceGateItems[] = {Tile::fenceGate_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Fence_Gate)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Fence_Gate, this, fenceGateItems, 1 ) ); + + int mycelItems[] = {Tile::mycel_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Mycel)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Mycel, this, mycelItems, 1 ) ); + + int waterLilyItems[] = {Tile::waterLily_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Water_Lily)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Water_Lily, this, waterLilyItems, 1 ) ); + + int netherBrickItems[] = {Tile::netherBrick_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Nether_Brick)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Nether_Brick, this, netherBrickItems, 1 ) ); + + int netherFenceItems[] = {Tile::netherFence_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Nether_Fence)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Nether_Fence, this, netherFenceItems, 1 ) ); + + int netherStalkItems[] = {Tile::netherStalk_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Nether_Stalk)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Nether_Stalk, this, netherStalkItems, 1 ) ); + + int enchantTableItems[] = {Tile::enchantTable_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Enchant_Table)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Enchant_Table, this, enchantTableItems, 1 ) ); + + int brewingStandItems[] = {Tile::brewingStand_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Brewing_Stand)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Brewing_Stand, this, brewingStandItems, 1, Item::brewingStand_Id ) ); + + int cauldronItems[] = {Tile::cauldron_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Cauldron)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Cauldron, this, cauldronItems, 1, Item::cauldron_Id ) ); + + int endPortalItems[] = {Tile::endPortalTile_Id}; + if(!isHintCompleted(e_Tutorial_Hint_End_Portal)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_End_Portal, this, endPortalItems, 1, -2 ) ); + + int endPortalFrameItems[] = {Tile::endPortalFrameTile_Id}; + if(!isHintCompleted(e_Tutorial_Hint_End_Portal_Frame)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_End_Portal_Frame, this, endPortalFrameItems, 1 ) ); + + int whiteStoneItems[] = {Tile::whiteStone_Id}; + if(!isHintCompleted(e_Tutorial_Hint_White_Stone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_White_Stone, this, whiteStoneItems, 1 ) ); + + int dragonEggItems[] = {Tile::dragonEgg_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Dragon_Egg)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Dragon_Egg, this, dragonEggItems, 1 ) ); + + int redstoneLampItems[] = {Tile::redstoneLight_Id, Tile::redstoneLight_lit_Id}; + if(!isHintCompleted(e_Tutorial_Hint_RedstoneLamp)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_RedstoneLamp, this, redstoneLampItems, 2 ) ); + + int cocoaItems[] = {Tile::cocoa_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Cocoa)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Cocoa, this, cocoaItems, 1 ) ); + + int emeraldOreItems[] = {Tile::emeraldOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_EmeraldOre)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_EmeraldOre, this, emeraldOreItems, 1 ) ); + + int emeraldBlockItems[] = {Tile::emeraldBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_EmeraldBlock)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_EmeraldBlock, this, emeraldBlockItems, 1 ) ); + + int enderChestItems[] = {Tile::enderChest_Id}; + if(!isHintCompleted(e_Tutorial_Hint_EnderChest)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_EnderChest, this, enderChestItems, 1 ) ); + + int tripwireSourceItems[] = {Tile::tripWireSource_Id}; + if(!isHintCompleted(e_Tutorial_Hint_TripwireSource)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_TripwireSource, this, tripwireSourceItems, 1 ) ); + + int tripwireItems[] = {Tile::tripWire_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Tripwire)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tripwire, this, tripwireItems, 1, Item::string_Id ) ); + + int cobblestoneWallItems[] = {Tile::cobbleWall_Id}; + if(!isHintCompleted(e_Tutorial_Hint_CobblestoneWall)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_CobblestoneWall, this, cobblestoneWallItems, 1, -1, WallTile::TYPE_NORMAL ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_CobblestoneWall, this, cobblestoneWallItems, 1, -1, WallTile::TYPE_MOSSY ) ); + } + + int flowerpotItems[] = {Tile::flowerPot_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Flowerpot)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Flowerpot, this, flowerpotItems, 1, Item::flowerPot_Id ) ); + + int anvilItems[] = {Tile::anvil_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Anvil)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Anvil, this, anvilItems, 1 ) ); + + int quartzOreItems[] = {Tile::netherQuartz_Id}; + if(!isHintCompleted(e_Tutorial_Hint_QuartzOre)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzOre, this, quartzOreItems, 1 ) ); + + int quartzBlockItems[] = {Tile::quartzBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_QuartzBlock)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_DEFAULT ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_CHISELED ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_LINES_Y ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_LINES_X ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_LINES_Z ) ); + } + + int carpetItems[] = {Tile::woolCarpet_Id}; + if(!isHintCompleted(e_Tutorial_Hint_WoolCarpet)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_WoolCarpet, this, carpetItems, 1 ) ); + + int potatoItems[] = {Tile::potatoes_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Potato)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Potato, this, potatoItems, 1, -1, -1, 7 ) ); + + int carrotItems[] = {Tile::carrots_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Carrot)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Carrot, this, carrotItems, 1, -1, -1, 7 ) ); + + /* + * ENTITY HINTS + */ + if(!isHintCompleted(e_Tutorial_Hint_Squid)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Squid, this, IDS_DESC_SQUID, IDS_SQUID, eTYPE_SQUID ) ); + if(!isHintCompleted(e_Tutorial_Hint_Cow)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Cow, this, IDS_DESC_COW, IDS_COW, eTYPE_COW ) ); + if(!isHintCompleted(e_Tutorial_Hint_Sheep)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Sheep, this, IDS_DESC_SHEEP, IDS_SHEEP, eTYPE_SHEEP ) ); + if(!isHintCompleted(e_Tutorial_Hint_Chicken)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Chicken, this, IDS_DESC_CHICKEN, IDS_CHICKEN, eTYPE_CHICKEN ) ); + if(!isHintCompleted(e_Tutorial_Hint_Pig)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Pig, this, IDS_DESC_PIG, IDS_PIG, eTYPE_PIG ) ); + if(!isHintCompleted(e_Tutorial_Hint_Wolf)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Wolf, this, IDS_DESC_WOLF, IDS_WOLF, eTYPE_WOLF ) ); + if(!isHintCompleted(e_Tutorial_Hint_Creeper)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Creeper, this, IDS_DESC_CREEPER, IDS_CREEPER, eTYPE_CREEPER ) ); + if(!isHintCompleted(e_Tutorial_Hint_Skeleton)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Skeleton, this, IDS_DESC_SKELETON, IDS_SKELETON, eTYPE_SKELETON ) ); + if(!isHintCompleted(e_Tutorial_Hint_Spider)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Spider, this, IDS_DESC_SPIDER, IDS_SPIDER, eTYPE_SPIDER ) ); + if(!isHintCompleted(e_Tutorial_Hint_Zombie)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Zombie, this, IDS_DESC_ZOMBIE, IDS_ZOMBIE, eTYPE_ZOMBIE ) ); + if(!isHintCompleted(e_Tutorial_Hint_Pig_Zombie)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Pig_Zombie, this, IDS_DESC_PIGZOMBIE, IDS_PIGZOMBIE, eTYPE_PIGZOMBIE ) ); + if(!isHintCompleted(e_Tutorial_Hint_Ghast)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Ghast, this, IDS_DESC_GHAST, IDS_GHAST, eTYPE_GHAST ) ); + if(!isHintCompleted(e_Tutorial_Hint_Slime)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Slime, this, IDS_DESC_SLIME, IDS_SLIME, eTYPE_SLIME ) ); + if(!isHintCompleted(e_Tutorial_Hint_Enderman)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Enderman, this, IDS_DESC_ENDERMAN, IDS_ENDERMAN, eTYPE_ENDERMAN ) ); + if(!isHintCompleted(e_Tutorial_Hint_Silverfish)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Silverfish, this, IDS_DESC_SILVERFISH, IDS_SILVERFISH, eTYPE_SILVERFISH ) ); + if(!isHintCompleted(e_Tutorial_Hint_Cave_Spider)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Cave_Spider, this, IDS_DESC_CAVE_SPIDER, IDS_CAVE_SPIDER, eTYPE_CAVESPIDER ) ); + if(!isHintCompleted(e_Tutorial_Hint_MushroomCow)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_MushroomCow, this, IDS_DESC_MUSHROOM_COW, IDS_MUSHROOM_COW, eTYPE_MUSHROOMCOW) ); + if(!isHintCompleted(e_Tutorial_Hint_SnowMan)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_SnowMan, this, IDS_DESC_SNOWMAN, IDS_SNOWMAN, eTYPE_SNOWMAN ) ); + if(!isHintCompleted(e_Tutorial_Hint_IronGolem)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_IronGolem, this, IDS_DESC_IRONGOLEM, IDS_IRONGOLEM, eTYPE_VILLAGERGOLEM ) ); + if(!isHintCompleted(e_Tutorial_Hint_EnderDragon)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_EnderDragon, this, IDS_DESC_ENDERDRAGON, IDS_ENDERDRAGON, eTYPE_ENDERDRAGON ) ); + if(!isHintCompleted(e_Tutorial_Hint_Blaze)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Blaze, this, IDS_DESC_BLAZE, IDS_BLAZE, eTYPE_BLAZE ) ); + if(!isHintCompleted(e_Tutorial_Hint_Lava_Slime)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Lava_Slime, this, IDS_DESC_LAVA_SLIME, IDS_LAVA_SLIME, eTYPE_LAVASLIME ) ); + if(!isHintCompleted(e_Tutorial_Hint_Ozelot)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Ozelot, this, IDS_DESC_OZELOT, IDS_OZELOT, eTYPE_OZELOT ) ); + if(!isHintCompleted(e_Tutorial_Hint_Villager)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Villager, this, IDS_DESC_VILLAGER, IDS_VILLAGER, eTYPE_VILLAGER) ); + + + /* + * ITEM HINTS + */ + int shovelItems[] = {Item::shovel_wood->id, Item::shovel_stone->id, Item::shovel_iron->id, Item::shovel_gold->id, Item::shovel_diamond->id}; + if(!isHintCompleted(e_Tutorial_Hint_Item_Shovel)) addHint(e_Tutorial_State_Gameplay, new DiggerItemHint(e_Tutorial_Hint_Item_Shovel, this, IDS_TUTORIAL_HINT_DIGGER_ITEM_SHOVEL, shovelItems, 5) ); + + int hatchetItems[] = {Item::hatchet_wood->id, Item::hatchet_stone->id, Item::hatchet_iron->id, Item::hatchet_gold->id, Item::hatchet_diamond->id}; + if(!isHintCompleted(e_Tutorial_Hint_Item_Hatchet)) addHint(e_Tutorial_State_Gameplay, new DiggerItemHint(e_Tutorial_Hint_Item_Hatchet, this, IDS_TUTORIAL_HINT_DIGGER_ITEM_HATCHET, hatchetItems, 5 ) ); + + int pickaxeItems[] = {Item::pickAxe_wood->id, Item::pickAxe_stone->id, Item::pickAxe_iron->id, Item::pickAxe_gold->id, Item::pickAxe_diamond->id}; + if(!isHintCompleted(e_Tutorial_Hint_Item_Pickaxe)) addHint(e_Tutorial_State_Gameplay, new DiggerItemHint(e_Tutorial_Hint_Item_Pickaxe, this, IDS_TUTORIAL_HINT_DIGGER_ITEM_PICKAXE, pickaxeItems, 5 ) ); + + /* + * + * + * INVENTORY + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Inventory_Menu) ) + { + ProcedureCompoundTask *inventoryOverviewTask = new ProcedureCompoundTask( this ); + inventoryOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_INV_OVERVIEW, IDS_TUTORIAL_PROMPT_INV_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_Inventory) ); + inventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_INV_PICK_UP, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + inventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_INV_MOVE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + inventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_INV_DROP, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + inventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_INV_INFO, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Inventory_Menu, inventoryOverviewTask ); + } + + /* + * + * + * CREATIVE INVENTORY + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Creative_Inventory_Menu) ) + { + ProcedureCompoundTask *creativeInventoryOverviewTask = new ProcedureCompoundTask( this ); + creativeInventoryOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_OVERVIEW, IDS_TUTORIAL_PROMPT_CREATIVE_INV_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_CreativeInventory) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_PICK_UP, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_MOVE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_DROP, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_NAV, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_INFO, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Creative_Inventory_Menu, creativeInventoryOverviewTask ); + } + + /* + * + * + * CRAFTING + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_2x2Crafting_Menu ) ) + { + ProcedureCompoundTask *craftingOverviewTask = new ProcedureCompoundTask( this ); + craftingOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_CRAFT_OVERVIEW, IDS_TUTORIAL_PROMPT_CRAFT_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_Crafting) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_NAV, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_CREATE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_CRAFT_TABLE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_INVENTORY, IDS_TUTORIAL_PROMPT_PRESS_X_TO_TOGGLE_DESCRIPTION, false, ACTION_MENU_X) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_DESCRIPTION, IDS_TUTORIAL_PROMPT_PRESS_X_TO_TOGGLE_INGREDIENTS, false, ACTION_MENU_X) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_INGREDIENTS, IDS_TUTORIAL_PROMPT_PRESS_X_TO_TOGGLE_INVENTORY, false, ACTION_MENU_X) ); + addTask(e_Tutorial_State_2x2Crafting_Menu, craftingOverviewTask ); + } + // Other tasks can be added in the derived classes + + addHint(e_Tutorial_State_2x2Crafting_Menu, new TutorialHint(e_Tutorial_Hint_Always_On, this, IDS_TUTORIAL_HINT_CRAFT_NO_INGREDIENTS, TutorialHint::e_Hint_NoIngredients) ); + + addHint(e_Tutorial_State_3x3Crafting_Menu, new TutorialHint(e_Tutorial_Hint_Always_On, this, IDS_TUTORIAL_HINT_CRAFT_NO_INGREDIENTS, TutorialHint::e_Hint_NoIngredients) ); + + /* + * + * + * FURNACE + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Furnace_Menu ) ) + { + ProcedureCompoundTask *furnaceOverviewTask = new ProcedureCompoundTask( this ); + furnaceOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_FURNACE_OVERVIEW, IDS_TUTORIAL_PROMPT_FURNACE_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_Furnace) ); + furnaceOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_FURNACE_METHOD, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + furnaceOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_FURNACE_FUELS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + furnaceOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_FURNACE_INGREDIENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Furnace_Menu, furnaceOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * BREWING MENU + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Brewing_Menu ) ) + { + ProcedureCompoundTask *brewingOverviewTask = new ProcedureCompoundTask( this ); + brewingOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_OVERVIEW, IDS_TUTORIAL_PROMPT_BREWING_MENU_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_BrewingMenu) ); + brewingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_METHOD, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + brewingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_BASIC_INGREDIENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + brewingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_EXTENDED_INGREDIENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + brewingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_EXTENDED_INGREDIENTS_2, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Brewing_Menu, brewingOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * ENCHANTING MENU + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Enchanting_Menu ) ) + { + ProcedureCompoundTask *enchantingOverviewTask = new ProcedureCompoundTask( this ); + enchantingOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_OVERVIEW, IDS_TUTORIAL_PROMPT_ENCHANTING_MENU_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_EnchantingMenu) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_START, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_ENCHANTMENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_COST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_ENCHANT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_BETTER_ENCHANTMENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting_Menu, enchantingOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * ANVIL MENU + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Anvil_Menu ) ) + { + ProcedureCompoundTask *anvilOverviewTask = new ProcedureCompoundTask( this ); + anvilOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_OVERVIEW, IDS_TUTORIAL_PROMPT_ANVIL_MENU_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_AnvilMenu) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_START, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_REPAIR, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_SACRIFICE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_ENCHANT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_COST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_RENAMING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_SMITH, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil_Menu, anvilOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * TRADING MENU + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Trading_Menu ) ) + { + ProcedureCompoundTask *tradingOverviewTask = new ProcedureCompoundTask( this ); + tradingOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_OVERVIEW, IDS_TUTORIAL_PROMPT_TRADING_MENU_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_TradingMenu) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_START, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_UNAVAILABLE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_DETAILS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_INVENTORY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_TRADE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading_Menu, tradingOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * MINECART + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Riding_Minecart ) ) + { + addTask(e_Tutorial_State_Riding_Minecart, new ChoiceTask(this, IDS_TUTORIAL_TASK_MINECART_OVERVIEW, IDS_TUTORIAL_PROMPT_MINECART_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Minecart) ); + addTask(e_Tutorial_State_Riding_Minecart, new InfoTask(this, IDS_TUTORIAL_TASK_MINECART_RAILS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Riding_Minecart, new InfoTask(this, IDS_TUTORIAL_TASK_MINECART_POWERED_RAILS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Riding_Minecart, new InfoTask(this, IDS_TUTORIAL_TASK_MINECART_PUSHING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + + /* + * + * + * BOAT + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Riding_Boat ) ) + { + addTask(e_Tutorial_State_Riding_Boat, new ChoiceTask(this, IDS_TUTORIAL_TASK_BOAT_OVERVIEW, IDS_TUTORIAL_PROMPT_BOAT_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Boat) ); + addTask(e_Tutorial_State_Riding_Boat, new InfoTask(this, IDS_TUTORIAL_TASK_BOAT_STEER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + + /* + * + * + * FISHING + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Fishing ) ) + { + addTask(e_Tutorial_State_Fishing, new ChoiceTask(this, IDS_TUTORIAL_TASK_FISHING_OVERVIEW, IDS_TUTORIAL_PROMPT_FISHING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Fishing) ); + addTask(e_Tutorial_State_Fishing, new InfoTask(this, IDS_TUTORIAL_TASK_FISHING_CAST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Fishing, new InfoTask(this, IDS_TUTORIAL_TASK_FISHING_FISH, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Fishing, new InfoTask(this, IDS_TUTORIAL_TASK_FISHING_USES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + + /* + * + * + * BED + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Bed ) ) + { + addTask(e_Tutorial_State_Bed, new ChoiceTask(this, IDS_TUTORIAL_TASK_BED_OVERVIEW, IDS_TUTORIAL_PROMPT_BED_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Bed) ); + addTask(e_Tutorial_State_Bed, new InfoTask(this, IDS_TUTORIAL_TASK_BED_PLACEMENT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Bed, new InfoTask(this, IDS_TUTORIAL_TASK_BED_MULTIPLAYER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + + /* + * + * + * FOOD BAR + * + */ + if(!isFullTutorial && !isStateCompleted( e_Tutorial_State_Food_Bar ) ) + { + addTask(e_Tutorial_State_Food_Bar, new ChoiceTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_OVERVIEW, IDS_TUTORIAL_PROMPT_FOOD_BAR_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_FoodBar) ); + addTask(e_Tutorial_State_Food_Bar, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_DEPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Food_Bar, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_HEAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Food_Bar, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_FEED, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } +} + +Tutorial::~Tutorial() +{ + for(AUTO_VAR(it, m_globalConstraints.begin()); it != m_globalConstraints.end(); ++it) + { + delete (*it); + } + for(unordered_map::iterator it = messages.begin(); it != messages.end(); ++it) + { + delete (*it).second; + } + for(unsigned int i = 0; i < e_Tutorial_State_Max; ++i) + { + for(AUTO_VAR(it, activeTasks[i].begin()); it < activeTasks[i].end(); ++it) + { + delete (*it); + } + for(AUTO_VAR(it, hints[i].begin()); it < hints[i].end(); ++it) + { + delete (*it); + } + + currentTask[i] = NULL; + currentFailedConstraint[i] = NULL; + } +} + +void Tutorial::debugResetPlayerSavedProgress(int iPad) +{ +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)StorageManager.GetGameDefinedProfileData(iPad); +#else + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)ProfileManager.GetGameDefinedProfileData(iPad); +#endif + ZeroMemory( pGameSettings->ucTutorialCompletion, TUTORIAL_PROFILE_STORAGE_BYTES ); + pGameSettings->uiSpecialTutorialBitmask = 0; +} + +void Tutorial::setCompleted( int completableId ) +{ + //if(app.GetGameSettingsDebugMask(m_iPad) && app.GetGameSettingsDebugMask()&(1L<= 0 && completableIndex < TUTORIAL_PROFILE_STORAGE_BITS ) + { + // Set the bit for this position +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)StorageManager.GetGameDefinedProfileData(m_iPad); +#else + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)ProfileManager.GetGameDefinedProfileData(m_iPad); +#endif + int arrayIndex = completableIndex >> 3; + int bitIndex = 7 - (completableIndex % 8); + pGameSettings->ucTutorialCompletion[arrayIndex] |= 1<bSettingsChanged=true; + } +} + +bool Tutorial::getCompleted( int completableId ) +{ + //if(app.GetGameSettingsDebugMask(m_iPad) && app.GetGameSettingsDebugMask()&(1L<= 0 && completableIndex < TUTORIAL_PROFILE_STORAGE_BITS ) + { + // Read the bit for this position + //Retrieve the data pointer from the profile +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)StorageManager.GetGameDefinedProfileData(m_iPad); +#else + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)ProfileManager.GetGameDefinedProfileData(m_iPad); +#endif + int arrayIndex = completableIndex >> 3; + int bitIndex = 7 - (completableIndex % 8); + return (pGameSettings->ucTutorialCompletion[arrayIndex] & 1<getId(); + + if( hintId != e_Tutorial_Hint_Always_On ) + { + setHintCompleted( hint->getId() ); + hints[m_CurrentState].erase( find(hints[m_CurrentState].begin(), hints[m_CurrentState].end(), hint) ); + delete hint; + } + // else + // { + // find(hints[m_CurrentState].begin(), hints[m_CurrentState].end(), hint); + // } +} + +void Tutorial::tick() +{ + // Don't do anything for the first 2 seconds so that the loading screen is gone + if(!m_bHasTickedOnce) + { + int time = GetTickCount(); + if(m_firstTickTime == 0) + { + m_firstTickTime = time; + } + else if ( time - m_firstTickTime > 1500 ) + { + m_bHasTickedOnce = true; + } + } + if(!m_bHasTickedOnce) + { + return; + } + + bool constraintChanged = false; + bool taskChanged = false; + + for(unsigned int state = 0; state < e_Tutorial_State_Max; ++state) + { + AUTO_VAR(it, constraintsToRemove[state].begin()); + while(it < constraintsToRemove[state].end() ) + { + ++(*it).second; + if( (*it).second > m_iTutorialConstraintDelayRemoveTicks ) + { + TutorialConstraint *c = (*it).first; + constraints[state].erase( find( constraints[state].begin(), constraints[state].end(), c) ); + c->setQueuedForRemoval(false); + it = constraintsToRemove[state].erase( it ); + + if( c->getDeleteOnDeactivate() ) + { + delete c; + } + } + else + { + ++it; + } + } + } + + // 4J Stu TODO - Make this a constraint + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(m_freezeTime && !m_timeFrozen && !m_fullTutorialComplete ) + { + // Need to set the time on both levels to stop the flickering as the local level + // tries to predict the time + MinecraftServer::SetTimeOfDay(m_iTutorialFreezeTimeValue); + pMinecraft->level->setOverrideTimeOfDay(m_iTutorialFreezeTimeValue); // Always daytime + m_timeFrozen = true; + } + else if(m_freezeTime && m_timeFrozen && m_fullTutorialComplete) + { + __int64 currentTime = pMinecraft->level->getTime(); + int currentDayTime = (currentTime % Level::TICKS_PER_DAY); + int timeToAdd = 0; + if(currentDayTime > m_iTutorialFreezeTimeValue) + { + timeToAdd = (Level::TICKS_PER_DAY - currentDayTime) + m_iTutorialFreezeTimeValue; + } + else + { + timeToAdd = m_iTutorialFreezeTimeValue - currentDayTime; + } + __int64 targetTime = currentTime + timeToAdd; + MinecraftServer::SetTimeOfDay(-1); + MinecraftServer::SetTime(targetTime); + pMinecraft->level->setOverrideTimeOfDay(-1); + pMinecraft->level->setTime(targetTime); + m_timeFrozen = false; + } + + if(!m_allowShow) + { + if( currentTask[m_CurrentState] != NULL && (!currentTask[m_CurrentState]->AllowFade() || (lastMessageTime + m_iTutorialDisplayMessageTime ) > GetTickCount() ) ) + { + uiTempDisabled = true; + } + ui.SetTutorialVisible( m_iPad, false ); + return; + } + + + if(!hasRequestedUI ) + { +#ifdef _XBOX + m_bSceneIsSplitscreen=app.GetLocalPlayerCount()>1; + if(m_bSceneIsSplitscreen) + { + app.NavigateToScene(m_iPad, eUIComponent_TutorialPopup,(void *)this, false, false, &m_hTutorialScene); + } + else + { + app.NavigateToScene(m_iPad, eUIComponent_TutorialPopup,(void *)this, false, false, &m_hTutorialScene); + } +#else + ui.SetTutorial(m_iPad, this); +#endif + hasRequestedUI = true; + } + else + { + // if we've changed mode, we may need to change scene + if(m_bSceneIsSplitscreen!=(app.GetLocalPlayerCount()>1)) + { +#ifdef _XBOX + app.TutorialSceneNavigateBack(m_iPad); + m_bSceneIsSplitscreen=app.GetLocalPlayerCount()>1; + if(m_bSceneIsSplitscreen) + { + app.NavigateToScene(m_iPad, eUIComponent_TutorialPopup,(void *)this, false, false, &m_hTutorialScene); + } + else + { + app.NavigateToScene(m_iPad, eUIComponent_TutorialPopup,(void *)this, false, false, &m_hTutorialScene); + } +#else + ui.SetTutorial(m_iPad, this); +#endif + } + } + + if(ui.IsPauseMenuDisplayed( m_iPad ) ) + { + if( currentTask[m_CurrentState] != NULL && (!currentTask[m_CurrentState]->AllowFade() || (lastMessageTime + m_iTutorialDisplayMessageTime ) > GetTickCount() ) ) + { + uiTempDisabled = true; + } + ui.SetTutorialVisible( m_iPad, false ); + return; + } + if( uiTempDisabled ) + { + ui.SetTutorialVisible( m_iPad, true ); + lastMessageTime = GetTickCount(); + uiTempDisabled = false; + } + + // Check constraints + for(AUTO_VAR(it, m_globalConstraints.begin()); it < m_globalConstraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + constraint->tick(m_iPad); + } + + // Check hints + int hintNeeded = -1; + if(!m_hintDisplayed) + { + // 4J Stu - TU-1 interim + // Allow turning off all the hints + bool hintsOn = m_isFullTutorial || app.GetGameSettings(m_iPad,eGameSetting_Hints); + + if(hintsOn) + { + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->tick(); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + message->m_allowFade = hint->allowFade(); + message->m_forceDisplay = true; + setMessage( hint, message ); + break; + } + } + } + } + + // Check constraints + // Only need to update these if we aren't already failing something + if( !m_allTutorialsComplete && (currentFailedConstraint[m_CurrentState] == NULL || currentFailedConstraint[m_CurrentState]->isConstraintSatisfied(m_iPad)) ) + { + if( currentFailedConstraint[m_CurrentState] != NULL && currentFailedConstraint[m_CurrentState]->isConstraintSatisfied(m_iPad) ) + { + constraintChanged = true; + currentFailedConstraint[m_CurrentState] = NULL; + } + for(AUTO_VAR(it, constraints[m_CurrentState].begin()); it < constraints[m_CurrentState].end(); ++it) + { + TutorialConstraint *constraint = *it; + if( !constraint->isConstraintSatisfied(m_iPad) && constraint->isConstraintRestrictive(m_iPad) ) + { + constraintChanged = true; + currentFailedConstraint[m_CurrentState] = constraint; + } + } + } + + if( !m_allTutorialsComplete && currentFailedConstraint[m_CurrentState] == NULL ) + { + // Update tasks + bool isCurrentTask = true; + AUTO_VAR(it, activeTasks[m_CurrentState].begin()); + while(activeTasks[m_CurrentState].size() > 0 && it < activeTasks[m_CurrentState].end()) + { + TutorialTask *task = *it; + if( isCurrentTask || task->isPreCompletionEnabled() ) + { + isCurrentTask = false; + if( + ( !task->ShowMinimumTime() || ( task->hasBeenActivated() && (lastMessageTime + m_iTutorialMinimumDisplayMessageTime ) < GetTickCount() ) ) + && task->isCompleted() + ) + { + eTutorial_CompletionAction compAction = task->getCompletionAction(); + it = activeTasks[m_CurrentState].erase( it ); + delete task; + task = NULL; + + if( activeTasks[m_CurrentState].size() > 0 ) + { + switch( compAction ) + { + case e_Tutorial_Completion_Complete_State_Gameplay_Constraints: + { + // 4J Stu - Move the delayed constraints to the gameplay state so that they are in + // effect for a bit longer + AUTO_VAR(itCon, constraintsToRemove[m_CurrentState].begin()); + while(itCon != constraintsToRemove[m_CurrentState].end() ) + { + constraints[e_Tutorial_State_Gameplay].push_back(itCon->first); + constraintsToRemove[e_Tutorial_State_Gameplay].push_back( pair(itCon->first, itCon->second) ); + + constraints[m_CurrentState].erase( find( constraints[m_CurrentState].begin(), constraints[m_CurrentState].end(), itCon->first) ); + itCon = constraintsToRemove[m_CurrentState].erase(itCon); + } + } + // Fall through the the normal complete state + case e_Tutorial_Completion_Complete_State: + for(AUTO_VAR(itRem, activeTasks[m_CurrentState].begin()); itRem < activeTasks[m_CurrentState].end(); ++itRem) + { + delete (*itRem); + } + activeTasks[m_CurrentState].clear(); + break; + case e_Tutorial_Completion_Jump_To_Last_Task: + { + TutorialTask *lastTask = activeTasks[m_CurrentState].at( activeTasks[m_CurrentState].size() - 1 ); + activeTasks[m_CurrentState].pop_back(); + for(AUTO_VAR(itRem, activeTasks[m_CurrentState].begin()); itRem < activeTasks[m_CurrentState].end(); ++itRem) + { + delete (*itRem); + } + activeTasks[m_CurrentState].clear(); + activeTasks[m_CurrentState].push_back( lastTask ); + it = activeTasks[m_CurrentState].begin(); + } + break; + case e_Tutorial_Completion_None: + default: + break; + } + } + + if( activeTasks[m_CurrentState].size() > 0 ) + { + currentTask[m_CurrentState] = activeTasks[m_CurrentState][0]; + currentTask[m_CurrentState]->setAsCurrentTask(); + } + else + { + setStateCompleted( m_CurrentState ); + + currentTask[m_CurrentState] = NULL; + } + taskChanged = true; + + // If we can complete this early, check if we can complete it right now + if( currentTask[m_CurrentState] != NULL && currentTask[m_CurrentState]->isPreCompletionEnabled() ) + { + isCurrentTask = true; + } + } + else + { + ++it; + } + if( task != NULL && task->ShowMinimumTime() && task->hasBeenActivated() && (lastMessageTime + m_iTutorialMinimumDisplayMessageTime ) < GetTickCount() ) + { + task->setShownForMinimumTime(); + + if( !m_hintDisplayed ) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = task->getDescriptionId(); + message->m_promptId = task->getPromptId(); + message->m_allowFade = task->AllowFade(); + message->m_replaceCurrent = true; + setMessage( message ); + } + } + } + else + { + ++it; + } + } + + if( currentTask[m_CurrentState] == NULL && activeTasks[m_CurrentState].size() > 0 ) + { + currentTask[m_CurrentState] = activeTasks[m_CurrentState][0]; + currentTask[m_CurrentState]->setAsCurrentTask(); + taskChanged = true; + } + } + + if(!m_allTutorialsComplete && (taskChanged || m_hasStateChanged) ) + { + bool allComplete = true; + for(unsigned int state = 0; state < e_Tutorial_State_Max; ++state) + { + if(activeTasks[state].size() > 0 ) + { + allComplete = false; + break; + } + if(state==e_Tutorial_State_Gameplay) + { + m_fullTutorialComplete = true; + Minecraft::GetInstance()->playerLeftTutorial(m_iPad); + } + } + if(allComplete) + m_allTutorialsComplete = true; + } + + if( constraintChanged || taskChanged || m_hasStateChanged || + (currentFailedConstraint[m_CurrentState] == NULL && currentTask[m_CurrentState] != NULL && (m_lastMessage == NULL || currentTask[m_CurrentState]->getDescriptionId() != m_lastMessage->m_messageId) && !m_hintDisplayed) + ) + { + if( currentFailedConstraint[m_CurrentState] != NULL ) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = currentFailedConstraint[m_CurrentState]->getDescriptionId(); + message->m_allowFade = false; + setMessage( message ); + } + else if( currentTask[m_CurrentState] != NULL ) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = currentTask[m_CurrentState]->getDescriptionId(); + message->m_promptId = currentTask[m_CurrentState]->getPromptId(); + message->m_allowFade = currentTask[m_CurrentState]->AllowFade(); + setMessage( message ); + currentTask[m_CurrentState]->TaskReminders()? m_iTaskReminders = 1 : m_iTaskReminders = 0; + } + else + { + setMessage( NULL ); + } + } + + if(m_hintDisplayed && (lastMessageTime + m_iTutorialDisplayMessageTime ) < GetTickCount() ) + { + m_hintDisplayed = false; + } + + if( currentFailedConstraint[m_CurrentState] == NULL && currentTask[m_CurrentState] != NULL && (m_iTaskReminders!=0) && (lastMessageTime + (m_iTaskReminders * m_iTutorialReminderTime) ) < GetTickCount() ) + { + // Reminder + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = currentTask[m_CurrentState]->getDescriptionId(); + message->m_promptId = currentTask[m_CurrentState]->getPromptId(); + message->m_allowFade = currentTask[m_CurrentState]->AllowFade(); + message->m_isReminder = true; + setMessage( message ); + ++m_iTaskReminders; + if( m_iTaskReminders > 1 ) + m_iTaskReminders = 1; + } + + m_hasStateChanged = false; + + // If we have completed this state, and it is one that occurs during normal gameplay then change back to the gameplay track + if( m_CurrentState != e_Tutorial_State_Gameplay && activeTasks[m_CurrentState].size() == 0 && (isSelectedItemState() || !ui.GetMenuDisplayed(m_iPad) ) ) + { + this->changeTutorialState( e_Tutorial_State_Gameplay ); + } +} + +bool Tutorial::setMessage(PopupMessageDetails *message) +{ + if(message != NULL && !message->m_forceDisplay && + m_lastMessageState == m_CurrentState && + message->isSameContent(m_lastMessage) && + ( !message->m_isReminder || ( (lastMessageTime + m_iTutorialReminderTime ) > GetTickCount() && message->m_isReminder ) ) + ) + { + delete message; + return false; + } + + if(message != NULL && (message->m_messageId > 0 || !message->m_messageString.empty()) ) + { + m_lastMessageState = m_CurrentState; + + if(!message->m_replaceCurrent) lastMessageTime = GetTickCount(); + + wstring text; + if(!message->m_messageString.empty()) + { + text = message->m_messageString; + } + else + { + AUTO_VAR(it, messages.find(message->m_messageId)); + if( it != messages.end() && it->second != NULL ) + { + TutorialMessage *messageString = it->second; + text = wstring( messageString->getMessageForDisplay() ); + } + else + { + text = wstring( app.GetString(message->m_messageId) ); + } + } + + if(!message->m_promptString.empty()) + { + text.append(message->m_promptString); + } + else if(message->m_promptId >= 0) + { + AUTO_VAR(it, messages.find(message->m_promptId)); + if(it != messages.end() && it->second != NULL) + { + TutorialMessage *prompt = it->second; + text.append( prompt->getMessageForDisplay() ); + } + } + + wstring title; + TutorialPopupInfo popupInfo; + popupInfo.interactScene = m_UIScene; + popupInfo.desc = text.c_str(); + popupInfo.icon = message->m_icon; + popupInfo.iAuxVal = message->m_iAuxVal; + popupInfo.allowFade = message->m_allowFade; + popupInfo.isReminder = message->m_isReminder; + popupInfo.tutorial = this; + if( !message->m_titleString.empty() || message->m_titleId > 0 ) + { + if(message->m_titleString.empty()) title = wstring( app.GetString(message->m_titleId) ); + else title = message->m_titleString; + + popupInfo.title = title.c_str(); + ui.SetTutorialDescription( m_iPad, &popupInfo ); + } + else + { + ui.SetTutorialDescription( m_iPad, &popupInfo ); + } + } + else if( (m_lastMessage != NULL && m_lastMessage->m_messageId != -1) ) //&& (lastMessageTime + m_iTutorialReminderTime ) > GetTickCount() ) + { + // This should cause the popup to dissappear + TutorialPopupInfo popupInfo; + popupInfo.interactScene = m_UIScene; + popupInfo.tutorial = this; + ui.SetTutorialDescription( m_iPad, &popupInfo ); + } + + if(m_lastMessage != NULL) delete m_lastMessage; + m_lastMessage = message; + + return true; +} + +bool Tutorial::setMessage(TutorialHint *hint, PopupMessageDetails *message) +{ + // 4J Stu - TU-1 interim + // Allow turning off all the hints + bool hintsOn = m_isFullTutorial || (app.GetGameSettings(m_iPad,eGameSetting_Hints) && app.GetGameSettings(m_iPad,eGameSetting_DisplayHUD)); + + bool messageShown = false; + DWORD time = GetTickCount(); + if(message != NULL && (message->m_forceDisplay || hintsOn) && + (!message->m_delay || + ( + (m_hintDisplayed && (time - m_lastHintDisplayedTime) > m_iTutorialHintDelayTime ) || + (!m_hintDisplayed && (time - lastMessageTime) > m_iTutorialMinimumDisplayMessageTime ) + ) + ) + ) + { + messageShown = setMessage( message ); + + if(messageShown) + { + m_lastHintDisplayedTime = time; + m_hintDisplayed = true; + if(hint!=NULL) setHintCompleted( hint ); + } + } + return messageShown; +} + +bool Tutorial::setMessage(const wstring &messageString, int icon, int auxValue) +{ + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageString = messageString; + message->m_icon = icon; + message->m_iAuxVal = auxValue; + message->m_forceDisplay = true; + + return setMessage(message); +} + +void Tutorial::showTutorialPopup(bool show) +{ + m_allowShow = show; + + if(!show) + { + if( currentTask[m_CurrentState] != NULL && (!currentTask[m_CurrentState]->AllowFade() || (lastMessageTime + m_iTutorialDisplayMessageTime ) > GetTickCount() ) ) + { + uiTempDisabled = true; + } + ui.SetTutorialVisible( m_iPad, show ); + } +} + +void Tutorial::useItemOn(Level *level, shared_ptr item, int x, int y, int z, bool bTestUseOnly) +{ + for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + { + TutorialTask *task = *it; + task->useItemOn(level, item, x, y, z, bTestUseOnly); + } +} + +void Tutorial::useItemOn(shared_ptr item, bool bTestUseOnly) +{ + for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + { + TutorialTask *task = *it; + task->useItem(item, bTestUseOnly); + } +} + +void Tutorial::completeUsingItem(shared_ptr item) +{ + for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + { + TutorialTask *task = *it; + task->completeUsingItem(item); + } + + // Fix for #46922 - TU5: UI: Player receives a reminder that he is hungry while "hunger bar" is full (triggered in split-screen mode) + if(m_CurrentState != e_Tutorial_State_Gameplay) + { + for(AUTO_VAR(it, activeTasks[e_Tutorial_State_Gameplay].begin()); it < activeTasks[e_Tutorial_State_Gameplay].end(); ++it) + { + TutorialTask *task = *it; + task->completeUsingItem(item); + } + } +} + +void Tutorial::startDestroyBlock(shared_ptr item, Tile *tile) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->startDestroyBlock(item, tile); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::destroyBlock(Tile *tile) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->destroyBlock(tile); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::attack(shared_ptr player, shared_ptr entity) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->attack(player->inventory->getSelected(), entity); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::itemDamaged(shared_ptr item) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->itemDamaged(item); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::handleUIInput(int iAction) +{ + if( m_hintDisplayed ) return; + + //for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + //{ + // TutorialTask *task = *it; + // task->handleUIInput(iAction); + //} + if(currentTask[m_CurrentState] != NULL) + currentTask[m_CurrentState]->handleUIInput(iAction); +} + +void Tutorial::createItemSelected(shared_ptr item, bool canMake) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->createItemSelected(item, canMake); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::onCrafted(shared_ptr item) +{ + for(unsigned int state = 0; state < e_Tutorial_State_Max; ++state) + { + for(AUTO_VAR(it, activeTasks[state].begin()); it < activeTasks[state].end(); ++it) + { + TutorialTask *task = *it; + task->onCrafted(item); + } + } +} + +void Tutorial::onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux) +{ + if( !m_hintDisplayed ) + { + bool hintNeeded = false; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->onTake(item); + if(hintNeeded) + { + break; + } + + } + } + + for(unsigned int state = 0; state < e_Tutorial_State_Max; ++state) + { + for(AUTO_VAR(it, activeTasks[state].begin()); it < activeTasks[state].end(); ++it) + { + TutorialTask *task = *it; + task->onTake(item, invItemCountAnyAux, invItemCountThisAux); + } + } +} + +void Tutorial::onSelectedItemChanged(shared_ptr item) +{ + // We only handle this if we are in a state that allows changing based on the selected item + // Menus and states like riding in a minecart will NOT allow this + if( isSelectedItemState() ) + { + if(item != NULL) + { + switch(item->id) + { + case Item::fishingRod_Id: + changeTutorialState(e_Tutorial_State_Fishing); + break; + default: + changeTutorialState(e_Tutorial_State_Gameplay); + break; + } + } + else + { + changeTutorialState(e_Tutorial_State_Gameplay); + } + } +} + +void Tutorial::onLookAt(int id, int iData) +{ + if( m_hintDisplayed ) return; + + bool hintNeeded = false; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->onLookAt(id, iData); + if(hintNeeded) + { + break; + } + } + + if( m_CurrentState == e_Tutorial_State_Gameplay ) + { + if(id > 0) + { + switch(id) + { + case Tile::bed_Id: + changeTutorialState(e_Tutorial_State_Bed); + break; + default: + break; + } + } + } +} + +void Tutorial::onLookAtEntity(eINSTANCEOF type) +{ + if( m_hintDisplayed ) return; + + bool hintNeeded = false; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->onLookAtEntity(type); + if(hintNeeded) + { + break; + } + } +} + +void Tutorial::onEffectChanged(MobEffect *effect, bool bRemoved) +{ + for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + { + TutorialTask *task = *it; + task->onEffectChanged(effect,bRemoved); + } +} + +bool Tutorial::canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt) +{ + bool allowed = true; + for(AUTO_VAR(it, constraints[m_CurrentState].begin()); it < constraints[m_CurrentState].end(); ++it) + { + TutorialConstraint *constraint = *it; + if( !constraint->isConstraintSatisfied(m_iPad) && !constraint->canMoveToPosition(xo,yo,zo,xt,yt,zt) ) + { + allowed = false; + break; + } + } + return allowed; +} + +bool Tutorial::isInputAllowed(int mapping) +{ + if( m_hintDisplayed ) return true; + + // If the player is under water then allow all keypresses so they can jump out + if( Minecraft::GetInstance()->localplayers[m_iPad]->isUnderLiquid(Material::water) ) return true; + + bool allowed = true; + for(AUTO_VAR(it, constraints[m_CurrentState].begin()); it < constraints[m_CurrentState].end(); ++it) + { + TutorialConstraint *constraint = *it; + if( constraint->isMappingConstrained( m_iPad, mapping ) ) + { + allowed = false; + break; + } + } + return allowed; +} + +vector *Tutorial::getTasks() +{ + return &tasks; +} + +unsigned int Tutorial::getCurrentTaskIndex() +{ + unsigned int index = 0; + for(AUTO_VAR(it, tasks.begin()); it < tasks.end(); ++it) + { + if(*it == currentTask[e_Tutorial_State_Gameplay]) + break; + + ++index; + } + return index; +} + +void Tutorial::AddGlobalConstraint(TutorialConstraint *c) +{ + m_globalConstraints.push_back(c); +} + +void Tutorial::AddConstraint(TutorialConstraint *c) +{ + constraints[m_CurrentState].push_back(c); +} + +void Tutorial::RemoveConstraint(TutorialConstraint *c, bool delayedRemove /*= false*/) +{ + if( currentFailedConstraint[m_CurrentState] == c ) + currentFailedConstraint[m_CurrentState] = NULL; + + if( c->getQueuedForRemoval() ) + { + // If it is already queued for removal, remove it on the next tick + /*for(AUTO_VAR(it, constraintsToRemove[m_CurrentState].begin()); it < constraintsToRemove[m_CurrentState].end(); ++it) + { + if( it->first == c ) + { + it->second = m_iTutorialConstraintDelayRemoveTicks; + break; + } + }*/ + } + else if(delayedRemove) + { + c->setQueuedForRemoval(true); + constraintsToRemove[m_CurrentState].push_back( pair(c, 0) ); + } + else + { + for( AUTO_VAR(it, constraintsToRemove[m_CurrentState].begin()); it < constraintsToRemove[m_CurrentState].end(); ++it) + { + if( it->first == c ) + { + constraintsToRemove[m_CurrentState].erase( it ); + break; + } + } + + AUTO_VAR(it, find( constraints[m_CurrentState].begin(), constraints[m_CurrentState].end(), c)); + if( it != constraints[m_CurrentState].end() ) constraints[m_CurrentState].erase( find( constraints[m_CurrentState].begin(), constraints[m_CurrentState].end(), c) ); + + // It may be in the gameplay list, so remove it from there if it is + it = find( constraints[e_Tutorial_State_Gameplay].begin(), constraints[e_Tutorial_State_Gameplay].end(), c); + if( it != constraints[e_Tutorial_State_Gameplay].end() ) constraints[e_Tutorial_State_Gameplay].erase( find( constraints[e_Tutorial_State_Gameplay].begin(), constraints[e_Tutorial_State_Gameplay].end(), c) ); + } +} + +void Tutorial::addTask(eTutorial_State state, TutorialTask *t) +{ + if( state == e_Tutorial_State_Gameplay ) + { + tasks.push_back(t); + } + activeTasks[state].push_back(t); +} + +void Tutorial::addHint(eTutorial_State state, TutorialHint *h) +{ + hints[state].push_back(h); +} + +void Tutorial::addMessage(int messageId, bool limitRepeats /*= false*/, unsigned char numRepeats /*= TUTORIAL_MESSAGE_DEFAULT_SHOW*/) +{ + if(messageId >= 0 && messages.find(messageId)==messages.end()) + messages[messageId] = new TutorialMessage(messageId, limitRepeats, numRepeats); +} + +#ifdef _XBOX +void Tutorial::changeTutorialState(eTutorial_State newState, CXuiScene *scene /*= NULL*/) +#else +void Tutorial::changeTutorialState(eTutorial_State newState, UIScene *scene /*= NULL*/) +#endif +{ + if(newState == m_CurrentState) + { + // If clearing the scene, make sure that the tutorial popup has its reference to this scene removed +#ifndef _XBOX + if( scene == NULL ) + { + ui.RemoveInteractSceneReference(m_iPad, m_UIScene); + } +#endif + m_UIScene = scene; + return; + } + // 4J Stu - TU-1 interim + // Allow turning off all the hints + bool hintsOn = m_isFullTutorial || app.GetGameSettings(m_iPad,eGameSetting_Hints); + + if(hintsOn) + { + // If we have completed this state, and it is one that occurs during normal gameplay then change back to the gameplay track + if( newState != e_Tutorial_State_Gameplay && activeTasks[newState].size() == 0 && !ui.GetMenuDisplayed(m_iPad) ) + { + return; + } + + // The action that caused the change of state may also have completed the current task + if( currentTask[m_CurrentState] != NULL && currentTask[m_CurrentState]->isCompleted() ) + { + activeTasks[m_CurrentState].erase( find( activeTasks[m_CurrentState].begin(), activeTasks[m_CurrentState].end(), currentTask[m_CurrentState]) ); + + if( activeTasks[m_CurrentState].size() > 0 ) + { + currentTask[m_CurrentState] = activeTasks[m_CurrentState][0]; + currentTask[m_CurrentState]->setAsCurrentTask(); + } + else + { + currentTask[m_CurrentState] = NULL; + } + } + + if( currentTask[m_CurrentState] != NULL ) + { + currentTask[m_CurrentState]->onStateChange(newState); + } + + // Make sure that the current message is cleared + setMessage( NULL ); + + // If clearing the scene, make sure that the tutorial popup has its reference to this scene removed +#ifndef _XBOX + if( scene == NULL ) + { + ui.RemoveInteractSceneReference(m_iPad, m_UIScene); + } +#endif + m_UIScene = scene; + + + if( m_CurrentState != newState ) + { + for(AUTO_VAR(it, activeTasks[newState].begin()); it < activeTasks[newState].end(); ++it) + { + TutorialTask *task = *it; + task->onStateChange(newState); + } + m_CurrentState = newState; + m_hasStateChanged = true; + m_hintDisplayed = false; + } + } +} + +bool Tutorial::isSelectedItemState() +{ + bool isSelectedItemState = false; + switch(m_CurrentState) + { + case e_Tutorial_State_Gameplay: + case e_Tutorial_State_Fishing: + isSelectedItemState = true; + break; + default: + break; + } + return isSelectedItemState; +} diff --git a/Minecraft.Client/Common/Tutorial/Tutorial.h b/Minecraft.Client/Common/Tutorial/Tutorial.h new file mode 100644 index 00000000..aaaaba0a --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/Tutorial.h @@ -0,0 +1,199 @@ +#pragma once +using namespace std; +#include "TutorialTask.h" +#include "TutorialConstraint.h" +#include "TutorialHint.h" +#include "TutorialMessage.h" +#include "TutorialEnum.h" + +// #define TUTORIAL_HINT_DELAY_TIME 14000 // How long we should wait from displaying one hint to the next +// #define TUTORIAL_DISPLAY_MESSAGE_TIME 7000 +// #define TUTORIAL_MINIMUM_DISPLAY_MESSAGE_TIME 2000 +// #define TUTORIAL_REMINDER_TIME (TUTORIAL_DISPLAY_MESSAGE_TIME + 20000) +// #define TUTORIAL_CONSTRAINT_DELAY_REMOVE_TICKS 15 +// +// // 0-24000 +// #define TUTORIAL_FREEZE_TIME_VALUE 8000 + +class Level; +class CXuiScene; + +class Tutorial +{ +public: + class PopupMessageDetails + { + public: + int m_messageId; + int m_promptId; + int m_titleId; + wstring m_messageString; + wstring m_promptString; + wstring m_titleString; + int m_icon; + int m_iAuxVal; + bool m_allowFade; + bool m_isReminder; + bool m_replaceCurrent; + bool m_forceDisplay; + bool m_delay; + + PopupMessageDetails() + { + m_messageId = -1; + m_promptId = -1; + m_titleId = -1; + m_messageString = L""; + m_promptString = L""; + m_titleString = L""; + m_icon = TUTORIAL_NO_ICON; + m_iAuxVal = 0; + m_allowFade = true; + m_isReminder = false; + m_replaceCurrent = false; + m_forceDisplay = false; + m_delay = false; + } + + bool isSameContent(PopupMessageDetails *other); + + }; + +private: + static int m_iTutorialHintDelayTime; + static int m_iTutorialDisplayMessageTime; + static int m_iTutorialMinimumDisplayMessageTime; + static int m_iTutorialExtraReminderTime; + static int m_iTutorialReminderTime; + static int m_iTutorialConstraintDelayRemoveTicks; + static int m_iTutorialFreezeTimeValue; + eTutorial_State m_CurrentState; + bool m_hasStateChanged; +#ifdef _XBOX + HXUIOBJ m_hTutorialScene; // to store the popup scene (splitscreen or normal) +#endif + bool m_bSceneIsSplitscreen; + + bool m_bHasTickedOnce; + int m_firstTickTime; + +protected: + unordered_map messages; + vector m_globalConstraints; + vector constraints[e_Tutorial_State_Max]; + vector< pair > constraintsToRemove[e_Tutorial_State_Max]; + vector tasks; // We store a copy of the tasks for the main gameplay tutorial so that we could display an overview menu + vector activeTasks[e_Tutorial_State_Max]; + vector hints[e_Tutorial_State_Max]; + TutorialTask *currentTask[e_Tutorial_State_Max]; + TutorialConstraint *currentFailedConstraint[e_Tutorial_State_Max]; + + bool m_freezeTime; + bool m_timeFrozen; + //D3DXVECTOR3 m_OriginalPosition; + +public: + DWORD lastMessageTime; + DWORD m_lastHintDisplayedTime; +private: + PopupMessageDetails *m_lastMessage; + + eTutorial_State m_lastMessageState; + unsigned int m_iTaskReminders; + + bool m_allowShow; + +public: + bool m_hintDisplayed; + +private: + bool hasRequestedUI; + bool uiTempDisabled; + +#ifdef _XBOX + CXuiScene *m_UIScene; +#else + UIScene *m_UIScene; +#endif + + int m_iPad; +public: + bool m_allTutorialsComplete; + bool m_fullTutorialComplete; + bool m_isFullTutorial; +public: + Tutorial(int iPad, bool isFullTutorial = false); + ~Tutorial(); + void tick(); + + int getPad() { return m_iPad; } + + virtual bool isStateCompleted( eTutorial_State state ); + virtual void setStateCompleted( eTutorial_State state ); + bool isHintCompleted( eTutorial_Hint hint ); + void setHintCompleted( eTutorial_Hint hint ); + void setHintCompleted( TutorialHint *hint ); + + // completableId will be either a eTutorial_State value or eTutorial_Hint + void setCompleted( int completableId ); + bool getCompleted( int completableId ); + +#ifdef _XBOX + void changeTutorialState(eTutorial_State newState, CXuiScene *scene = NULL); +#else + void changeTutorialState(eTutorial_State newState, UIScene *scene = NULL); +#endif + bool isSelectedItemState(); + + bool setMessage(PopupMessageDetails *message); + bool setMessage(TutorialHint *hint, PopupMessageDetails *message); + bool setMessage(const wstring &message, int icon, int auxValue); + + void showTutorialPopup(bool show); + + void useItemOn(Level *level, shared_ptr item, int x, int y, int z,bool bTestUseOnly=false); + void useItemOn(shared_ptr item, bool bTestUseOnly=false); + void completeUsingItem(shared_ptr item); + void startDestroyBlock(shared_ptr item, Tile *tile); + void destroyBlock(Tile *tile); + void attack(shared_ptr player, shared_ptr entity); + void itemDamaged(shared_ptr item); + + void handleUIInput(int iAction); + void createItemSelected(shared_ptr item, bool canMake); + void onCrafted(shared_ptr item); + void onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux); + void onSelectedItemChanged(shared_ptr item); + void onLookAt(int id, int iData=0); + void onLookAtEntity(eINSTANCEOF type); + void onEffectChanged(MobEffect *effect, bool bRemoved=false); + + bool canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt); + bool isInputAllowed(int mapping); + + void AddGlobalConstraint(TutorialConstraint *c); + void AddConstraint(TutorialConstraint *c); + void RemoveConstraint(TutorialConstraint *c, bool delayedRemove = false); + void addTask(eTutorial_State state, TutorialTask *t); + void addHint(eTutorial_State state, TutorialHint *h); + void addMessage(int messageId, bool limitRepeats = false, unsigned char numRepeats = TUTORIAL_MESSAGE_DEFAULT_SHOW); + + int GetTutorialDisplayMessageTime() {return m_iTutorialDisplayMessageTime;} + + // Only for the main gameplay tutorial + vector *getTasks(); + unsigned int getCurrentTaskIndex(); + +#ifdef _XBOX + CXuiScene *getScene() { return m_UIScene; } +#else + UIScene *getScene() { return m_UIScene; } +#endif + eTutorial_State getCurrentState() { return m_CurrentState; } + + // These are required so that we have a consistent mapping of the completion bits stored in the profile data + static void staticCtor(); + static vector s_completableTasks; + + static void debugResetPlayerSavedProgress(int iPad); +}; diff --git a/Minecraft.Client/Common/Tutorial/TutorialConstraint.h b/Minecraft.Client/Common/Tutorial/TutorialConstraint.h new file mode 100644 index 00000000..877fd57e --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialConstraint.h @@ -0,0 +1,41 @@ +#pragma once + +// 4J Stu - An abstract class that represents a constraint on what the user is able to do +class TutorialConstraint +{ +private: + int descriptionId; + bool m_deleteOnDeactivate; + bool m_queuedForRemoval; +public: + enum ConstraintType + { + e_ConstraintInput = 0, // Constraint on controller input + e_ConstraintArea, + e_ConstraintAllInput, + e_ConstraintXuiInput, + e_ConstraintChangeState, + }; + + TutorialConstraint(int descriptionId) : descriptionId( descriptionId ), m_deleteOnDeactivate( false ), m_queuedForRemoval( false ) {} + virtual ~TutorialConstraint() {} + + int getDescriptionId() { return descriptionId; } + + virtual ConstraintType getType() = 0; + + virtual void tick(int iPad) {} + virtual bool isConstraintSatisfied(int iPad) { return true; } + virtual bool isConstraintRestrictive(int iPad) { return true; } + + virtual bool isMappingConstrained(int iPad, int mapping) { return false;} + virtual bool isXuiInputConstrained(int vk) { return false;} + + void setDeleteOnDeactivate(bool deleteOnDeactivated) { m_deleteOnDeactivate = deleteOnDeactivated; } + bool getDeleteOnDeactivate() { return m_deleteOnDeactivate; } + + void setQueuedForRemoval(bool queued) { m_queuedForRemoval = queued; } + bool getQueuedForRemoval() { return m_queuedForRemoval; } + + virtual bool canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt) { return true; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialConstraints.h b/Minecraft.Client/Common/Tutorial/TutorialConstraints.h new file mode 100644 index 00000000..74bb8935 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialConstraints.h @@ -0,0 +1,4 @@ +#include "TutorialConstraint.h" +#include "AreaConstraint.h" +#include "ChangeStateConstraint.h" +#include "InputConstraint.h" \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialEnum.h b/Minecraft.Client/Common/Tutorial/TutorialEnum.h new file mode 100644 index 00000000..33f2e67d --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialEnum.h @@ -0,0 +1,329 @@ +#pragma once + +typedef struct { + WORD index; + DWORD diffsSize; + BYTE *diffs; + DWORD lastByteChanged; +} TutorialDiff_Chunk; + +typedef struct { + DWORD diffCount; + TutorialDiff_Chunk *diffs; +} TutorialDiff_File; + +#define TUTORIAL_NO_TEXT -1 +#define TUTORIAL_NO_ICON -1 + +// If you want to make these bigger, be aware that that will affect what is stored after the tutorial data in the profile data +// See Xbox_App.h for the struct +#define TUTORIAL_PROFILE_STORAGE_BITS 512 +#define TUTORIAL_PROFILE_STORAGE_BYTES (TUTORIAL_PROFILE_STORAGE_BITS/8) + +// 4J Stu - The total number of eTutorial_State and eTutorial_Hint must be less than 512, as we only have 512 bits of profile +// data to flag whether or not the player has seen them +// In general a block or tool will have one each. We have a state if we need more than one message, or a hint if just once +// message will suffice +// Tasks added here should also be added in the Tutorial::staticCtor() if you wish to store completion in the profile data +enum eTutorial_State +{ + e_Tutorial_State_Any = -2, + e_Tutorial_State_None = -1, + + e_Tutorial_State_Gameplay = 0, + + e_Tutorial_State_Inventory_Menu, + e_Tutorial_State_2x2Crafting_Menu, + e_Tutorial_State_3x3Crafting_Menu, + e_Tutorial_State_Furnace_Menu, + + e_Tutorial_State_Riding_Minecart, + e_Tutorial_State_Riding_Boat, + e_Tutorial_State_Fishing, + + e_Tutorial_State_Bed, + + e_Tutorial_State_Container_Menu, + e_Tutorial_State_Trap_Menu, + e_Tutorial_State_Redstone_And_Piston, + e_Tutorial_State_Portal, + e_Tutorial_State_Creative_Inventory_Menu, // Added TU5 + e_Tutorial_State_Food_Bar, // Added TU5 + e_Tutorial_State_CreativeMode, // Added TU7 + e_Tutorial_State_Brewing, + e_Tutorial_State_Brewing_Menu, + e_Tutorial_State_Enchanting, + e_Tutorial_State_Enchanting_Menu, + e_Tutorial_State_Farming, + e_Tutorial_State_Breeding, + e_Tutorial_State_Golem, + e_Tutorial_State_Trading, + e_Tutorial_State_Trading_Menu, + e_Tutorial_State_Anvil, + e_Tutorial_State_Anvil_Menu, + e_Tutorial_State_Enderchests, + + e_Tutorial_State_Unused_9, + e_Tutorial_State_Unused_10, + + e_Tutorial_State_Max +}; + +// Hints added here should also be added in the Tutorial::staticCtor() if you wish to store completion in the profile data +enum eTutorial_Hint +{ + e_Tutorial_Hint_Always_On = e_Tutorial_State_Max, + + e_Tutorial_Hint_Hold_To_Mine, + e_Tutorial_Hint_Tool_Damaged, + e_Tutorial_Hint_Swim_Up, + + e_Tutorial_Hint_Unused_2, + e_Tutorial_Hint_Unused_3, + e_Tutorial_Hint_Unused_4, + e_Tutorial_Hint_Unused_5, + e_Tutorial_Hint_Unused_6, + e_Tutorial_Hint_Unused_7, + e_Tutorial_Hint_Unused_8, + e_Tutorial_Hint_Unused_9, + e_Tutorial_Hint_Unused_10, + + e_Tutorial_Hint_Rock, + e_Tutorial_Hint_Stone, + e_Tutorial_Hint_Planks, + e_Tutorial_Hint_Sapling, + e_Tutorial_Hint_Unbreakable, + e_Tutorial_Hint_Water, + e_Tutorial_Hint_Lava, + e_Tutorial_Hint_Sand, + e_Tutorial_Hint_Gravel, + e_Tutorial_Hint_Gold_Ore, + e_Tutorial_Hint_Iron_Ore, + e_Tutorial_Hint_Coal_Ore, + e_Tutorial_Hint_Tree_Trunk, + e_Tutorial_Hint_Leaves, + e_Tutorial_Hint_Glass, + e_Tutorial_Hint_Lapis_Ore, + e_Tutorial_Hint_Lapis_Block, + e_Tutorial_Hint_Dispenser, + e_Tutorial_Hint_Sandstone, + e_Tutorial_Hint_Note_Block, + e_Tutorial_Hint_Powered_Rail, + e_Tutorial_Hint_Detector_Rail, + e_Tutorial_Hint_Tall_Grass, + e_Tutorial_Hint_Wool, + e_Tutorial_Hint_Flower, + e_Tutorial_Hint_Mushroom, + e_Tutorial_Hint_Gold_Block, + e_Tutorial_Hint_Iron_Block, + e_Tutorial_Hint_Stone_Slab, + e_Tutorial_Hint_Red_Brick, + e_Tutorial_Hint_Tnt, + e_Tutorial_Hint_Bookshelf, + e_Tutorial_Hint_Moss_Stone, + e_Tutorial_Hint_Obsidian, + e_Tutorial_Hint_Torch, + e_Tutorial_Hint_MobSpawner, + e_Tutorial_Hint_Chest, + e_Tutorial_Hint_Redstone, + e_Tutorial_Hint_Diamond_Ore, + e_Tutorial_Hint_Diamond_Block, + e_Tutorial_Hint_Crafting_Table, + e_Tutorial_Hint_Crops, + e_Tutorial_Hint_Farmland, + e_Tutorial_Hint_Furnace, + e_Tutorial_Hint_Sign, + e_Tutorial_Hint_Door_Wood, + e_Tutorial_Hint_Ladder, + e_Tutorial_Hint_Stairs_Stone, + e_Tutorial_Hint_Rail, + e_Tutorial_Hint_Lever, + e_Tutorial_Hint_PressurePlate, + e_Tutorial_Hint_Door_Iron, + e_Tutorial_Hint_Redstone_Ore, + e_Tutorial_Hint_Redstone_Torch, + e_Tutorial_Hint_Button, + e_Tutorial_Hint_Snow, + e_Tutorial_Hint_Ice, + e_Tutorial_Hint_Cactus, + e_Tutorial_Hint_Clay, + e_Tutorial_Hint_Sugarcane, + e_Tutorial_Hint_Record_Player, + e_Tutorial_Hint_Pumpkin, + e_Tutorial_Hint_Hell_Rock, + e_Tutorial_Hint_Hell_Sand, + e_Tutorial_Hint_Glowstone, + e_Tutorial_Hint_Portal, + e_Tutorial_Hint_Pumpkin_Lit, + e_Tutorial_Hint_Cake, + e_Tutorial_Hint_Redstone_Repeater, + e_Tutorial_Hint_Trapdoor, + e_Tutorial_Hint_Piston, + e_Tutorial_Hint_Sticky_Piston, + e_Tutorial_Hint_Monster_Stone_Egg, + e_Tutorial_Hint_Stone_Brick_Smooth, + e_Tutorial_Hint_Huge_Mushroom, + e_Tutorial_Hint_Iron_Fence, + e_Tutorial_Hint_Thin_Glass, + e_Tutorial_Hint_Melon, + e_Tutorial_Hint_Vine, + e_Tutorial_Hint_Fence_Gate, + e_Tutorial_Hint_Mycel, + e_Tutorial_Hint_Water_Lily, + e_Tutorial_Hint_Nether_Brick, + e_Tutorial_Hint_Nether_Fence, + e_Tutorial_Hint_Nether_Stalk, + e_Tutorial_Hint_Enchant_Table, + e_Tutorial_Hint_Brewing_Stand, + e_Tutorial_Hint_Cauldron, + e_Tutorial_Hint_End_Portal, + e_Tutorial_Hint_End_Portal_Frame, + + e_Tutorial_Hint_Squid, + e_Tutorial_Hint_Cow, + e_Tutorial_Hint_Sheep, + e_Tutorial_Hint_Chicken, + e_Tutorial_Hint_Pig, + e_Tutorial_Hint_Wolf, + e_Tutorial_Hint_Creeper, + e_Tutorial_Hint_Skeleton, + e_Tutorial_Hint_Spider, + e_Tutorial_Hint_Zombie, + e_Tutorial_Hint_Pig_Zombie, + e_Tutorial_Hint_Ghast, + e_Tutorial_Hint_Slime, + e_Tutorial_Hint_Enderman, + e_Tutorial_Hint_Silverfish, + e_Tutorial_Hint_Cave_Spider, + e_Tutorial_Hint_MushroomCow, + e_Tutorial_Hint_SnowMan, + e_Tutorial_Hint_IronGolem, + e_Tutorial_Hint_EnderDragon, + e_Tutorial_Hint_Blaze, + e_Tutorial_Hint_Lava_Slime, + + e_Tutorial_Hint_Ozelot, + e_Tutorial_Hint_Villager, + + e_Tutorial_Hint_Item_Shovel, + e_Tutorial_Hint_Item_Hatchet, + e_Tutorial_Hint_Item_Pickaxe, + e_Tutorial_Hint_Item_Flint_And_Steel, + e_Tutorial_Hint_Item_Apple, + e_Tutorial_Hint_Item_Bow, + e_Tutorial_Hint_Item_Arrow, + e_Tutorial_Hint_Item_Coal, + e_Tutorial_Hint_Item_Diamond, + e_Tutorial_Hint_Item_Iron_Ingot, + e_Tutorial_Hint_Item_Gold_Ingot, + e_Tutorial_Hint_Item_Sword, + e_Tutorial_Hint_Item_Stick, + e_Tutorial_Hint_Item_Bowl, + e_Tutorial_Hint_Item_Mushroom_Stew, + e_Tutorial_Hint_Item_String, + e_Tutorial_Hint_Item_Feather, + e_Tutorial_Hint_Item_Sulphur, + e_Tutorial_Hint_Item_Hoe, + e_Tutorial_Hint_Item_Seeds, + e_Tutorial_Hint_Item_Wheat, + e_Tutorial_Hint_Item_Bread, + e_Tutorial_Hint_Item_Helmet, + e_Tutorial_Hint_Item_Chestplate, + e_Tutorial_Hint_Item_Leggings, + e_Tutorial_Hint_Item_Boots, + e_Tutorial_Hint_Item_Flint, + e_Tutorial_Hint_Item_Porkchop_Raw, + e_Tutorial_Hint_Item_Porkchop_Cooked, + e_Tutorial_Hint_Item_Painting, + e_Tutorial_Hint_Item_Apple_Gold, + e_Tutorial_Hint_Item_Sign, + e_Tutorial_Hint_Item_Door_Wood, + e_Tutorial_Hint_Item_Bucket_Empty, + e_Tutorial_Hint_Item_Bucket_Water, + e_Tutorial_Hint_Item_Bucket_Lava, + e_Tutorial_Hint_Item_Minecart, + e_Tutorial_Hint_Item_Saddle, + e_Tutorial_Hint_Item_Door_Iron, + e_Tutorial_Hint_Item_Redstone, + e_Tutorial_Hint_Item_Snowball, + e_Tutorial_Hint_Item_Boat, + e_Tutorial_Hint_Item_Leather, + e_Tutorial_Hint_Item_Milk, + e_Tutorial_Hint_Item_Brick, + e_Tutorial_Hint_Item_Clay, + e_Tutorial_Hint_Item_Reeds, + e_Tutorial_Hint_Item_Paper, + e_Tutorial_Hint_Item_Book, + e_Tutorial_Hint_Item_Slimeball, + e_Tutorial_Hint_Item_Minecart_Chest, + e_Tutorial_Hint_Item_Minecart_Furnace, + e_Tutorial_Hint_Item_Egg, + e_Tutorial_Hint_Item_Compass, + e_Tutorial_Hint_Item_Clock, + e_Tutorial_Hint_Item_Yellow_Dust, + e_Tutorial_Hint_Item_Fish_Raw, + e_Tutorial_Hint_Item_Fish_Cooked, + e_Tutorial_Hint_Item_Dye_Powder, + e_Tutorial_Hint_Item_Bone, + e_Tutorial_Hint_Item_Sugar, + e_Tutorial_Hint_Item_Cake, + e_Tutorial_Hint_Item_Diode, + e_Tutorial_Hint_Item_Cookie, + e_Tutorial_Hint_Item_Map, + e_Tutorial_Hint_Item_Record, + + e_Tutorial_Hint_White_Stone, + e_Tutorial_Hint_Dragon_Egg, + e_Tutorial_Hint_RedstoneLamp, + e_Tutorial_Hint_Cocoa, + + e_Tutorial_Hint_EmeraldOre, + e_Tutorial_Hint_EmeraldBlock, + e_Tutorial_Hint_EnderChest, + e_Tutorial_Hint_TripwireSource, + e_Tutorial_Hint_Tripwire, + e_Tutorial_Hint_CobblestoneWall, + e_Tutorial_Hint_Flowerpot, + e_Tutorial_Hint_Anvil, + e_Tutorial_Hint_QuartzOre, + e_Tutorial_Hint_QuartzBlock, + e_Tutorial_Hint_WoolCarpet, + + e_Tutorial_Hint_Potato, + e_Tutorial_Hint_Carrot, + + e_Tutorial_Hint_Item_Unused_18, + e_Tutorial_Hint_Item_Unused_19, + e_Tutorial_Hint_Item_Unused_20, + + e_Tutorial_Hint_Item_Max, +}; + +// We store the first time that we complete these tasks to be used in telemetry +enum eTutorial_Telemetry +{ + eTutorial_Telemetry_None = e_Tutorial_Hint_Item_Max, + + eTutorial_Telemetry_TrialStart, + eTutorial_Telemetry_Halfway, + eTutorial_Telemetry_Complete, + + eTutorial_Telemetry_Unused_1, + eTutorial_Telemetry_Unused_2, + eTutorial_Telemetry_Unused_3, + eTutorial_Telemetry_Unused_4, + eTutorial_Telemetry_Unused_5, + eTutorial_Telemetry_Unused_6, + eTutorial_Telemetry_Unused_7, + eTutorial_Telemetry_Unused_8, + eTutorial_Telemetry_Unused_9, + eTutorial_Telemetry_Unused_10, +}; + +enum eTutorial_CompletionAction +{ + e_Tutorial_Completion_None, + e_Tutorial_Completion_Complete_State, // This will make the current tutorial state complete + e_Tutorial_Completion_Complete_State_Gameplay_Constraints, // This will make the current tutorial state complete, and move the delayed constraints to the gameplay state + e_Tutorial_Completion_Jump_To_Last_Task, +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialHint.cpp b/Minecraft.Client/Common/Tutorial/TutorialHint.cpp new file mode 100644 index 00000000..5f0808bf --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialHint.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "Tutorial.h" +#include "TutorialHint.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" + +TutorialHint::TutorialHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, eHintType type, bool allowFade /*= true*/) + : m_id( id ), m_tutorial(tutorial), m_descriptionId( descriptionId ), m_type( type ), m_counter( 0 ), + m_lastTile( NULL ), m_hintNeeded( true ), m_allowFade(allowFade) +{ + tutorial->addMessage(descriptionId, type != e_Hint_NoIngredients); +} + +int TutorialHint::startDestroyBlock(shared_ptr item, Tile *tile) +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_HoldToMine: + if( tile == m_lastTile && m_hintNeeded ) + { + ++m_counter; + if(m_counter > TUTORIAL_HINT_MAX_MINE_REPEATS) + { + returnVal = m_descriptionId; + } + } + else + { + m_counter = 0; + } + m_lastTile = tile; + break; + default: + break; + } + + return returnVal; +} + +int TutorialHint::destroyBlock(Tile *tile) +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_HoldToMine: + if(tile == m_lastTile && m_counter > 0) + { + m_hintNeeded = false; + } + break; + default: + break; + } + + return returnVal; +} + +int TutorialHint::attack(shared_ptr item, shared_ptr entity) +{ + /* + switch(m_type) + { + default: + return -1; + } + */ + return -1; +} + +int TutorialHint::createItemSelected(shared_ptr item, bool canMake) +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_NoIngredients: + if(!canMake) + returnVal = m_descriptionId; + break; + default: + break; + } + return returnVal; +} + +int TutorialHint::itemDamaged(shared_ptr item) +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_ToolDamaged: + returnVal = m_descriptionId; + break; + default: + break; + } + return returnVal; +} + +bool TutorialHint::onTake( shared_ptr item ) +{ + return false; +} + +bool TutorialHint::onLookAt(int id, int iData) +{ + return false; +} + +bool TutorialHint::onLookAtEntity(eINSTANCEOF type) +{ + return false; +} + +int TutorialHint::tick() +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_SwimUp: + if( Minecraft::GetInstance()->localplayers[m_tutorial->getPad()]->isUnderLiquid(Material::water) ) returnVal = m_descriptionId; + break; + } + return returnVal; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialHint.h b/Minecraft.Client/Common/Tutorial/TutorialHint.h new file mode 100644 index 00000000..8ca543cc --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialHint.h @@ -0,0 +1,53 @@ +#pragma once +using namespace std; + +#include "TutorialEnum.h" + +#define TUTORIAL_HINT_MAX_MINE_REPEATS 20 + +class Level; +class Tutorial; + +class TutorialHint +{ +public: + enum eHintType + { + e_Hint_DiggerItem, + e_Hint_HoldToMine, + e_Hint_NoIngredients, + e_Hint_ToolDamaged, + e_Hint_TakeItem, + e_Hint_Area, + e_Hint_LookAtTile, + e_Hint_LookAtEntity, + e_Hint_SwimUp, + }; + +protected: + eHintType m_type; + int m_descriptionId; + Tutorial *m_tutorial; + eTutorial_Hint m_id; + + int m_counter; + Tile *m_lastTile; + bool m_hintNeeded; + bool m_allowFade; + +public: + TutorialHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, eHintType type, bool allowFade = true); + + eTutorial_Hint getId() { return m_id; } + + virtual int startDestroyBlock(shared_ptr item, Tile *tile); + virtual int destroyBlock(Tile *tile); + virtual int attack(shared_ptr item, shared_ptr entity); + virtual int createItemSelected(shared_ptr item, bool canMake); + virtual int itemDamaged(shared_ptr item); + virtual bool onTake( shared_ptr item ); + virtual bool onLookAt(int id, int iData=0); + virtual bool onLookAtEntity(eINSTANCEOF type); + virtual int tick(); + virtual bool allowFade() { return m_allowFade; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialHints.h b/Minecraft.Client/Common/Tutorial/TutorialHints.h new file mode 100644 index 00000000..5c7381ab --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialHints.h @@ -0,0 +1,7 @@ +#pragma once + +#include "AreaHint.h" +#include "DiggerItemHint.h" +#include "LookAtTileHint.h" +#include "TakeItemHint.h" +#include "LookAtEntityHint.h" \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialMessage.cpp b/Minecraft.Client/Common/Tutorial/TutorialMessage.cpp new file mode 100644 index 00000000..1f007035 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialMessage.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "TutorialMessage.h" + +TutorialMessage::TutorialMessage(int messageId, bool limitRepeats /*= false*/, unsigned char numRepeats /*= TUTORIAL_MESSAGE_DEFAULT_SHOW*/) + : messageId( messageId ), limitRepeats( limitRepeats ), numRepeats( numRepeats ), timesShown( 0 ) +{ +} + +bool TutorialMessage::canDisplay() +{ + return !limitRepeats || (timesShown < numRepeats); +} + +LPCWSTR TutorialMessage::getMessageForDisplay() +{ + if(!canDisplay()) + return L""; + + if(limitRepeats) + ++timesShown; + + return app.GetString( messageId ); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialMessage.h b/Minecraft.Client/Common/Tutorial/TutorialMessage.h new file mode 100644 index 00000000..6a0b4d46 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialMessage.h @@ -0,0 +1,20 @@ +#pragma once + +// The default number of times any message should be shown +#define TUTORIAL_MESSAGE_DEFAULT_SHOW 3 + +class TutorialMessage +{ +private: + int messageId; + bool limitRepeats; + unsigned char numRepeats; + unsigned char timesShown; + DWORD lastDisplayed; + +public: + TutorialMessage(int messageId, bool limitRepeats = false, unsigned char numRepeats = TUTORIAL_MESSAGE_DEFAULT_SHOW); + + bool canDisplay(); + LPCWSTR getMessageForDisplay(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialMode.cpp b/Minecraft.Client/Common/Tutorial/TutorialMode.cpp new file mode 100644 index 00000000..82c81598 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialMode.cpp @@ -0,0 +1,124 @@ +#include "stdafx.h" +#include +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\MultiPlayerLevel.h" +#include "..\..\..\Minecraft.World\Inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "TutorialMode.h" + +TutorialMode::TutorialMode(int iPad, Minecraft *minecraft, ClientConnection *connection) : MultiPlayerGameMode( minecraft, connection ), m_iPad( iPad ) +{ +} + +TutorialMode::~TutorialMode() +{ + if(tutorial != NULL) + delete tutorial; +} + +void TutorialMode::startDestroyBlock(int x, int y, int z, int face) +{ + if(!tutorial->m_allTutorialsComplete) + { + int t = minecraft->level->getTile(x, y, z); + tutorial->startDestroyBlock(minecraft->player->inventory->getSelected(), Tile::tiles[t]); + } + MultiPlayerGameMode::startDestroyBlock( x, y, z, face ); +} + +bool TutorialMode::destroyBlock(int x, int y, int z, int face) +{ + if(!tutorial->m_allTutorialsComplete) + { + int t = minecraft->level->getTile(x, y, z); + tutorial->destroyBlock(Tile::tiles[t]); + } + shared_ptr item = minecraft->player->getSelectedItem(); + int damageBefore; + if(item != NULL) + { + damageBefore = item->getDamageValue(); + } + bool changed = MultiPlayerGameMode::destroyBlock( x, y, z, face ); + + if(!tutorial->m_allTutorialsComplete) + { + if ( item != NULL && item->isDamageableItem() ) + { + int max = item->getMaxDamage(); + int damageNow = item->getDamageValue(); + + if(damageNow > damageBefore && damageNow > (max/2) ) + { + tutorial->itemDamaged( item ); + } + } + } + + return changed; +} + +void TutorialMode::tick() +{ + MultiPlayerGameMode::tick(); + + if(!tutorial->m_allTutorialsComplete) + tutorial->tick(); + + /* + if( tutorial.m_allTutorialsComplete && (tutorial.lastMessageTime + m_iTutorialDisplayMessageTime) < GetTickCount() ) + { + // Exit tutorial + minecraft->gameMode = new SurvivalMode( this ); + delete this; + } + */ +} + +bool TutorialMode::useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, Vec3 *hit, bool bTestUseOnly, bool *pbUsedItem) +{ + bool haveItem = false; + int itemCount = 0; + if(!tutorial->m_allTutorialsComplete) + { + tutorial->useItemOn(level, item, x, y, z, bTestUseOnly); + + if(!bTestUseOnly) + { + if(item != NULL) + { + haveItem = true; + itemCount = item->count; + } + } + } + bool result = MultiPlayerGameMode::useItemOn( player, level, item, x, y, z, face, hit, bTestUseOnly, pbUsedItem ); + + if(!bTestUseOnly) + { + if(!tutorial->m_allTutorialsComplete) + { + if( result && haveItem && itemCount > item->count ) + { + tutorial->useItemOn(item); + } + } + } + return result; +} + +void TutorialMode::attack(shared_ptr player, shared_ptr entity) +{ + if(!tutorial->m_allTutorialsComplete) + tutorial->attack(player, entity); + + MultiPlayerGameMode::attack( player, entity ); +} + +bool TutorialMode::isInputAllowed(int mapping) +{ + return tutorial->m_allTutorialsComplete || tutorial->isInputAllowed( mapping ); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialMode.h b/Minecraft.Client/Common/Tutorial/TutorialMode.h new file mode 100644 index 00000000..75e24edf --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialMode.h @@ -0,0 +1,28 @@ +#pragma once +using namespace std; + +#include "..\..\MultiPlayerGameMode.h" +#include "Tutorial.h" + +class TutorialMode : public MultiPlayerGameMode +{ +protected: + Tutorial *tutorial; + int m_iPad; + + // Function to make this an abstract class + virtual bool isImplemented() = 0; +public: + TutorialMode(int iPad, Minecraft *minecraft, ClientConnection *connection); + virtual ~TutorialMode(); + + virtual void startDestroyBlock(int x, int y, int z, int face); + virtual bool destroyBlock(int x, int y, int z, int face); + virtual void tick(); + virtual bool useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, Vec3 *hit, bool bTestUseOnly=false, bool *pbUsedItem=NULL); + virtual void attack(shared_ptr player, shared_ptr entity); + + virtual bool isInputAllowed(int mapping); + + Tutorial *getTutorial() { return tutorial; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialTask.cpp b/Minecraft.Client/Common/Tutorial/TutorialTask.cpp new file mode 100644 index 00000000..2251ab07 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialTask.cpp @@ -0,0 +1,78 @@ +#include "stdafx.h" +#include "Tutorial.h" +#include "TutorialConstraints.h" +#include "TutorialTask.h" + +TutorialTask::TutorialTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, vector *inConstraints, + bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders) + : tutorial( tutorial ), descriptionId( descriptionId ), m_promptId( -1 ), enablePreCompletion( enablePreCompletion ), + areConstraintsEnabled( false ), bIsCompleted( false ), bHasBeenActivated( false ), + m_bAllowFade(bAllowFade), m_bTaskReminders(bTaskReminders), m_bShowMinimumTime( bShowMinimumTime), m_bShownForMinimumTime( false ) +{ + if(inConstraints != NULL) + { + for(AUTO_VAR(it, inConstraints->begin()); it < inConstraints->end(); ++it) + { + TutorialConstraint *constraint = *it; + constraints.push_back( constraint ); + } + delete inConstraints; + } + + tutorial->addMessage(descriptionId); +} + +TutorialTask::~TutorialTask() +{ + enableConstraints(false); + + for(AUTO_VAR(it, constraints.begin()); it < constraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + + if( constraint->getQueuedForRemoval() ) + { + constraint->setDeleteOnDeactivate(true); + } + else + { + delete constraint; + } + } +} + +void TutorialTask::taskCompleted() +{ + if( areConstraintsEnabled == true ) + enableConstraints( false ); +} + +void TutorialTask::enableConstraints(bool enable, bool delayRemove /*= false*/) +{ + if( !enable && (areConstraintsEnabled || !delayRemove) ) + { + // Remove + for(AUTO_VAR(it, constraints.begin()); it != constraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + //app.DebugPrintf(">>>>>>>> %i\n", constraints.size()); + tutorial->RemoveConstraint( constraint, delayRemove ); + } + areConstraintsEnabled = false; + } + else if( !areConstraintsEnabled && enable ) + { + // Add + for(AUTO_VAR(it, constraints.begin()); it != constraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + tutorial->AddConstraint( constraint ); + } + areConstraintsEnabled = true; + } +} + +void TutorialTask::setAsCurrentTask(bool active /*= true*/) +{ + bHasBeenActivated = active; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialTask.h b/Minecraft.Client/Common/Tutorial/TutorialTask.h new file mode 100644 index 00000000..92cb5999 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialTask.h @@ -0,0 +1,63 @@ +#pragma once +using namespace std; +#include "TutorialEnum.h" + +class Level; +class Tutorial; +class TutorialConstraint; +class MobEffect; + +// A class that represents each individual task in the tutorial. +// +// Members: +// enablePreCompletion - If this is true, then the player can complete this task out of sequence. +// This stops us asking them to do things they have already done +// constraints - A list of constraints which can be activated (as a whole). +// If they are active, then the constraints are removed when the task is completed +// areConstraintsEnabled- A flag which records whether or not we have added the constraints to the tutorial +class TutorialTask +{ +protected: + int descriptionId; + int m_promptId; + Tutorial *tutorial; + bool enablePreCompletion; + bool bHasBeenActivated; + bool m_bAllowFade; + bool m_bTaskReminders; + bool m_bShowMinimumTime; + +protected: + bool bIsCompleted; + bool m_bShownForMinimumTime; + vector constraints; + bool areConstraintsEnabled; +public: + TutorialTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, vector *inConstraints, bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ); + virtual ~TutorialTask(); + + virtual int getDescriptionId() { return descriptionId; } + virtual int getPromptId() { return m_promptId; } + + virtual bool isCompleted() = 0; + virtual eTutorial_CompletionAction getCompletionAction() { return e_Tutorial_Completion_None; } + virtual bool isPreCompletionEnabled() { return enablePreCompletion; } + virtual void taskCompleted(); + virtual void enableConstraints(bool enable, bool delayRemove = false); + virtual void setAsCurrentTask(bool active = true); + + virtual void setShownForMinimumTime() { m_bShownForMinimumTime = true; } + virtual bool hasBeenActivated() { return bHasBeenActivated; } + virtual bool AllowFade() { return m_bAllowFade;} + bool TaskReminders() { return m_bTaskReminders;} + virtual bool ShowMinimumTime() { return m_bShowMinimumTime;} + + virtual void useItemOn(Level *level, shared_ptr item, int x, int y, int z, bool bTestUseOnly=false) { } + virtual void useItem(shared_ptr item,bool bTestUseOnly=false) { } + virtual void completeUsingItem(shared_ptr item) { } + virtual void handleUIInput(int iAction) { } + virtual void onCrafted(shared_ptr item) { } + virtual void onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux) { } + virtual void onStateChange(eTutorial_State newState) { } + virtual void onEffectChanged(MobEffect *effect, bool bRemoved=false) { } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialTasks.h b/Minecraft.Client/Common/Tutorial/TutorialTasks.h new file mode 100644 index 00000000..591e9564 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialTasks.h @@ -0,0 +1,16 @@ +#include "StatTask.h" +#include "CraftTask.h" +#include "PickupTask.h" +#include "UseTileTask.h" +#include "UseItemTask.h" +#include "InfoTask.h" +#include "ControllerTask.h" +#include "ProcedureCompoundTask.h" +#include "XuiCraftingTask.h" +#include "StateChangeTask.h" +#include "ChoiceTask.h" +#include "FullTutorialActiveTask.h" +#include "AreaTask.h" +#include "ProgressFlagTask.h" +#include "CompleteUsingItemTask.h" +#include "EffectChangedTask.h" \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/UseItemTask.cpp b/Minecraft.Client/Common/Tutorial/UseItemTask.cpp new file mode 100644 index 00000000..09bac4d1 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/UseItemTask.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" +#include "UseItemTask.h" + +UseItemTask::UseItemTask(const int itemId, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion, vector *inConstraints, bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, bTaskReminders ), + itemId( itemId ) +{ +} + +bool UseItemTask::isCompleted() +{ + return bIsCompleted; +} + +void UseItemTask::useItem(shared_ptr item,bool bTestUseOnly) +{ + if(bTestUseOnly) return; + + if( item->id == itemId ) + bIsCompleted = true; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/UseItemTask.h b/Minecraft.Client/Common/Tutorial/UseItemTask.h new file mode 100644 index 00000000..46d71be4 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/UseItemTask.h @@ -0,0 +1,20 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class Level; + +// 4J Stu - Tasks that involve placing a tile +class UseItemTask : public TutorialTask +{ +private: + const int itemId; + bool completed; + +public: + UseItemTask(const int itemId, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion = false, vector *inConstraints = NULL, bool bShowMinimumTime = false, bool bAllowFade = true, bool bTaskReminders = true ); + virtual bool isCompleted(); + virtual void useItem(shared_ptr item, bool bTestUseOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/UseTileTask.cpp b/Minecraft.Client/Common/Tutorial/UseTileTask.cpp new file mode 100644 index 00000000..1f4ed4cb --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/UseTileTask.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" +#include "UseTileTask.h" + +UseTileTask::UseTileTask(const int tileId, int x, int y, int z, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion, vector *inConstraints, bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, bTaskReminders ), + x( x ), y( y ), z( z ), tileId( tileId ) +{ + useLocation = true; +} + +UseTileTask::UseTileTask(const int tileId, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion, vector *inConstraints, bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, bTaskReminders ), + tileId( tileId ) +{ + useLocation = false; +} + +bool UseTileTask::isCompleted() +{ + return bIsCompleted; +} + +void UseTileTask::useItemOn(Level *level, shared_ptr item, int x, int y, int z,bool bTestUseOnly) +{ + if(bTestUseOnly) return; + + if( !enablePreCompletion && !bHasBeenActivated) return; + + if( !useLocation || ( x == this->x && y == this->y && z == this->z ) ) + { + int t = level->getTile(x, y, z); + if( t == tileId ) + bIsCompleted = true; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/UseTileTask.h b/Minecraft.Client/Common/Tutorial/UseTileTask.h new file mode 100644 index 00000000..74b3a40c --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/UseTileTask.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class Level; + +// 4J Stu - Tasks that involve using a tile, with or without an item. e.g. Opening a chest +class UseTileTask : public TutorialTask +{ +private: + int x,y,z; + const int tileId; + bool useLocation; + bool completed; + +public: + UseTileTask(const int tileId, int x, int y, int z, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion = false, vector *inConstraints = NULL, bool bShowMinimumTime = false, bool bAllowFade = true, bool bTaskReminders = true ); + UseTileTask(const int tileId, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion = false, vector *inConstraints = NULL, bool bShowMinimumTime = false, bool bAllowFade = true, bool bTaskReminders = true); + virtual bool isCompleted(); + virtual void useItemOn(Level *level, shared_ptr item, int x, int y, int z, bool bTestUseOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/XuiCraftingTask.cpp b/Minecraft.Client/Common/Tutorial/XuiCraftingTask.cpp new file mode 100644 index 00000000..71b88479 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/XuiCraftingTask.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" +#if !(defined _XBOX) && !(defined __PSVITA__) +#include "..\UI\UI.h" +#endif +#include "Tutorial.h" +#include "XuiCraftingTask.h" + +bool XuiCraftingTask::isCompleted() +{ +#ifndef __PSVITA__ + // This doesn't seem to work + //IUIScene_CraftingMenu *craftScene = reinterpret_cast(tutorial->getScene()); +#ifdef _XBOX + CXuiSceneCraftingPanel *craftScene = (CXuiSceneCraftingPanel *)(tutorial->getScene()); +#else + UIScene_CraftingMenu *craftScene = reinterpret_cast(tutorial->getScene()); +#endif + + bool completed = false; + + switch(m_type) + { + case e_Crafting_SelectGroup: + if(craftScene != NULL && craftScene->getCurrentGroup() == m_group) + { + completed = true; + } + break; + case e_Crafting_SelectItem: + if(craftScene != NULL && craftScene->isItemSelected(m_item)) + { + completed = true; + } + break; + } + + return completed; +#else + return true; +#endif +} diff --git a/Minecraft.Client/Common/Tutorial/XuiCraftingTask.h b/Minecraft.Client/Common/Tutorial/XuiCraftingTask.h new file mode 100644 index 00000000..2dc48709 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/XuiCraftingTask.h @@ -0,0 +1,36 @@ +#pragma once +#include "TutorialTask.h" +#include "..\..\..\Minecraft.World\Recipy.h" + +class XuiCraftingTask : public TutorialTask +{ +public: + enum eCraftingTaskType + { + e_Crafting_SelectGroup, + e_Crafting_SelectItem, + }; + + // Select group + XuiCraftingTask(Tutorial *tutorial, int descriptionId, Recipy::_eGroupType groupToSelect, bool enablePreCompletion = false, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_group(groupToSelect), + m_type( e_Crafting_SelectGroup ) + {} + + // Select Item + XuiCraftingTask(Tutorial *tutorial, int descriptionId, int itemId, bool enablePreCompletion = false, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_item(itemId), + m_type( e_Crafting_SelectItem ) + {} + + virtual bool isCompleted(); + +private: + eCraftingTaskType m_type; + Recipy::_eGroupType m_group; + int m_item; +}; \ No newline at end of file -- cgit v1.2.3