- Added TutorialMessage ability

-- Tutorial Messages are an ability like any other, except it can only be displayed once. Subsequent calls are ignored, the ability is removed from the game as soon as it is added
-- This allows to add event triggered messages ingame. Messages are either text, or images (I don't have an image sample, but rules/classic.txt has a few examples that might help)
-- only tested on Windows, although I made sure the PSP version compiles. Hopefully I also made the necessary for it to work in the touch version (touching the screen should be enough to close the tuto message)
-- Room for improvement: possibility to choose a title in text mode, possibility to have some messages depending on others (e.g.: don't show message X until message Y has been shown), improve some of the abilities and triggers to give more flexibility, add events outside of game, to allow tuto messages in deck creator, etc...
This commit is contained in:
wagic.the.homebrew
2011-07-03 08:47:51 +00:00
parent 72c795c24b
commit 52b83a135c
25 changed files with 656 additions and 56 deletions

View File

@@ -8,4 +8,4 @@
# project structure. # project structure.
# Project target. # Project target.
target=Samsung Electronics Co., Ltd.:GALAXY Tab Addon:8 target=android-7

View File

@@ -80,6 +80,7 @@ LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.cpp \
$(MTG_PATH)/src/GuiPhaseBar.cpp \ $(MTG_PATH)/src/GuiPhaseBar.cpp \
$(MTG_PATH)/src/GuiPlay.cpp \ $(MTG_PATH)/src/GuiPlay.cpp \
$(MTG_PATH)/src/GuiStatic.cpp \ $(MTG_PATH)/src/GuiStatic.cpp \
$(MTG_PATH)/src/IconButton.cpp \
$(MTG_PATH)/src/ManaCost.cpp \ $(MTG_PATH)/src/ManaCost.cpp \
$(MTG_PATH)/src/ManaCostHybrid.cpp \ $(MTG_PATH)/src/ManaCostHybrid.cpp \
$(MTG_PATH)/src/MenuItem.cpp \ $(MTG_PATH)/src/MenuItem.cpp \

View File

@@ -1,4 +1,4 @@
OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIHints.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/AllAbilities.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/CardSelectorSingleton.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckEditorMenu.o objs/DeckMenu.o objs/DeckMenuItem.o objs/DeckMetaData.o objs/DeckStats.o objs/DuelLayers.o objs/Effects.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameState.o objs/GameStateAwards.o objs/GameStateDeckViewer.o objs/GameStateDuel.o objs/DeckManager.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GameStateStory.o objs/GameStateTransitions.o objs/GuiAvatars.o objs/GuiBackground.o objs/GuiCardsController.o objs/GuiCombat.o objs/GuiFrame.o objs/GuiHand.o objs/GuiLayers.o objs/GuiMana.o objs/GuiPhaseBar.o objs/GuiPlay.o objs/GuiStatic.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/ModRules.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/MTGPack.o objs/MTGRules.o objs/Navigator.o objs/ObjectAnalytics.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/PlayRestrictions.o objs/Pos.o objs/PrecompiledHeader.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/SimplePopup.o objs/StoryFlow.o objs/StyleManager.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/ThisDescriptor.o objs/Token.o objs/Translate.o objs/TranslateKeys.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/WDataSrc.o objs/WGui.o objs/WFilter.o objs/Tasks.o objs/WFont.o OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIHints.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/AllAbilities.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/CardSelectorSingleton.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckEditorMenu.o objs/DeckMenu.o objs/DeckMenuItem.o objs/DeckMetaData.o objs/DeckStats.o objs/DuelLayers.o objs/Effects.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameState.o objs/GameStateAwards.o objs/GameStateDeckViewer.o objs/GameStateDuel.o objs/DeckManager.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GameStateStory.o objs/GameStateTransitions.o objs/GuiAvatars.o objs/GuiBackground.o objs/GuiCardsController.o objs/GuiCombat.o objs/GuiFrame.o objs/GuiHand.o objs/GuiLayers.o objs/GuiMana.o objs/GuiPhaseBar.o objs/GuiPlay.o objs/GuiStatic.o objs/IconButton.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/ModRules.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/MTGPack.o objs/MTGRules.o objs/Navigator.o objs/ObjectAnalytics.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/PlayRestrictions.o objs/Pos.o objs/PrecompiledHeader.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/SimplePopup.o objs/StoryFlow.o objs/StyleManager.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/ThisDescriptor.o objs/Token.o objs/Translate.o objs/TranslateKeys.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/WDataSrc.o objs/WGui.o objs/WFilter.o objs/Tasks.o objs/WFont.o
DEPS = $(patsubst objs/%.o, deps/%.d, $(OBJS)) DEPS = $(patsubst objs/%.o, deps/%.d, $(OBJS))
RESULT = $(shell psp-config --psp-prefix 2> Makefile.cache) RESULT = $(shell psp-config --psp-prefix 2> Makefile.cache)

View File

@@ -0,0 +1,38 @@
include mtg.txt
name=Classic
[INIT]
mode=mtg
[PLAYER1]
#Tutorial is an ability that works like all others, but it is only activated once for a given player
#Therefore triggering it with somthing such as @each... will actually only work once
auto=tutorial(Welcome to Wagic!\n\nIn Wagic, you play as a wizard against the computer.\nYour goal is to kill your opponent with your spells and Creatures,\nby reducing your opponent's life to 0 or less.)
auto=tutorial(Wagic is divided in turns:one turn for you, one turn\nfor your opponent, and so on.\nEach turn is divided into phases, that we will explain.\n\nNote: If at any time you feel you are blocked\nand cannot play any card,\npress the "next phase" button\n-- LTrigger, Right click, or swipe up by default --\nto move to the next phase, until you reach the opponent's turn)
auto=tutorial(This is your turn, and we are in the first "Main Phase".\nThis is basically the phase in which you will play your spells.\n\nNote: In General, you cannot\nplay spells during your opponent's turn,\nbut there are of course exceptions to the rule.)
auto=tutorial(Your hand -- by default, shown on the right side of the screen --\n is where you will find your spells\n\nIn order to play spells and Creatures, you need mana.\n\nMana is usually obtained by putting Lands into play.)
auto=tutorial(If you have some "Land" cards in your hand,\ntry to click on one of them,\nand put it into play!\n\nNote:If you don't have any land, go to your opponent's turn by\npressing "next phase" several times\n-- LTrigger, Right click, or swipe up by default --)
auto=@movedTo(Land|myBattlefield):tutorial(Now that you have a Land into play,you can use it to get mana.\nMana is used to cast spells, but each spell has a cost,\nrepresented on the top right of the card.\n\nMana wears out at the end of each game phase,\nso if you want to cast a spell that costs 2 green mana,\nyou will generally need at least 2 forests in play.)
auto=@movedTo(Land|myBattlefield):tutorial(Once you have enough lands in play to cast your first spell,\nactivate the lands - click on them - then click on the spell.\n\nImportant note:\nLands don't require any mana to be played,\nbut you can only play one land per turn)
auto=@movedTo(Land|myBattlefield):tutorial(If you don't have enough lands in play to cast a spell,\nclick on the "next phase" button until you\nreach your opponent's turn.\n "next phase" is LTrigger, right click, or Swipe up by default)
auto=@each my draw:tutorial(Each of your turns, at the "Draw" phase,\nyou will draw one card from your deck into your hand.\n\nSome spells also allow you to draw additional cards!\n\nIf you get to draw a card but your deck is empty,\nyou lose the game, so be careful!)
auto=@movedTo(creature|myBattlefield):tutorial(Congratulations, you played your first creature!\n\nCreatures are used to attack your opponent\nduring your attacker's phase\n,and to protect you during your opponent's combat phase.\n\nCreatures are attached to two numbers P/T,\n called Power an toughness.\nPower is how much damage your creature can do,\ntoughness is how much damage it can take every turn)
auto=@movedTo(creature|myBattlefield):tutorial(Creatures can usually not attack\nduring the turn they came into play,\nyou have to wait an additional turn before you can\n"activate" them or attack with them.\nThis is known as "summoning sickness")
auto=@movedTo(creature|myBattlefield):tutorial(Some creatures have special abilities that\nare described on the card's text.\nThese abilities might require some mana to be activated,\nwhich is also described on the creature's text)
auto=@lifeloss(opponent):tutorial(You did some damage to your opponent.\nKeep doing this until their life goes to 0 or below,\nand victory is yours! )
auto=@lifeloss(controller):tutorial(You lost some life!\nBe careful to not go down to 0,\nor you will lose the game!))
auto=@each my combatattackers:tutorial(Attackers phase:\nThis is where you choose which of your creatures will\nattack your opponent.\n\nSelecting attacking creatures is done by clicking\non each creature you want to set as an attacker.\n\nCreatures usually can't attack on the turn they came into play,\nthis is known as "summoning sickness)
#Below doesn't work
#auto=@each my combatattackers:aslongas(creature|myBattlefield) tutorial(It seems you have creatures that can attack this turn\n, maybe you can run an offensive...)
auto=@movedTo(*|opponentStack):tutorial(When the computer casts a spell,\nthe card being casted is shown to you,\nand you can click on the action button to move on)
#this would work too and display a graphics file assuming there's a file in graphics/tutorials/tuto1.png
# Graphics files should be at most 480x272, 480x250 suggested
#auto=tutorial(tutorials/tuto1.png)

View File

@@ -1,5 +1,5 @@
#If you change this file, be sure to report your changes into testsuite.txt! #If you change this file, be sure to report your changes into testsuite.txt!
name=Classic hidden
[INIT] [INIT]
mode=mtg mode=mtg
[PLAYERS] [PLAYERS]

Binary file not shown.

Binary file not shown.

View File

@@ -18,6 +18,7 @@
#include "ThisDescriptor.h" #include "ThisDescriptor.h"
#include <JGui.h> #include <JGui.h>
#include <hge/hgeparticle.h> #include <hge/hgeparticle.h>
#include "IconButton.h"
#include <map> #include <map>
using std::map; using std::map;
@@ -829,6 +830,38 @@ public:
return a; return a;
} }
}; };
//Tutorial Messaging
class ATutorialMessage: public MTGAbility, public IconButtonsController
{
public:
string mMessage;
float mElapsed, mSH, mSW;
JTexture * mBgTex;
JQuad * mBg[9];
bool mUserCloseRequest, mDontShow;
bool mIsImage;
ATutorialMessage(MTGCardInstance * source, string message);
void Update(float dt);
bool CheckUserInput(JButton key);
void Render();
string getOptionName();
bool alreadyShown();
ATutorialMessage * clone() const;
~ATutorialMessage();
//JGuiListener Implementation
void ButtonPressed(int controllerId, int controlId);
static ATutorialMessage * Current;
};
//counters //counters
class AACounter: public ActivatedAbility class AACounter: public ActivatedAbility
{ {

View File

@@ -352,13 +352,15 @@ public:
int load(); int load();
GameOption * get(int); GameOption * get(int);
GameOption * get(string optionName);
GameOption& operator[](int); GameOption& operator[](int);
GameOption& operator[](string);
GameOptions(string filename); GameOptions(string filename);
~GameOptions(); ~GameOptions();
private: private:
vector<GameOption*> values; vector<GameOption*> values;
vector<string> unknown; map<string,GameOption*> unknownMap;
}; };
class GameSettings class GameSettings
@@ -408,6 +410,7 @@ public:
GameOption* get(int); GameOption* get(int);
GameOption& operator[](int); GameOption& operator[](int);
GameOption& operator[](string);
GameOptions* profileOptions; GameOptions* profileOptions;
GameOptions* globalOptions; GameOptions* globalOptions;

View File

@@ -0,0 +1,50 @@
#ifndef _ICONBUTTON_H
#define _ICONBUTTON_H
#include <string>
#include <JGui.h>
using std::string;
class IconButtonsController: public JGuiController, public JGuiListener
{
public:
float mX;
float mY;
IconButtonsController(float x, float y);
void SetColor(PIXEL_TYPE color);
};
class IconButton: public JGuiObject
{
private:
PIXEL_TYPE mColor;
IconButtonsController * mParent;
bool mHasFocus;
int mFontId;
string mText;
float mScale, mCurrentScale, mTargetScale;
float mX, mY;
float mTextRelativeX, mTextRelativeY;
JTexture * mTex;
JQuad * mQuad;
public:
IconButton(int id, IconButtonsController * parent, string texture, float x, float y, float scale, int fontId, string text, float textRelativeX, float textRelativeY, bool hasFocus = false);
IconButton(int id, IconButtonsController * parent, JQuad * quad, float x, float y, float scale, int fontId, string text, float textRelativeX, float textRelativeY, bool hasFocus = false);
void init(IconButtonsController * parent, JQuad * quad, float x, float y, float scale, int fontId, string text, float textRelativeX, float textRelativeY, bool hasFocus);
~IconButton();
ostream& toString(ostream& out) const;
bool hasFocus();
virtual void Render();
virtual void Update(float dt);
virtual void Entering();
virtual bool Leaving(JButton key);
virtual bool ButtonPressed();
void SetColor(PIXEL_TYPE color);
};
#endif

View File

@@ -52,7 +52,7 @@ public:
Task(char _type = ' '); Task(char _type = ' ');
static Task* createFromStr(string params, bool rand = false); static Task* createFromStr(const string params, bool rand = false);
virtual string toString(); virtual string toString();
string getDesc(); string getDesc();
virtual string createDesc() = 0; virtual string createDesc() = 0;

View File

@@ -100,6 +100,9 @@ std::vector<std::string> parseBetween(const std::string& s, string start, string
std::string wordWrap(const std::string& s, float width, int fontId); std::string wordWrap(const std::string& s, float width, int fontId);
//basic hash function
unsigned long hash_djb2(const char *str);
int loadRandValues(string s); int loadRandValues(string s);
int filesize(const char * filename); int filesize(const char * filename);
int fileExists(const char * filename); int fileExists(const char * filename);

View File

@@ -15,6 +15,7 @@ The Action Stack contains all information for Game Events that can be interrupte
#include "Translate.h" #include "Translate.h"
#include "WResourceManager.h" #include "WResourceManager.h"
#include "ModRules.h" #include "ModRules.h"
#include "AllAbilities.h"
#include <typeinfo> #include <typeinfo>
@@ -822,6 +823,12 @@ int ActionStack::receiveEventPlus(WEvent * event)
void ActionStack::Update(float dt) void ActionStack::Update(float dt)
{ {
//This is a hack to avoid updating the stack while tuto messages are being shown
//Ideally, the tuto messages should be moved to a layer above this one
if (ATutorialMessage::Current)
return;
askIfWishesToInterrupt = NULL; askIfWishesToInterrupt = NULL;
//modal = 0; //modal = 0;
GameObserver * game = GameObserver::GetInstance(); GameObserver * game = GameObserver::GetInstance();
@@ -1080,6 +1087,11 @@ void ActionStack::Fizzle(Interruptible * action)
void ActionStack::Render() void ActionStack::Render()
{ {
//This is a hack to avoid rendering the stack above the tuto messages
//Ideally, the tuto messages should be moved to a layer above this one
if (ATutorialMessage::Current)
return;
static const float kSpacer = 8; static const float kSpacer = 8;
static const float x0 = 250; static const float x0 = 250;
static const float y0 = 0; static const float y0 = 0;

View File

@@ -1,5 +1,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "AllAbilities.h" #include "AllAbilities.h"
#include "Translate.h"
#include <boost/algorithm/string.hpp>
//Activated Abilities //Activated Abilities
@@ -3573,6 +3575,285 @@ ABlinkGeneric::~ABlinkGeneric()
SAFE_DELETE(ability); SAFE_DELETE(ability);
} }
//Tutorial Messaging
ATutorialMessage * ATutorialMessage::Current = NULL;
ATutorialMessage::ATutorialMessage(MTGCardInstance * source, string message) : MTGAbility(0, source), IconButtonsController(0, 0)
{
mBgTex = NULL;
mElapsed = 0;
mIsImage = false;
for (int i = 0; i < 9; i++)
mBg[i] = NULL;
string gfx = WResourceManager::Instance()->graphicsFile(message);
if (fileExists(gfx.c_str()))
{
mIsImage = true;
mMessage = message;
}
else
{
mMessage = _(message); //translate directly here, remove this and translate at rendering time if it bites us
boost::replace_all(mMessage, "\\n", "\n");
}
if (mIsImage)
{
mX = SCREEN_WIDTH_F / 2;
mY = SCREEN_HEIGHT_F / 2;
}
else
{
mX = 0;
mY = -SCREEN_HEIGHT_F - 0.1f; //Offscreen
}
mDontShow = mUserCloseRequest = alreadyShown();
if(mDontShow)
forceDestroy = 1;
}
string ATutorialMessage::getOptionName()
{
std::stringstream out;
out << "tuto_";
out << hash_djb2(mMessage.c_str());
return out.str();
}
bool ATutorialMessage::alreadyShown()
{
return options[getOptionName()].number ? true : false;
}
bool ATutorialMessage::CheckUserInput(JButton key)
{
if (mUserCloseRequest) return false;
if(key == JGE_BTN_SEC || key == JGE_BTN_OK)
{
ButtonPressed(0, 1);
return true;
}
//Required for Mouse/touch input
IconButtonsController::CheckUserInput(key);
return true; //this ability is modal, so it catches all key events until it gets closed
}
void ATutorialMessage::Update(float dt)
{
if (!Current && !mDontShow)
Current = this;
if (Current != this)
return;
if (mUserCloseRequest && mY < -SCREEN_HEIGHT)
mDontShow = true;
if (mDontShow)
{
Current = NULL;
forceDestroy = 1;
return;
}
mElapsed += dt;
IconButtonsController::Update(dt);
if (mIsImage)
return;
//Below this only affects "text" mode
if (!mUserCloseRequest && mY < 0)
{
mY = -SCREEN_HEIGHT + (SCREEN_HEIGHT * mElapsed / 0.75f); //Todo: more physical drop-in.
if (mY >= 0)
mY = 0;
}
else if (mUserCloseRequest && mY > -SCREEN_HEIGHT)
{
mY = -(SCREEN_HEIGHT * mElapsed / 0.75f);
}
}
void ATutorialMessage::ButtonPressed(int controllerId, int controlId)
{
//TODO : cancel ALL tips/tutorials for JGE_BTN_SEC?
options[getOptionName()].number = 1;
options.save(); //TODO: if we experience I/O slowness in tutorials, move this save at the end of a turn, or at the end of the game.
mElapsed = 0;
mUserCloseRequest = true;
}
void ATutorialMessage::Render()
{
if (mDontShow)
return;
if (mY < -SCREEN_HEIGHT)
return;
if (!mBgTex)
{
if (mIsImage)
{
mBgTex = WResourceManager::Instance()->RetrieveTexture(mMessage, RETRIEVE_LOCK);
if (mBgTex)
{
mBg[0] = NEW JQuad(mBgTex, 0, 0, (float) mBgTex->mWidth, (float) mBgTex->mHeight);
mBg[0]->SetHotSpot(mBg[0]->mWidth / 2, mBg[0]->mHeight / 2);
//Continue Button
JQuadPtr quad = WResourceManager::Instance()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
quad->SetHotSpot(16, 16);
IconButton * iconButton = NEW IconButton(1, this, quad.get(), 0, mBg[0]->mHeight / 2, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
Add(iconButton);
}
if (options[Options::SFXVOLUME].number > 0)
{
JSample * sample = WResourceManager::Instance()->RetrieveSample("tutorial.wav");
if (sample)
JSoundSystem::GetInstance()->PlaySample(sample);
}
}
else
{
mBgTex = WResourceManager::Instance()->RetrieveTexture("taskboard.png", RETRIEVE_LOCK);
float unitH = static_cast<float> (mBgTex->mHeight / 4);
float unitW = static_cast<float> (mBgTex->mWidth / 4);
if (unitH == 0 || unitW == 0) return;
if (mBgTex)
{
mBg[0] = NEW JQuad(mBgTex, 0, 0, unitW, unitH);
mBg[1] = NEW JQuad(mBgTex, unitW, 0, unitW * 2, unitH);
mBg[2] = NEW JQuad(mBgTex, unitW * 3, 0, unitW, unitH);
mBg[3] = NEW JQuad(mBgTex, 0, unitH, unitW, unitH * 2);
mBg[4] = NEW JQuad(mBgTex, unitW, unitH, unitW * 2, unitH * 2);
mBg[5] = NEW JQuad(mBgTex, unitW * 3, unitH, unitW, unitH * 2);
mBg[6] = NEW JQuad(mBgTex, 0, unitH * 3, unitW, unitH);
mBg[7] = NEW JQuad(mBgTex, unitW, unitH * 3, unitW * 2, unitH);
mBg[8] = NEW JQuad(mBgTex, unitW * 3, unitH * 3, unitW, unitH);
}
//Continue Button
JQuadPtr quad = WResourceManager::Instance()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
quad->SetHotSpot(16, 16);
IconButton * iconButton = NEW IconButton(1, this, quad.get(), SCREEN_WIDTH_F / 2, SCREEN_HEIGHT_F - 60, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
Add(iconButton);
mSH = 64 / unitH;
mSW = 64 / unitW;
if (options[Options::SFXVOLUME].number > 0)
{
JSample * sample = WResourceManager::Instance()->RetrieveSample("chain.wav");
if (sample)
JSoundSystem::GetInstance()->PlaySample(sample);
}
}
}
JRenderer * r = JRenderer::GetInstance();
//Render background board
if (mBgTex)
{
if (mIsImage)
{
int alpha = mUserCloseRequest ? MAX(0, 255 - (int)(mElapsed * 500)) : MIN(255, (int)(mElapsed * 500)) ;
if (mUserCloseRequest && alpha == 0)
mDontShow = true;
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(alpha / 2,0,0,0));
mBg[0]->SetColor(ARGB(alpha,255,255,255));
r->RenderQuad(mBg[0], SCREEN_WIDTH_F /2 , SCREEN_HEIGHT_F / 2 , 0);
IconButtonsController::SetColor(ARGB(alpha,255,255,255));
}
else
{
//Setup fonts.
WFont * f2 = WResourceManager::Instance()->GetWFont(Fonts::MAGIC_FONT);
f2->SetColor(ARGB(255, 205, 237, 240));
r->FillRect(0, mY, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
r->RenderQuad(mBg[0], 0, mY, 0, mSW, mSH); //TL
r->RenderQuad(mBg[2], SCREEN_WIDTH - 64, mY, 0, mSW, mSH); //TR
r->RenderQuad(mBg[6], 0, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BL
r->RenderQuad(mBg[8], SCREEN_WIDTH - 64, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BR
//Stretch the sides
float stretchV = (144.0f / 128.0f) * mSH;
float stretchH = (176.0f / 128.0f) * mSW;
r->RenderQuad(mBg[3], 0, mY + 64, 0, mSW, stretchV); //L
r->RenderQuad(mBg[5], SCREEN_WIDTH - 64, mY + 64, 0, mSW, stretchV); //R
r->RenderQuad(mBg[1], 64, mY, 0, stretchH, mSH); //T1
r->RenderQuad(mBg[1], 240, mY, 0, stretchH, mSH); //T1
r->RenderQuad(mBg[7], 64, mY + 208, 0, stretchH, mSH); //B1
r->RenderQuad(mBg[7], 240, mY + 208, 0, stretchH, mSH); //B1
r->RenderQuad(mBg[4], 64, mY + 64, 0, stretchH, stretchV); //Center1
r->RenderQuad(mBg[4], 240, mY + 64, 0, stretchH, stretchV); //Center2
}
}
else
{
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
r->FillRect(10, 10 + mY, SCREEN_WIDTH - 10, SCREEN_HEIGHT - 10, ARGB(128,0,0,0));
}
if (!mBgTex || !mIsImage)
{
float posX = 40, posY = mY + 20;
string title = _("Help");
WFont * f = WResourceManager::Instance()->GetWFont(Fonts::MAGIC_FONT);
WFont * f3 = WResourceManager::Instance()->GetWFont(Fonts::MENU_FONT); //OPTION_FONT
f->SetColor(ARGB(255, 55, 46, 34));
f3->SetColor(ARGB(255, 219, 206, 151));
f3->DrawString(title.c_str(), static_cast<float> ((SCREEN_WIDTH - 20) / 2 - title.length() * 4), posY);
posY += 30;
f->DrawString(_(mMessage).c_str(), posX, posY);
posY += 20;
f->SetScale(1);
}
IconButtonsController::Render();
}
ATutorialMessage * ATutorialMessage::clone() const
{
ATutorialMessage * copy = NEW ATutorialMessage(*this);
copy->mUserCloseRequest = copy->alreadyShown();
return copy;
}
ATutorialMessage::~ATutorialMessage()
{
if (mBgTex)
{
WResourceManager::Instance()->Release(mBgTex);
for (int i = 0; i < 9; i++)
SAFE_DELETE(mBg[i]);
}
}
// utility functions // utility functions
// Given a delimited string of abilities, add the ones to the list that are "Basic" MTG abilities // Given a delimited string of abilities, add the ones to the list that are "Basic" MTG abilities

View File

@@ -91,10 +91,9 @@ int Options::getID(string name)
} }
//Is it an unlocked set? //Is it an unlocked set?
size_t un = strlen("unlocked_"); if (name.find("unlocked_") == 0)
if (un < name.size())
{ {
string setname = name.substr(un); string setname = name.substr(strlen("unlocked_"));
if (setlist.size()) if (setlist.size())
{ {
int unlocked = setlist[setname]; int unlocked = setlist[setname];
@@ -195,7 +194,7 @@ GameOption::GameOption(int value) :
{ {
} }
GameOption::GameOption(string value) : GameOption::GameOption(string value) :
number(0), str(value) number(atoi(value.c_str())), str(value)
{ {
} }
GameOption::GameOption(int num, string str) : GameOption::GameOption(int num, string str) :
@@ -335,7 +334,7 @@ int GameOptions::load()
int id = Options::getID(name); int id = Options::getID(name);
if (id == INVALID_OPTION) if (id == INVALID_OPTION)
{ {
unknown.push_back(s); if (!unknownMap[name]) unknownMap[name] = NEW GameOption(val);
continue; continue;
} }
@@ -385,8 +384,16 @@ int GameOptions::save()
opt->write(&file, name); opt->write(&file, name);
} }
for (vector<string>::size_type t = 0; t < unknown.size(); t++) for (map<string, GameOption *>::iterator it = unknownMap.begin(); it != unknownMap.end(); it++)
file << unknown[t] << "\n"; {
if (it->second)
{
if (it->second->str.size())
file << it->first << "=" << it->second->str << "\n";
else if (it->second->number)
file << it->first << "=" << it->second->number << "\n";
}
}
file.close(); file.close();
} }
return 1; return 1;
@@ -401,6 +408,26 @@ GameOption& GameOptions::operator[](int optionID)
return *go; return *go;
} }
GameOption& GameOptions::operator[](string optionName)
{
int id = Options::getID(optionName);
if (id != INVALID_OPTION)
return operator[](id);
GameOption * go = get(optionName);
return * go;
}
GameOption * GameOptions::get(string optionName)
{
if (!unknownMap[optionName])
unknownMap[optionName] = NEW GameOption(0);
return unknownMap[optionName];
}
GameOption * GameOptions::get(int optionID) GameOption * GameOptions::get(int optionID)
{ {
//Invalid options! //Invalid options!
@@ -480,6 +507,10 @@ GameOptions::~GameOptions()
for (vector<GameOption*>::iterator it = values.begin(); it != values.end(); it++) for (vector<GameOption*>::iterator it = values.begin(); it != values.end(); it++)
SAFE_DELETE(*it); SAFE_DELETE(*it);
values.clear(); values.clear();
for (map<string, GameOption*>::iterator it = unknownMap.begin(); it != unknownMap.end(); it++)
SAFE_DELETE(it->second);
unknownMap.clear();
} }
GameSettings options; GameSettings options;
@@ -570,6 +601,22 @@ GameOption& GameSettings::operator[](int optionID)
return *go; return *go;
} }
GameOption& GameSettings::operator[](string optionName)
{
int id = Options::getID(optionName);
if (id != INVALID_OPTION)
return operator[](id);
if (!profileOptions)
return invalid_option;
GameOption * go = profileOptions->get(optionName);
assert(go);
return *go;
}
GameOption* GameSettings::get(int optionID) GameOption* GameSettings::get(int optionID)
{ {
#ifdef DEBUG #ifdef DEBUG

View File

@@ -477,8 +477,7 @@ void GameStateShop::Update(float dt)
lightAlpha = 0; lightAlpha = 0;
if (lightAlpha > 50) if (lightAlpha > 50)
lightAlpha = 50; lightAlpha = 50;
// mParent->effect->UpdateSmall(dt);
// mParent->effect->UpdateBig(dt);
if (mStage != STAGE_FADE_IN) if (mStage != STAGE_FADE_IN)
mElapsed += dt; mElapsed += dt;

View File

@@ -0,0 +1,137 @@
#include "PrecompiledHeader.h"
#include "IconButton.h"
#include "WResourceManager.h"
#include "WFont.h"
#define SCALE_SELECTED 1.2f
#define SCALE_NORMAL 1.0f
IconButtonsController::IconButtonsController(float x, float y): JGuiController(0, NULL), mX(x), mY(y)
{
mListener = this;
}
void IconButtonsController::SetColor(PIXEL_TYPE color)
{
for (int i = 0; i < mCount; ++i)
if (mObjects[i])
((IconButton *)mObjects[i])->SetColor(color);
}
IconButton::IconButton(int id, IconButtonsController * parent, string texture, float x, float y, float scale, int fontId, string text, float textRelativeX, float textRelativeY, bool hasFocus): JGuiObject(id)
{
mQuad = NULL;
mTex = WResourceManager::Instance()->RetrieveTexture(texture, RETRIEVE_LOCK);
if (mTex)
{
mQuad = NEW JQuad(mTex, 0, 0, (float) mTex->mWidth, (float) mTex->mHeight);
}
init(parent, mQuad, x, y, scale, fontId, text, textRelativeX, textRelativeY, hasFocus);
}
IconButton::IconButton(int id, IconButtonsController * parent, JQuad * quad, float x, float y, float scale, int fontId, string text, float textRelativeX, float textRelativeY, bool hasFocus): JGuiObject(id)
{
mTex = NULL;
init(parent, quad, x, y, scale, fontId, text, textRelativeX, textRelativeY, hasFocus);
}
void IconButton::init(IconButtonsController * parent, JQuad * quad, float x, float y, float scale, int fontId, string text, float textRelativeX, float textRelativeY, bool hasFocus)
{
mParent = parent;
mQuad = quad;
mX = x;
mY = y;
mScale = scale;
mFontId = fontId;
mText = text;
mTextRelativeX = textRelativeX;
mTextRelativeY = textRelativeY;
mHasFocus = hasFocus;
mCurrentScale = scale;
mTargetScale = mHasFocus ? SCALE_SELECTED * mScale : SCALE_NORMAL * mScale;
SetColor(ARGB(255,255,255,255));
}
void IconButton::SetColor(PIXEL_TYPE color)
{
mColor = color;
}
bool IconButton::hasFocus()
{
return mHasFocus;
}
void IconButton::Render()
{
JRenderer * r = JRenderer::GetInstance();
float relX = mX + mParent->mX;
float relY = mY + mParent->mY;
if (mQuad)
{
mQuad->SetColor(mColor);
r->RenderQuad(mQuad, relX, relY, 0, mCurrentScale, mCurrentScale);
}
if (mText.size())
{
WFont * mFont = WResourceManager::Instance()->GetWFont(mFontId);
PIXEL_TYPE backup = mFont->GetColor();
mFont->SetColor(ARGB(255,0,0,0));
//TODO adapt if mTextRelativeX/Y/align are negative/positive
mFont->DrawString(mText.c_str(), relX + mTextRelativeX , relY + mTextRelativeY , JGETEXT_CENTER);
mFont->SetColor(backup);
}
}
void IconButton::Update(float dt)
{
if (mCurrentScale < mTargetScale)
{
mCurrentScale += 8.0f * dt;
if (mCurrentScale > mTargetScale) mCurrentScale = mTargetScale;
}
else if (mCurrentScale > mTargetScale)
{
mCurrentScale -= 8.0f * dt;
if (mCurrentScale < mTargetScale) mCurrentScale = mTargetScale;
}
}
void IconButton::Entering()
{
mHasFocus = true;
mTargetScale = SCALE_SELECTED * mScale;
}
bool IconButton::Leaving(JButton key)
{
mHasFocus = false;
mTargetScale = SCALE_NORMAL * mScale;
return true;
}
bool IconButton::ButtonPressed()
{
return true;
}
IconButton::~IconButton()
{
if (mTex)
{
WResourceManager::Instance()->Release(mTex);
SAFE_DELETE(mQuad);
}
}
ostream& IconButton::toString(ostream& out) const
{
return out << "IconButton ::: mHasFocus : " << mHasFocus;
}

View File

@@ -743,6 +743,15 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
} }
} }
//This one is not a real ability, it displays a message on the screen. We use this for tutorials
// Triggers need to be checked above this one, as events are usuallly what will trigger (...) these messages
vector<string> splitMsg = parseBetween(s, "tutorial(", ")");
if (splitMsg.size())
{
string msg = splitMsg[1];
return NEW ATutorialMessage(card, msg);
}
int restrictions = parseRestriction(s); int restrictions = parseRestriction(s);
string newName = ""; string newName = "";

View File

@@ -588,7 +588,7 @@ int Rules::load(string _filename)
if (_filename.size() < 5 || (_filename.find(".txt") == string::npos && _filename.find(".TXT") == string::npos)) if (_filename.size() < 5 || (_filename.find(".txt") == string::npos && _filename.find(".TXT") == string::npos))
return 0; return 0;
if (!filename.size()) //this check is necessary because of the recursive calls (a fil loads other files) if (!filename.size()) //this check is necessary because of the recursive calls (a file loads other files)
filename = _filename; filename = _filename;
char c_filename[4096]; char c_filename[4096];
if (fileExists(_filename.c_str())) if (fileExists(_filename.c_str()))
@@ -608,6 +608,7 @@ int Rules::load(string _filename)
if (!file) return 0; if (!file) return 0;
cleanup(); cleanup();
while (std::getline(file, s)) while (std::getline(file, s))
{ {
if (!s.size()) continue; if (!s.size()) continue;
@@ -618,6 +619,7 @@ int Rules::load(string _filename)
if (s.find("include ") == 0) if (s.find("include ") == 0)
{ {
load(s.substr(8)); load(s.substr(8));
hidden = false; //To avoid transmitting the hidden param to children
continue; continue;
} }
if (s.compare("[init]") == 0) if (s.compare("[init]") == 0)

View File

@@ -8,47 +8,10 @@
#include "MTGDefinitions.h" #include "MTGDefinitions.h"
#include <JRenderer.h> #include <JRenderer.h>
#include <math.h> #include <math.h>
#include "utils.h"
vector<string> Task::sAIDeckNames; vector<string> Task::sAIDeckNames;
/*---------------- Utils -----------------*/
// TODO: Move to dedicated file
//!! Copypaste from GameStateDeckViewer.cpp StringExplode. Move and #include here and there
void ExplodeStr(string str, string separator, vector<string>* results)
{
int found;
results->clear();
found = str.find_first_of(separator);
while (found != (int) string::npos)
{
if (found > 0)
{
results->push_back(str.substr(0, found));
}
else
{
results->push_back(" ");
}
str = str.substr(found + 1);
found = str.find_first_of(separator);
}
if (str.length() > 0)
{
results->push_back(str);
}
}
string ImplodeStr(string separator, vector<string> strs)
{
string result = "";
for (vector<string>::iterator it = strs.begin(); it != strs.end(); it++)
{
result += (it == strs.begin() ? "" : separator) + (*it);
}
return result;
}
/*---------------- Task -----------------*/ /*---------------- Task -----------------*/
Task::Task(char _type) Task::Task(char _type)
@@ -80,7 +43,7 @@ string Task::toString()
{ {
storeCommonAttribs(); storeCommonAttribs();
storeCustomAttribs(); storeCustomAttribs();
return ImplodeStr(ITEM_SEPARATOR, persistentAttribs); return join(persistentAttribs, ITEM_SEPARATOR);
} }
// Store basic attributes to vector, for saving // Store basic attributes to vector, for saving
@@ -226,12 +189,12 @@ string Task::getAIDeckName(int id)
// End of AI deck buffering code // End of AI deck buffering code
// Each child class has to be added to the switch in this function (clumsy..) // Each child class has to be added to the switch in this function (clumsy..)
Task* Task::createFromStr(string params, bool rand) Task* Task::createFromStr(const string params, bool rand)
{ {
vector<string> exploded; vector<string> exploded;
Task *result; Task *result;
ExplodeStr(params, ITEM_SEPARATOR, &exploded); split(params, ITEM_SEPARATOR[0], exploded);
switch (exploded[0][0]) switch (exploded[0][0])
{ {

View File

@@ -387,3 +387,13 @@ std::string wordWrap(const std::string& sentence, float width, int fontId)
} }
unsigned long hash_djb2(const char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}

View File

@@ -371,6 +371,7 @@
<ClCompile Include="src\GuiPhaseBar.cpp" /> <ClCompile Include="src\GuiPhaseBar.cpp" />
<ClCompile Include="src\GuiPlay.cpp" /> <ClCompile Include="src\GuiPlay.cpp" />
<ClCompile Include="src\GuiStatic.cpp" /> <ClCompile Include="src\GuiStatic.cpp" />
<ClCompile Include="src\IconButton.cpp" />
<ClCompile Include="src\ManaCost.cpp" /> <ClCompile Include="src\ManaCost.cpp" />
<ClCompile Include="src\ManaCostHybrid.cpp" /> <ClCompile Include="src\ManaCostHybrid.cpp" />
<ClCompile Include="src\MenuItem.cpp" /> <ClCompile Include="src\MenuItem.cpp" />
@@ -489,6 +490,7 @@
<ClInclude Include="include\GuiPhaseBar.h" /> <ClInclude Include="include\GuiPhaseBar.h" />
<ClInclude Include="include\GuiPlay.h" /> <ClInclude Include="include\GuiPlay.h" />
<ClInclude Include="include\GuiStatic.h" /> <ClInclude Include="include\GuiStatic.h" />
<ClInclude Include="include\IconButton.h" />
<ClInclude Include="include\Manacost.h" /> <ClInclude Include="include\Manacost.h" />
<ClInclude Include="include\ManaCostHybrid.h" /> <ClInclude Include="include\ManaCostHybrid.h" />
<ClInclude Include="include\MenuItem.h" /> <ClInclude Include="include\MenuItem.h" />

View File

@@ -310,6 +310,9 @@
<ClCompile Include="src\AIHints.cpp"> <ClCompile Include="src\AIHints.cpp">
<Filter>src</Filter> <Filter>src</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\IconButton.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="include\ActionElement.h"> <ClInclude Include="include\ActionElement.h">
@@ -645,6 +648,9 @@
<ClInclude Include="Windows\resource.h"> <ClInclude Include="Windows\resource.h">
<Filter>res</Filter> <Filter>res</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\IconButton.h">
<Filter>inc</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Makefile" /> <None Include="Makefile" />

View File

@@ -97,6 +97,7 @@ SOURCES += \
src/GuiPhaseBar.cpp\ src/GuiPhaseBar.cpp\
src/GuiPlay.cpp\ src/GuiPlay.cpp\
src/GuiStatic.cpp\ src/GuiStatic.cpp\
src/IconButton.cpp\
src/ManaCost.cpp\ src/ManaCost.cpp\
src/ManaCostHybrid.cpp\ src/ManaCostHybrid.cpp\
src/MenuItem.cpp\ src/MenuItem.cpp\
@@ -200,6 +201,7 @@ HEADERS += \
include/ThisDescriptor.h\ include/ThisDescriptor.h\
include/CardGui.h\ include/CardGui.h\
include/GameStateTransitions.h\ include/GameStateTransitions.h\
include/IconButton.h\
include/OptionItem.h\ include/OptionItem.h\
include/Token.h\ include/Token.h\
include/CardPrimitive.h\ include/CardPrimitive.h\

View File

@@ -93,6 +93,7 @@ SOURCES += \
src/GuiPhaseBar.cpp\ src/GuiPhaseBar.cpp\
src/GuiPlay.cpp\ src/GuiPlay.cpp\
src/GuiStatic.cpp\ src/GuiStatic.cpp\
src/IconButton.cpp\
src/ManaCost.cpp\ src/ManaCost.cpp\
src/ManaCostHybrid.cpp\ src/ManaCostHybrid.cpp\
src/MenuItem.cpp\ src/MenuItem.cpp\
@@ -195,6 +196,7 @@ HEADERS += \
include/ThisDescriptor.h\ include/ThisDescriptor.h\
include/CardGui.h\ include/CardGui.h\
include/GameStateTransitions.h\ include/GameStateTransitions.h\
include/IconButton.h\
include/OptionItem.h\ include/OptionItem.h\
include/Token.h\ include/Token.h\
include/CardPrimitive.h\ include/CardPrimitive.h\