Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
133 KB
Referenced Files
None
Subscribers
None
diff --git a/util/gui/animation.cpp b/util/gui/animation.cpp
index c2f696ce..131aeeed 100644
--- a/util/gui/animation.cpp
+++ b/util/gui/animation.cpp
@@ -1,1254 +1,1258 @@
#include "animation.h"
#include <stdio.h>
#include <vector>
#include <math.h>
#include <sstream>
#include "util/token.h"
#include "util/graphics/bitmap.h"
#include "util/font.h"
#include "util/graphics/gradient.h"
#include "../debug.h"
#include "../funcs.h"
#include "../file-system.h"
#include "../events.h"
#include "util/exceptions/load_exception.h"
using namespace std;
using namespace Gui;
// Temporary solution
static void renderSprite(const Graphics::Bitmap & bmp, const int x, const int y, const int alpha, const bool hflip, const bool vflip, const Graphics::Bitmap & work){
if (alpha != 255){
Graphics::Bitmap::transBlender( 0, 0, 0, alpha );
if (hflip && !vflip){
bmp.translucent().drawHFlip(x,y, work);
} else if (!hflip && vflip){
bmp.translucent().drawVFlip(x,y, work);
} else if (hflip && vflip){
bmp.translucent().drawHVFlip(x,y, work);
} else if (!hflip && !vflip){
bmp.translucent().draw(x,y, work);
}
} else {
if (hflip && !vflip){
bmp.drawHFlip(x,y, work);
} else if (!hflip && vflip){
bmp.drawVFlip(x,y, work);
} else if (hflip && vflip){
bmp.drawHVFlip(x,y, work);
} else if (!hflip && !vflip){
bmp.draw(x,y, work);
}
}
}
Element::Element(const Token * token):
time(50),
alpha(255){
if (token != NULL){
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
parseToken(tok);
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Gui::Animation::Element parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
}
Element::~Element(){
}
void Element::parseToken(const Token * token){
if (*token == "alpha"){
// get alpha
token->view() >> alpha;
} else if (*token == "offset"){
// Get the offset location it defaults to 0,0
double x=0, y=0;
try {
token->view() >> x >> y;
} catch (const TokenException & ex){
}
offset.set(x,y);
} else if (*token == "time"){
// time to display
token->view() >> time;
} else {
Global::debug( 3 ) << "Unhandled Gui::Animation::Element attribute: " << endl;
if (Global::getDebug() >= 3){
token->print(" ");
}
}
}
void Element::act(double xvel, double yvel){
scrollOffset.moveBy(xvel, yvel);
}
ImageFrame::ImageFrame(const Token *the_token, ImageMap &images, const string & baseDir):
Element(the_token),
bmp(NULL),
horizontalFlip(false),
verticalFlip(false){
/*
if (*the_token != "frame"){
throw LoadException(__FILE__, __LINE__, "Not an frame");
}
*/
/* The usual setup of an animation frame is
// use image -1 to not draw anything, it can be used to get a blinking effect
(frame (image NUM) (alpha NUM) (offset x y) (hflip 0|1) (vflip 0|1) (time NUM))
*/
TokenView view = the_token->view();
while (view.hasMore()){
try{
const Token * token;
view >> token;
parseToken(token, baseDir, images);
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu animation parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
void ImageFrame::parseToken(const Token * token, const string & baseDir, ImageMap & images){
if (*token == "image"){
// get the number
string maybeNumber;
token->view() >> maybeNumber;
if (Util::matchRegex(maybeNumber, Util::Regex("\\d+"))){
int num;
token->view() >> num;
if (images.find(num) == images.end()){
ostringstream out;
out << "No image for index " << num;
throw LoadException(__FILE__, __LINE__, out.str());
}
// now assign the bitmap
bmp = images[num];
} else {
bmp = Util::ReferenceCount<Graphics::Bitmap>(new Graphics::Bitmap(Storage::instance().find(Filesystem::RelativePath(baseDir + "/" + maybeNumber)).path()));
}
} else if (*token == "hflip"){
// horizontal flip
token->view() >> horizontalFlip;
} else if (*token == "vflip"){
// horizontal flip
token->view() >> verticalFlip;
} else {
Global::debug( 3 ) << "Unhandled Gui::Animation::ImageFrame attribute: "<<endl;
if (Global::getDebug() >= 3){
token->print(" ");
}
}
}
ImageFrame::ImageFrame(Util::ReferenceCount<Graphics::Bitmap> bmp):
Element(NULL),
bmp(bmp),
horizontalFlip(false),
verticalFlip(false){
time = -1;
}
ImageFrame::~ImageFrame(){
}
static bool closeFloat(double a, double b){
const double epsilon = 0.001;
return fabs(a-b) < epsilon;
}
void ImageFrame::act(double xvel, double yvel){
scrollOffset.moveBy(xvel, yvel);
if (bmp != NULL){
if (scrollOffset.getDistanceFromCenterX() >= bmp->getWidth()){
scrollOffset.setX(0);
} else if (scrollOffset.getDistanceFromCenterX() <= -(bmp->getWidth())){
scrollOffset.setX(0);
}
if (scrollOffset.getDistanceFromCenterY() >= bmp->getHeight()){
scrollOffset.setY(0);
} else if (scrollOffset.getDistanceFromCenterY() <= -(bmp->getHeight())){
scrollOffset.setY(0);
}
}
}
void ImageFrame::draw(const int xaxis, const int yaxis, const Graphics::Bitmap & work){
if (!bmp){
return;
}
if (!closeFloat(scrollOffset.getDistanceFromCenterX(), 0) || !closeFloat(scrollOffset.getDistanceFromCenterY(), 0)){
// Lets do some scrolling
// Graphics::Bitmap temp = Graphics::Bitmap::temporaryBitmap(bmp->getWidth(), bmp->getHeight());
//AnimationPoint loc;
AbsolutePoint loc;
if (scrollOffset.getRelativeX() < 0){
loc.setX(scrollOffset.getDistanceFromCenterX() + bmp->getWidth());
} else if (scrollOffset.getRelativeX() > 0){
loc.setX(scrollOffset.getDistanceFromCenterX() - bmp->getWidth());
}
if (scrollOffset.getRelativeY() < 0){
loc.setY(scrollOffset.getDistanceFromCenterY() + bmp->getHeight());
} else if (scrollOffset.getRelativeY() > 0){
loc.setY(scrollOffset.getDistanceFromCenterY() - bmp->getHeight());
}
/*
bmp->Blit((int) scrollOffset.getDistanceFromCenterX(), (int) scrollOffset.getDistanceFromCenterY(), temp);
bmp->Blit((int) scrollOffset.getDistanceFromCenterX(), (int) loc.getY(), temp);
bmp->Blit((int) loc.getX(), (int) scrollOffset.getDistanceFromCenterY(), temp);
bmp->Blit((int) loc.getX(), (int) loc.getY(), temp);
renderSprite(temp, (int)(xaxis+offset.getDistanceFromCenterX()), (int)(yaxis+offset.getDistanceFromCenterY()), alpha, horizontalFlip, verticalFlip, work);
*/
double x = xaxis+offset.getDistanceFromCenterX();
double y = yaxis+offset.getDistanceFromCenterY();
renderSprite(*bmp,
(int)(x + scrollOffset.getDistanceFromCenterX()),
(int)(y + scrollOffset.getDistanceFromCenterY()),
alpha, horizontalFlip, verticalFlip, work);
renderSprite(*bmp,
(int)(x + loc.getX()),
(int)(y + scrollOffset.getDistanceFromCenterY()),
alpha, horizontalFlip, verticalFlip, work);
renderSprite(*bmp,
(int)(x + scrollOffset.getDistanceFromCenterX()),
(int)(y + loc.getY()),
alpha, horizontalFlip, verticalFlip, work);
renderSprite(*bmp,
(int)(x + loc.getX()),
(int)(y + loc.getY()),
alpha, horizontalFlip, verticalFlip, work);
} else {
renderSprite(*bmp, (int)(xaxis+offset.getDistanceFromCenterX()), (int)(yaxis+offset.getDistanceFromCenterY()), alpha, horizontalFlip, verticalFlip, work);
}
}
/* This is mainly called from select-list to draw the cells. I'm not sure
* of the utility of stretching the bitmap to fit the cell dimensions. Most
* likely the cell will contain an image and the image will be the same
* size as the cell.
*/
void ImageFrame::draw(const Graphics::Bitmap & work){
Graphics::Bitmap temp(bmp->getWidth(), bmp->getHeight());
temp.clearToMask();
renderSprite(*bmp, 0, 0, alpha, horizontalFlip, verticalFlip, temp);
temp.drawStretched(work);
/* FIXME: This should work, but it doesn't.. */
/*
Graphics::StretchedBitmap show(bmp->getWidth(), bmp->getHeight(), work);
show.start();
renderSprite(*bmp, 0, 0, alpha, horizontalFlip, verticalFlip, show);
show.finish();
*/
}
void ImageFrame::reset(){
scrollOffset = RelativePoint();
}
void ImageFrame::setToEnd(const RelativePoint & end){
scrollOffset = end;
}
static const char * FRAME_TEXT = "Offset: ( %f, %f)\nScroll Offset: ( %f, %f)\nTime: %d\nHorizontal Flip: %d\nVertical Flip: %d\nAlpha: %d\n\n";
const std::string ImageFrame::getInfo(){
char info[255];
sprintf(info, FRAME_TEXT, offset.getRelativeX(), offset.getRelativeY(), scrollOffset.getRelativeX(), scrollOffset.getRelativeY(), time, horizontalFlip, verticalFlip, alpha);
return std::string(info);
}
TextFrame::TextFrame(const Token *token):
Element(token),
fontWidth(20),
fontHeight(20){
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * token;
view >> token;
parseToken(token);
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu animation parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
TextFrame::~TextFrame(){
}
void TextFrame::act(double xvel, double yvel){
gradient.forward();
Element::act(xvel, yvel);
}
static const Font & getFont(const string & font, int width, int height){
if (font != ""){
return Font::getFont(Filesystem::RelativePath(font), width, height);
}
return Font::getDefaultFont(width, height);
}
void TextFrame::draw(int xaxis, int yaxis, const Graphics::Bitmap & work){
double x = xaxis + offset.getDistanceFromCenterX() + scrollOffset.getDistanceFromCenterX();
double y = yaxis + offset.getDistanceFromCenterY() + scrollOffset.getDistanceFromCenterY();
const Font & font = getFont(this->font, fontWidth, fontHeight);
font.printf((int) x, (int) y, gradient.current(), work, "%s", 0, message.c_str());
}
void TextFrame::draw(const Graphics::Bitmap & work){
/* Probably don't need this.. but implement it if you do! */
}
void TextFrame::reset(){
scrollOffset = RelativePoint();
}
void TextFrame::setToEnd(const RelativePoint & point){
scrollOffset = point;
}
const std::string TextFrame::getInfo(){
return message;
}
void TextFrame::parseToken(const Token * token){
if (*token == "message"){
token->view() >> message;
} else if (*token == "font"){
TokenView view = token->view();
view >> font;
try{
view >> fontWidth >> fontHeight;
} catch (const TokenException & ignore){
}
if (fontWidth < 1){
fontWidth = 1;
}
if (fontHeight < 1){
fontHeight = 1;
}
} else if (*token == "color"){
int red, green, blue;
token->view() >> red >> green >> blue;
gradient = Effects::Gradient(Graphics::makeColor(red, green, blue));
} else if (*token == "gradient"){
gradient = Effects::Gradient(token);
}
}
Sequence::Sequence(){
}
Sequence::~Sequence(){
}
SequenceFrame::SequenceFrame(const Util::ReferenceCount<Element> & frame):
frame(frame),
ticks(0){
}
void SequenceFrame::draw(int xaxis, int yaxis, const Graphics::Bitmap & work) const {
frame->draw(xaxis, yaxis, work);
}
Util::ReferenceCount<Element> SequenceFrame::getCurrentFrame() const {
return frame;
}
int SequenceFrame::totalTicks() const {
return frame->getTime();
}
void SequenceFrame::setToEnd(){
ticks = frame->getTime();
}
bool SequenceFrame::forward(int tickCount, double velocityX, double velocityY){
frame->act(tickCount * velocityX, tickCount * velocityY);
ticks += tickCount;
return ticks > frame->getTime();
}
bool SequenceFrame::reverse(int tickCount, double velocityX, double velocityY){
frame->act(-tickCount * velocityX, -tickCount * velocityY);
ticks -= tickCount;
return ticks < 0;
}
void SequenceFrame::forwardFrame(){
/* nop */
}
void SequenceFrame::backFrame(){
/* nop */
}
void SequenceFrame::reset(){
ticks = 0;
frame->reset();
}
void SequenceFrame::resetTicks(){
ticks = 0;
}
SequenceLoop::SequenceLoop(int loops):
currentFrame(0),
currentLoop(loops),
loopTimes(loops){
}
Util::ReferenceCount<Element> SequenceLoop::getCurrentFrame() const {
if (currentFrame < frames.size()){
return frames[currentFrame]->getCurrentFrame();
} else {
/* Return the last frame */
if (frames.size() > 0){
return frames[frames.size() - 1]->getCurrentFrame();
}
}
return Util::ReferenceCount<Element>(NULL);
}
Util::ReferenceCount<Sequence> SequenceLoop::getCurrentSequence() const {
if (currentFrame < frames.size()){
return frames[currentFrame];
} else {
/* Return the last frame */
if (frames.size() > 0){
return frames[frames.size() - 1];
}
}
return Util::ReferenceCount<Sequence>(NULL);
}
void SequenceLoop::draw(int xaxis, int yaxis, const Graphics::Bitmap & work) const {
Util::ReferenceCount<Sequence> current = getCurrentSequence();
if (current != NULL){
current->draw(xaxis, yaxis, work);
}
}
void SequenceLoop::reset(){
currentFrame = 0;
currentLoop = loopTimes;
for (vector<Util::ReferenceCount<Sequence> >::iterator it = frames.begin(); it != frames.end(); it++){
Util::ReferenceCount<Sequence> & sequence = *it;
sequence->reset();
}
}
/* Does the same thing as reset except for calling resetTicks on children nodes */
void SequenceLoop::resetTicks(){
currentFrame = 0;
currentLoop = loopTimes;
for (vector<Util::ReferenceCount<Sequence> >::iterator it = frames.begin(); it != frames.end(); it++){
Util::ReferenceCount<Sequence> & sequence = *it;
sequence->resetTicks();
}
}
/* Similar to resetTicks but doesn't reset the current frame or loop */
void SequenceLoop::resetChildrenTicks(){
for (vector<Util::ReferenceCount<Sequence> >::iterator it = frames.begin(); it != frames.end(); it++){
Util::ReferenceCount<Sequence> & sequence = *it;
sequence->resetTicks();
}
}
void SequenceLoop::setToEnd(){
/* FIXME */
}
void SequenceLoop::addSequence(const Util::ReferenceCount<Sequence> & sequence){
frames.push_back(sequence);
}
static Util::ReferenceCount<Sequence> parseSequence(const Token * token, ImageMap & images, const string & baseDir){
if (*token == "frame"){
Util::ReferenceCount<Element> frame(new ImageFrame(token, images, baseDir));
return Util::ReferenceCount<Sequence>(new SequenceFrame(frame));
} else if (*token == "text"){
Util::ReferenceCount<Element> frame(new TextFrame(token));
return Util::ReferenceCount<Sequence>(new SequenceFrame(frame));
} else if (*token == "loop"){
int times;
token->view() >> times;
Util::ReferenceCount<SequenceLoop> loop(new SequenceLoop(times));
loop->parse(token, images, baseDir);
return loop;
} else if (*token == "all"){
return Util::ReferenceCount<Sequence>(new SequenceAll(token, images, baseDir));
} else if (*token == "random"){
return Util::ReferenceCount<Sequence>(new SequenceRandom(token, images, baseDir));
}
return Util::ReferenceCount<Sequence>(NULL);
}
void SequenceLoop::parse(const Token * token, ImageMap & images, const string & baseDir){
TokenView view = token->view();
/* first ignore the number of times to loop */
int ignore;
view >> ignore;
while (view.hasMore()){
const Token * next;
view >> next;
Util::ReferenceCount<Sequence> sequence = parseSequence(next, images, baseDir);
if (sequence != NULL){
addSequence(sequence);
}
}
}
int SequenceLoop::totalTicks() const {
int total = 0;
for (vector<Util::ReferenceCount<Sequence> >::const_iterator it = frames.begin(); it != frames.end(); it++){
const Util::ReferenceCount<Sequence> & sequence = *it;
total += sequence->totalTicks();
}
return total * (1 + loopTimes);
}
bool SequenceLoop::forward(int tickCount, double velocityX, double velocityY){
Util::ReferenceCount<Sequence> current = getCurrentSequence();
if (current != NULL){
if (current->forward(tickCount, velocityX, velocityY)){
forwardFrame();
}
}
return currentFrame == frames.size();
}
bool SequenceLoop::reverse(int tickCount, double velocityX, double velocityY){
Util::ReferenceCount<Sequence> current = getCurrentSequence();
if (current != NULL){
if (current->reverse(tickCount, velocityX, velocityY)){
backFrame();
}
}
return currentFrame == frames.size();
}
void SequenceLoop::forwardFrame(){
currentFrame += 1;
/* do the loop */
if (currentFrame == frames.size() && currentLoop > 0){
currentLoop -= 1;
currentFrame = 0;
resetChildrenTicks();
}
}
void SequenceLoop::backFrame(){
currentFrame -= 1;
/* do the loop */
if (currentFrame <= 0 && currentLoop > 0){
currentLoop -= 1;
resetChildrenTicks();
if (frames.size() > 0){
currentFrame = frames.size() - 1;
} else {
currentFrame = 0;
}
}
}
SequenceAll::SequenceAll(const Token * token, ImageMap & images, const string & baseDir){
TokenView view = token->view();
while (view.hasMore()){
const Token * next;
view >> next;
Util::ReferenceCount<Sequence> sequence = parseSequence(next, images, baseDir);
if (sequence != NULL){
addSequence(sequence);
}
}
}
Util::ReferenceCount<Element> SequenceAll::getCurrentFrame() const {
return Util::ReferenceCount<Element>(NULL);
}
void SequenceAll::reset(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->reset();
}
}
void SequenceAll::resetTicks(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->resetTicks();
}
}
void SequenceAll::setToEnd(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->setToEnd();
}
}
void SequenceAll::addSequence(const Util::ReferenceCount<Sequence> & sequence){
sequences.push_back(sequence);
}
void SequenceAll::draw(int xaxis, int yaxis, const Graphics::Bitmap & work) const {
for (SequenceConstIterator it = sequences.begin(); it != sequences.end(); it++){
const Util::ReferenceCount<Sequence> & next = *it;
next->draw(xaxis, yaxis, work);
}
}
/* Maximum ticks of any sequence */
int SequenceAll::totalTicks() const {
int max = 0;
for (SequenceConstIterator it = sequences.begin(); it != sequences.end(); it++){
const Util::ReferenceCount<Sequence> & next = *it;
int what = next->totalTicks();
if (what > max){
max = what;
}
}
return max;
}
bool SequenceAll::forward(int tickCount, double velocityX, double velocityY){
bool go = false;
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
go = next->forward(tickCount, velocityX, velocityY) && go;
}
return go;
}
bool SequenceAll::reverse(int tickCount, double velocityX, double velocityY){
bool go = false;
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
go = next->reverse(tickCount, velocityX, velocityY) && go;
}
return go;
}
void SequenceAll::forwardFrame(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->forwardFrame();
}
}
void SequenceAll::backFrame(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->backFrame();
}
}
SequenceRandom::SequenceRandom(const Token * token, ImageMap & images, const std::string & baseDir):
current(0){
TokenView view = token->view();
while (view.hasMore()){
const Token * next;
view >> next;
Util::ReferenceCount<Sequence> sequence = parseSequence(next, images, baseDir);
if (sequence != NULL){
addSequence(sequence);
}
}
current = Util::rnd(sequences.size());
}
Util::ReferenceCount<Element> SequenceRandom::getCurrentFrame() const {
return Util::ReferenceCount<Element>(NULL);
}
void SequenceRandom::reset(){
current = Util::rnd(sequences.size());
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->reset();
}
}
void SequenceRandom::resetTicks(){
current = Util::rnd(sequences.size());
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->resetTicks();
}
}
void SequenceRandom::setToEnd(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->setToEnd();
}
}
void SequenceRandom::addSequence(const Util::ReferenceCount<Sequence> & sequence){
sequences.push_back(sequence);
}
void SequenceRandom::draw(int xaxis, int yaxis, const Graphics::Bitmap & work) const {
if (current < sequences.size()){
Util::ReferenceCount<Sequence> now = sequences[current];
now->draw(xaxis, yaxis, work);
}
}
int SequenceRandom::totalTicks() const {
int max = 0;
for (SequenceConstIterator it = sequences.begin(); it != sequences.end(); it++){
const Util::ReferenceCount<Sequence> & next = *it;
int what = next->totalTicks();
if (what > max){
max = what;
}
}
return max;
}
bool SequenceRandom::forward(int tickCount, double velocityX, double velocityY){
if (current < sequences.size()){
Util::ReferenceCount<Sequence> now = sequences[current];
return now->forward(tickCount, velocityX, velocityY);
}
return true;
}
bool SequenceRandom::reverse(int tickCount, double velocityX, double velocityY){
if (current < sequences.size()){
Util::ReferenceCount<Sequence> now = sequences[current];
return now->reverse(tickCount, velocityX, velocityY);
}
return true;
}
void SequenceRandom::forwardFrame(){
if (current < sequences.size()){
sequences[current]->forwardFrame();
}
}
void SequenceRandom::backFrame(){
if (current < sequences.size()){
sequences[current]->backFrame();
}
}
static int CURRENT_ID = 0;
static int getNextId(){
return CURRENT_ID++;
}
Animation::Animation(const Token *the_token):
id(getNextId()),
depth(BackgroundBottom),
allowReset(true),
sequence(0){
/* Whats this for? */
images[-1] = 0;
std::string basedir = ".";
if ( *the_token != "anim" && *the_token != "animation" ){
throw LoadException(__FILE__, __LINE__, "Not an animation");
}
/* The usual setup of an animation is
The images must be listed prior to listing any frames, basedir can be used to set the directory where the images are located
loop will begin at the subsequent frame listed after loop
axis is the location in which the drawing must be placed
location *old* - used to render in background or foreground (0 == background [default]| 1 == foreground)
depth - used to render in background or foreground space (depth background bottom|middle|top) | (depth foreground bottom|midle|top)
reset - used to allow resetting of animation (0 == no | 1 == yes [default])
velocity - used to get a wrapping scroll effect while animating
window - area in which the item will be contained
(anim (id NUM)
(location NUM)
(scale NUM) ;; optional
(depth background|foreground NUM)
(basedir LOCATION)
(image NUM FILE [SCALE])
(velocity x y)
(axis x y)
(frame "Read comments above in constructor")
(loop 2)
(reset NUM)
(window x1 y1 x2 y2))
*/
double scale = 1.0;
bool scaleSet = false;
const Token & tok = *the_token;
TokenView view = tok.view();
while (view.hasMore()){
try{
const Token * token;
view >> token;
if (*token == "id"){
// get the id
token->view() >> id;
} else if (*token == "location"){
// translate location to depth
/*int location = 0;
token->view() >> location;
if (location == 0){
depth = BackgroundBottom;
} else if (location == 1){
depth = ForegroundBottom;
}*/
Global::debug(0) << "The option (" << token->getLineage() << ") in the file '" << token->getFileName() << "' is no longer valid and will be ignored. Consider using 'depth' instead." << std::endl;
} else if (*token == "scale"){
token->view() >> scale;
scaleSet = true;
} else if (*token == "depth"){
// get the depth
std::string name, level;
token->view() >> name >> level;
if (name == "background"){
if (level == "bottom"){
depth = BackgroundBottom;
} else if (level == "middle"){
depth = BackgroundMiddle;
} else if (level == "top"){
depth = BackgroundTop;
}
} else if (name == "foreground"){
if (level == "bottom"){
depth = ForegroundBottom;
} else if (level == "middle"){
depth = ForegroundMiddle;
} else if (level == "top"){
depth = ForegroundTop;
}
}
} else if (*token == "basedir"){
// set the base directory for loading images
token->view() >> basedir;
} else if (*token == "image"){
// add bitmaps by number to the map
int number;
std::string temp;
double localScale = 1;
bool localScaleSet = false;
TokenView view = token->view();
view >> number >> temp;
try{
view >> localScale;
localScaleSet = true;
} catch (const TokenException & fail){
}
Util::ReferenceCount<Graphics::Bitmap> bmp(new Graphics::Bitmap(*Storage::instance().open(Storage::instance().find(Filesystem::RelativePath(basedir + "/" + temp)))));
if (!bmp->getError()){
if (scaleSet || localScaleSet){
if (localScaleSet){
*bmp = bmp->scaleBy(localScale, localScale);
} else {
*bmp = bmp->scaleBy(scale, scale);
}
}
images[number] = bmp;
}
} else if (*token == "axis"){
// Get the axis location it defaults to 0,0
double x=0, y=0;
try {
token->view() >> x >> y;
} catch (const TokenException & ex){
}
axis.set(x,y);
} else if (*token == "window"){
// Windowed area where to clip if necessary otherwise it defaults to max
double x1=0,x2=0, y1=0,y2=0;
try {
token->view() >> x1 >> y1 >> x2 >> y2;
} catch (const TokenException & ex){
}
window.set(x1,y1,x2,y2);
} else if (*token == "velocity"){
// This allows the animation to get a wrapping scroll action going on
double x=0, y=0;
try {
token->view() >> x >> y;
} catch (const TokenException & ex){
}
velocity.set(x,y);
} else if (*token == "frame"){
Util::ReferenceCount<Element> frame(new ImageFrame(token, images, basedir));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
} else if (*token == "text"){
Util::ReferenceCount<Element> frame(new TextFrame(token));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
} else if (*token == "all"){
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceAll(token, images, basedir)));
} else if (*token == "random"){
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceRandom(token, images, basedir)));
} else if (*token == "loop"){
// start loop here
int times;
token->view() >> times;
Util::ReferenceCount<SequenceLoop> loop(new SequenceLoop(times));
loop->parse(token, images, basedir);
sequence.addSequence(loop);
/*
if (l >= (int)frames.size()){
ostringstream out;
out << "Loop location is larger than the number of frames. Loop: " << loop << " Frames: " << frames.size();
throw LoadException(__FILE__, __LINE__, out.str());
}
*/
} else if (*token == "reset"){
// Allow reset of animation
token->view() >> allowReset;
} else {
Global::debug( 3 ) << "Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
token->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu animation parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
Animation::Animation():
id(getNextId()),
depth(BackgroundBottom),
allowReset(false),
sequence(0){
}
Animation::Animation(const std::string & background):
id(getNextId()),
depth(BackgroundBottom),
allowReset(true),
sequence(0){
// add bitmap
Util::ReferenceCount<Graphics::Bitmap> bmp(new Graphics::Bitmap(*Storage::instance().open(Storage::instance().find(Filesystem::RelativePath(background)))));
if (bmp->getError()){
throw LoadException(__FILE__,__LINE__, "Problem loading file: " + background);
} else {
images[0] = bmp;
}
Util::ReferenceCount<Element> frame(new ImageFrame(bmp));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
}
Animation::Animation(const Filesystem::AbsolutePath & path):
id(getNextId()),
depth(BackgroundBottom),
allowReset(true),
sequence(0){
// add bitmap
Util::ReferenceCount<Graphics::Bitmap> bmp(new Graphics::Bitmap(*Storage::instance().open(path)));
if (bmp->getError()){
throw LoadException(__FILE__,__LINE__, "Problem loading file: " + path.path());
} else {
images[0] = bmp;
}
Util::ReferenceCount<Element> frame(new ImageFrame(bmp));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
}
Animation::Animation(Util::ReferenceCount<Graphics::Bitmap> image):
id(getNextId()),
depth(BackgroundBottom),
allowReset(true),
sequence(0){
images[0] = image;
Util::ReferenceCount<Element> frame(new ImageFrame(image));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
}
Animation::~Animation(){
}
int Animation::totalTicks() const {
return sequence.totalTicks();
}
void Animation::forward(int tickCount){
sequence.forward(tickCount, velocity.getRelativeX(), velocity.getRelativeY());
}
void Animation::reverse(int tickCount){
sequence.reverse(tickCount, velocity.getRelativeX(), velocity.getRelativeY());
}
void Animation::act(){
forward();
}
void Animation::draw(const Graphics::Bitmap & work) const {
/* FIXME: should use sub-bitmaps here */
/*const int x = window.getX();
const int y = window.getY();
const int width = window.getWidth();
const int height = window.getHeight();
Global::debug(0) << "Distance x2: " << width << " Distance y2: " << height << std::endl;
Graphics::Bitmap clipped(work, x, y, height, width);
frames[currentFrame]->draw(0, 0,clipped);*/
// Set clip from the axis default is 0,0,bitmap width, bitmap height
work.setClipRect(-window.getPosition().getDistanceFromCenterX(),
-window.getPosition().getDistanceFromCenterY(),
work.getWidth() - window.getPosition2().getDistanceFromCenterX(),
work.getHeight() - window.getPosition2().getDistanceFromCenterY());
sequence.draw(axis.getDistanceFromCenterX(), axis.getDistanceFromCenterY(), work);
work.setClipRect(0, 0, work.getWidth(), work.getHeight());
}
void Animation::draw(int x, int y, int width, int height, const Graphics::Bitmap & work) const {
const Util::ReferenceCount<Element> & frame = sequence.getCurrentFrame();
if (frame != NULL){
Graphics::Bitmap clipped(work, x, y, width, height);
frame->draw(clipped);
}
}
void Animation::forwardFrame(){
sequence.forwardFrame();
}
void Animation::backFrame(){
sequence.backFrame();
}
void Animation::reset(){
sequence.reset();
}
void Animation::resetAll(){
sequence.reset();
}
void Animation::setToEnd(){
/* FIXME */
sequence.setToEnd();
// currentFrame = frames.size()-1;
// currentLoop = 0;
// Set offsets
/*
for (std::vector<Util::ReferenceCount<Element> >::iterator i = frames.begin(); i != frames.end(); ++i){
Util::ReferenceCount<Element> frame = *i;
frame->setToEnd(RelativePoint(ticks * velocity.getRelativeX(), ticks * velocity.getRelativeY()));
}
*/
}
DrawableAnimation::DrawableAnimation(const Util::ReferenceCount<Util::Draw> & drawer, Depth depth):
drawer(drawer){
this->depth = depth;
}
+
+DrawableAnimation::DrawableAnimation(const Util::ReferenceCount<Util::Draw> & drawer):
+drawer(drawer){
+}
void DrawableAnimation::draw(const Graphics::Bitmap & work) const {
drawer->draw(work);
}
void DrawableAnimation::draw(int x, int y, int width, int height, const Graphics::Bitmap & work) const {
draw(Graphics::Bitmap(work, x, y, width, height));
}
const std::string Animation::getInfo(){
/*
static const char * ANIMATION_TEXT = "Animation ID: %d\nTicks: %d\nFrame Index: %d\nLoop From: %d\nAxis: ( %f, %f)\nVelocity: ( %f, %f)\n";
char info[255];
sprintf(info, ANIMATION_TEXT, id, ticks, currentFrame, loop, axis.getRelativeX(), axis.getRelativeY(), velocity.getRelativeX(), velocity.getRelativeY());
return std::string(info) + frames[currentFrame]->getInfo();
*/
/* FIXME */
ostringstream out;
out << "Animation ID: " << id;
return out.str();
}
AnimationManager::AnimationManager(){
// Set the current id to 0 for each context
CURRENT_ID = 0;
}
AnimationManager::AnimationManager(const AnimationManager & copy):
animations(copy.animations){
// Set the current id to 0 for each context
CURRENT_ID = 0;
}
AnimationManager::~AnimationManager(){
}
const AnimationManager & AnimationManager::operator=(const AnimationManager & copy){
animations = copy.animations;
return *this;
}
void AnimationManager::forward(int tickCount){
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
animation->forward(tickCount);
}
}
}
}
void AnimationManager::reverse(int tickCount){
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
animation->reverse(tickCount);
}
}
}
}
void AnimationManager::act(){
forward();
}
void AnimationManager::render(const Gui::Animation::Depth & depth, const Graphics::Bitmap & work) const {
std::map< Gui::Animation::Depth, std::vector< Util::ReferenceCount<Gui::Animation> > >::const_iterator animation = animations.find(depth);
if (animation != animations.end()){
const vector<Util::ReferenceCount<Gui::Animation> > & all = animation->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::const_iterator i = all.begin(); i != all.end(); ++i){
Util::ReferenceCount<Gui::Animation> animation = *i;
if (animation != NULL){
animation->draw(work);
}
}
}
}
-void AnimationManager::add(Util::ReferenceCount<Gui::Animation > animation){
+void AnimationManager::add(const Util::ReferenceCount<Gui::Animation > & animation){
animations[animation->getDepth()].push_back(animation);
}
void AnimationManager::reset(){
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
animation->resetAll();
}
}
}
}
int AnimationManager::totalTicks() const {
int count = 0;
for (map<Gui::Animation::Depth, vector<Util::ReferenceCount<Gui::Animation> > >::const_iterator it = animations.begin(); it != animations.end(); it++){
Gui::Animation::Depth depth = it->first;
const vector<Util::ReferenceCount<Gui::Animation> > & stuff = it->second;
int next = countTicks(stuff);
/* If any animation takes infinite time then the whole thing takes infinite time */
if (next == -1){
return -1;
}
if (next > count){
count = next;
}
}
return count;
}
int AnimationManager::countTicks(const vector<Util::ReferenceCount<Animation> > & toCount) const {
int total = 0;
for (vector<Util::ReferenceCount<Animation> >::const_iterator it = toCount.begin(); it != toCount.end(); it++){
const Util::ReferenceCount<Animation> & animation = *it;
int ticks = animation->totalTicks();
if (ticks == -1){
return ticks;
}
total += ticks;
}
return total;
}
void AnimationManager::setToEnd(){
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
animation->setToEnd();
}
}
}
}
const std::string AnimationManager::getInfo(int id, bool all){
std::string info("");
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
if (id == animation->getID() || all){
info += animation->getInfo();
}
}
}
}
return info;
}
const std::vector<int> AnimationManager::getIdList(){
std::vector<int> list;
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
list.push_back(animation->getID());
}
}
}
return list;
}
diff --git a/util/gui/animation.h b/util/gui/animation.h
index 456ece98..c419bc46 100644
--- a/util/gui/animation.h
+++ b/util/gui/animation.h
@@ -1,392 +1,393 @@
#ifndef _paintown_gui_animation_h
#define _paintown_gui_animation_h
#include <vector>
#include <map>
#include <string>
#include "coordinate.h"
#include "util/pointer.h"
#include "util/graphics/gradient.h"
class Filesystem;
namespace Path{
class AbsolutePath;
}
typedef Path::AbsolutePath AbsolutePath;
namespace Graphics{
class Bitmap;
}
namespace Util{
class Draw;
}
class Token;
namespace Gui{
// To hold images by number easier to access and reuse
typedef std::map<int, Util::ReferenceCount<Graphics::Bitmap> > ImageMap;
/*! Base Element class for frame types */
class Element{
public:
virtual ~Element();
virtual void act(double xvel, double yvel);
virtual void draw(int xaxis, int yaxis, const Graphics::Bitmap &) = 0;
virtual void draw(const Graphics::Bitmap &) = 0;
virtual void reset() = 0;
virtual void setToEnd(const RelativePoint &) = 0;
virtual const std::string getInfo() = 0;
virtual inline const RelativePoint getOffset() const {
return this->offset;
}
virtual inline const RelativePoint getScrollOffset() const {
return this->offset;
}
virtual inline int getTime() const {
return this->time;
}
virtual inline int getAlpha() const {
return this->alpha;
}
protected:
Element(const Token *);
RelativePoint offset;
RelativePoint scrollOffset;
int time;
int alpha;
private:
void parseToken(const Token *);
};
/*! Image Frame */
class ImageFrame : public Element{
public:
ImageFrame(const Token *, ImageMap &, const std::string &);
ImageFrame(Util::ReferenceCount<Graphics::Bitmap> bmp);
virtual ~ImageFrame();
virtual void act(double xvel, double yvel);
virtual void draw(int xaxis, int yaxis, const Graphics::Bitmap &);
virtual void draw(const Graphics::Bitmap &);
virtual void reset();
virtual void setToEnd(const RelativePoint &);
virtual const std::string getInfo();
virtual inline const Util::ReferenceCount<Graphics::Bitmap> & getBitmap() const {
return this->bmp;
}
protected:
virtual void parseToken(const Token *, const std::string &, ImageMap &);
protected:
Util::ReferenceCount<Graphics::Bitmap> bmp;
bool horizontalFlip;
bool verticalFlip;
};
/*! Text Frame */
class TextFrame: public Element {
public:
TextFrame(const Token *);
virtual ~TextFrame();
virtual void act(double xvel, double yvel);
virtual void draw(int xaxis, int yaxis, const Graphics::Bitmap &);
virtual void draw(const Graphics::Bitmap &);
virtual void reset();
virtual void setToEnd(const RelativePoint &);
virtual const std::string getInfo();
protected:
virtual void parseToken(const Token *);
/* FIXME: default this to Globals::DEFAULT_FONT */
std::string font;
std::string message;
int fontWidth, fontHeight;
Effects::Gradient gradient;
};
/* Iterates over a series of items */
class Sequence{
public:
Sequence();
virtual Util::ReferenceCount<Element> getCurrentFrame() const = 0;
virtual int totalTicks() const = 0;
virtual void setToEnd() = 0;
/* Move the sequence along by the number of ticks and at the specified speed
* Returns true if the sequence can't move any farther.
*/
virtual bool forward(int tickCount, double velocityX, double velocityY) = 0;
virtual bool reverse(int tickCount, double velocityX, double velocityY) = 0;
virtual void draw(int xaxis, int yaxis, const Graphics::Bitmap &) const = 0;
virtual void reset() = 0;
virtual void resetTicks() = 0;
/* Forcifully move to the next/previous frame */
virtual void forwardFrame() = 0;
virtual void backFrame() = 0;
virtual ~Sequence();
};
class SequenceFrame: public Sequence {
public:
SequenceFrame(const Util::ReferenceCount<Element> & frame);
virtual Util::ReferenceCount<Element> getCurrentFrame() const;
virtual int totalTicks() const;
virtual void reset();
virtual void resetTicks();
virtual void setToEnd();
virtual void draw(int xaxis, int yaxis, const Graphics::Bitmap &) const;
/* Move the sequence along by the number of ticks and at the specified speed */
virtual bool forward(int tickCount, double velocityX, double velocityY);
virtual bool reverse(int tickCount, double velocityX, double velocityY);
/* Forcifully move to the next/previous frame */
virtual void forwardFrame();
virtual void backFrame();
protected:
Util::ReferenceCount<Element> frame;
int ticks;
};
/* Shows sequences in a loop */
class SequenceLoop: public Sequence {
public:
SequenceLoop(int loops);
virtual Util::ReferenceCount<Element> getCurrentFrame() const;
virtual void reset();
virtual void resetTicks();
virtual void setToEnd();
virtual void addSequence(const Util::ReferenceCount<Sequence> & sequence);
virtual void parse(const Token * token, ImageMap & map, const std::string & baseDir);
virtual void draw(int xaxis, int yaxis, const Graphics::Bitmap &) const;
virtual int totalTicks() const;
/* Move the sequence along by the number of ticks and at the specified speed */
virtual bool forward(int tickCount, double velocityX, double velocityY);
virtual bool reverse(int tickCount, double velocityX, double velocityY);
/* Forcifully move to the next/previous frame */
virtual void forwardFrame();
virtual void backFrame();
protected:
void resetChildrenTicks();
virtual Util::ReferenceCount<Sequence> getCurrentSequence() const;
/* The current frame to display */
unsigned int currentFrame;
/* The number of times left to loop */
unsigned int currentLoop;
/* The total number of times to loop */
const unsigned int loopTimes;
std::vector<Util::ReferenceCount<Sequence> > frames;
};
/* Shows all sequences simaltaneously */
class SequenceAll: public Sequence {
public:
SequenceAll(const Token * token, ImageMap & images, const std::string & baseDir);
virtual Util::ReferenceCount<Element> getCurrentFrame() const;
virtual void reset();
virtual void resetTicks();
virtual void setToEnd();
virtual void addSequence(const Util::ReferenceCount<Sequence> & sequence);
virtual void draw(int xaxis, int yaxis, const Graphics::Bitmap &) const;
virtual int totalTicks() const;
/* Move the sequence along by the number of ticks and at the specified speed */
virtual bool forward(int tickCount, double velocityX, double velocityY);
virtual bool reverse(int tickCount, double velocityX, double velocityY);
/* Forcifully move to the next/previous frame */
virtual void forwardFrame();
virtual void backFrame();
protected:
std::vector<Util::ReferenceCount<Sequence> > sequences;
typedef std::vector<Util::ReferenceCount<Sequence> >::iterator SequenceIterator;
typedef std::vector<Util::ReferenceCount<Sequence> >::const_iterator SequenceConstIterator;
};
/* Displays a random child node for its duration */
class SequenceRandom: public Sequence {
public:
SequenceRandom(const Token * token, ImageMap & images, const std::string & baseDir);
virtual Util::ReferenceCount<Element> getCurrentFrame() const;
virtual void reset();
virtual void resetTicks();
virtual void setToEnd();
virtual void addSequence(const Util::ReferenceCount<Sequence> & sequence);
virtual void draw(int xaxis, int yaxis, const Graphics::Bitmap &) const;
virtual int totalTicks() const;
/* Move the sequence along by the number of ticks and at the specified speed */
virtual bool forward(int tickCount, double velocityX, double velocityY);
virtual bool reverse(int tickCount, double velocityX, double velocityY);
/* Forcifully move to the next/previous frame */
virtual void forwardFrame();
virtual void backFrame();
protected:
unsigned int current;
std::vector<Util::ReferenceCount<Sequence> > sequences;
typedef std::vector<Util::ReferenceCount<Sequence> >::iterator SequenceIterator;
typedef std::vector<Util::ReferenceCount<Sequence> >::const_iterator SequenceConstIterator;
};
/* Handles a series of bitmaps */
class Animation{
protected:
Animation();
public:
Animation(const Token *token);
/*! Load only a single bitmap (for backwards compatibility of backgrounds in menu) */
Animation(const std::string &);
Animation(const AbsolutePath &);
/*! use an existing bitmap */
Animation(Util::ReferenceCount<Graphics::Bitmap> image);
virtual ~Animation();
/*! Reverse with ticks */
virtual void reverse(int tickCount = 1);
/*! Forward with ticks */
virtual void forward(int tickCount = 1);
/*! Logic ticking per iteration and moving to subsequent frames */
virtual void act();
/*! Draw */
virtual void draw(const Graphics::Bitmap &) const;
virtual void draw(int x, int y, int width, int height, const Graphics::Bitmap &) const;
/*! Forward to next frame with no regard to ticks */
virtual void forwardFrame();
/*! Back a frame with no regard to ticks */
virtual void backFrame();
/*! Reset everything to the beginning of the start of the animation */
virtual void resetAll();
/*! Set everything to the end of the animation */
virtual void setToEnd();
/*! Get printable information regarding current frame */
virtual const std::string getInfo();
/*! Reset only frame ticks and other things are ignored */
virtual void reset();
/* Total number of ticks used by this animation. If any frames have a time
* of -1 then the total time will also be -1, meaning infinity.
*/
virtual int totalTicks() const;
/*! Return ID */
virtual inline int getID() const {
return id;
}
/*! Depth of animation
TODO make depth unlimited and just use integers for weight of depth
*/
enum Depth {
BackgroundBottom,
BackgroundMiddle,
BackgroundTop,
ForegroundBottom,
ForegroundMiddle,
ForegroundTop,
};
/*! Get depth */
inline const Depth & getDepth() const {
return this->depth;
}
protected:
int id;
Depth depth;
unsigned int currentSequence;
bool allowReset;
RelativePoint axis;
// This allows the frames to scroll in place
RelativePoint velocity;
Coordinate window;
// std::vector<Util::ReferenceCount<Frame> > frames;
SequenceLoop sequence;
ImageMap images;
};
/* Handles a Util::Draw, which really can't be controlled in any way. This just wraps the drawer.draw() method */
class DrawableAnimation: public Animation {
public:
DrawableAnimation(const Util::ReferenceCount<Util::Draw> & drawer, Depth depth);
+ DrawableAnimation(const Util::ReferenceCount<Util::Draw> & drawer);
virtual void draw(const Graphics::Bitmap &) const;
virtual void draw(int x, int y, int width, int height, const Graphics::Bitmap &) const;
protected:
const Util::ReferenceCount<Util::Draw> drawer;
};
/*! Generalized to for re-use in other contexts (menu, cutscene, characterselect, etc) */
class AnimationManager{
public:
AnimationManager();
AnimationManager(const AnimationManager &);
virtual ~AnimationManager();
const AnimationManager & operator=(const AnimationManager &);
void forward(int tickCount = 1);
void reverse(int tickCount = 1);
void act();
void render(const Gui::Animation::Depth &, const Graphics::Bitmap &) const;
- void add(Util::ReferenceCount<Gui::Animation > animation);
+ void add(const Util::ReferenceCount<Gui::Animation > & animation);
void reset();
void setToEnd();
int totalTicks() const;
const std::string getInfo(int id, bool all = false);
const std::vector<int> getIdList();
virtual inline const bool empty() const{
return this->animations.empty();
}
protected:
int countTicks(const std::vector<Util::ReferenceCount<Gui::Animation> > & toCount) const;
private:
std::map< Gui::Animation::Depth, std::vector< Util::ReferenceCount<Gui::Animation> > > animations;
};
}
#endif
diff --git a/util/menu/menu.cpp b/util/menu/menu.cpp
index 471a02fa..1642ec80 100644
--- a/util/menu/menu.cpp
+++ b/util/menu/menu.cpp
@@ -1,1849 +1,1853 @@
#include "util/graphics/bitmap.h"
#include "menu.h"
#include "menu_option.h"
#include "util/version.h"
#include "util/funcs.h"
#include "util/sound/sound.h"
#include "util/font.h"
#include "util/token.h"
#include "util/events.h"
#include "util/tokenreader.h"
#include "util/file-system.h"
#include "util/resource.h"
#include "util/debug.h"
#include "util/init.h"
#include "util/configuration.h"
#include "util/sound/music.h"
#include "util/graphics/gradient.h"
#include "util/exceptions/shutdown_exception.h"
#include "util/exceptions/exception.h"
#include "optionfactory.h"
#include "actionfactory.h"
#include "util/input/input-manager.h"
#include "util/input/input-map.h"
#include "util/input/input-source.h"
#include "util/parameter.h"
#include <queue>
#include <map>
#include <ostream>
#include <sstream>
#include "util/gui/context-box.h"
using namespace std;
using namespace Gui;
/* the current font is a property of the dynamic execution. so it will
* be modified by various functions that call Parameter::push
*/
static const Font & currentFont(){
return Menu::menuFontParameter.current()->get();
}
Util::Parameter<Util::ReferenceCount<Menu::FontInfo> > Menu::menuFontParameter;
/*
static std::string sharedFont = "fonts/arial.ttf";
static int sharedFontWidth = 24;
static int sharedFontHeight = 24;
*/
Effects::Gradient Menu::standardGradient(){
return Gui::standardGradient(50);
}
Menu::Point::Point():
x(0),
y(0){
}
Menu::Point::Point(int x, int y):
x(x),
y(y){
}
Menu::Point::~Point(){
}
Menu::InfoBox::InfoBox():
state(NotActive),
fadeAlpha(0){
popup.setFadeSpeed(20);
}
Menu::InfoBox::~InfoBox(){
}
void Menu::InfoBox::act(const Font & font){
popup.act(font);
int speed = 9;
switch (state){
case Opening: {
if (fadeAlpha < 255){
fadeAlpha += speed;
}
if (fadeAlpha >= 255){
fadeAlpha = 255;
if (popup.isActive()){
state = Active;
}
}
break;
}
case Closing: {
if (fadeAlpha > 0){
fadeAlpha -= speed;
}
if (fadeAlpha <= 0){
fadeAlpha = 0;
if (!popup.isActive()){
state = NotActive;
}
}
break;
}
case Active:
case NotActive:
default:
break;
}
}
void Menu::InfoBox::render(const Graphics::Bitmap &){
throw MenuException(__FILE__, __LINE__, "Don't call the render(Bitmap) function");
}
void Menu::InfoBox::render(const Graphics::Bitmap & bmp, const Font & vFont){
popup.render(bmp);
// const Font & vFont = Configuration::getMenuFont()->get(*font);
const int x1 = popup.getArea().getX()+(int)(popup.getTransforms().getRadius()/2);
const int y1 = popup.getArea().getY()+2;
const int x2 = popup.getArea().getX2()-(int)(popup.getTransforms().getRadius()/2);
const int y2 = popup.getArea().getY2()-2;
bmp.setClipRect(x1, y1, x2, y2);
// FIXME height is proportionally wrong in a majority of the cases, this is perhaps due to ftalleg.
int sy = location.getY() - vFont.getHeight()/6;// - location.getHeight()/2 - vFont.getHeight()/2;
static Graphics::Color white = Graphics::makeColor(255,255,255);
unsigned int padding_index = 0;
for (vector<string>::iterator it = text.begin(); it != text.end(); it++){
string & str = *it;
if (fadeAlpha < 255){
Graphics::Bitmap::transBlender(0, 0, 0, fadeAlpha);
vFont.printf(location.getX() + padding[padding_index]/2, sy, white, bmp.translucent(), str, 0 );
} else {
vFont.printf(location.getX() + padding[padding_index]/2, sy, white, bmp, str, 0 );
}
sy += vFont.getHeight();
padding_index++;
}
bmp.setClipRect(0, 0, bmp.getWidth(), bmp.getHeight());
}
void Menu::InfoBox::open(){
state = Opening;
popup.location = location;
popup.transforms = transforms;
popup.colors = colors;
popup.open();
fadeAlpha = 0;
}
void Menu::InfoBox::close(){
state = Closing;
popup.close();
}
/* dimensions are computed lazily when we get a font, but only compute once per font */
void Menu::InfoBox::initialize(const Font & font){
int maxWidth = 0;
int height = 0;
for (vector<string>::iterator it = text.begin(); it != text.end(); it++){
// Add the padding
ostringstream pad;
pad << (*it)[0] << (*it)[(*it).size()-1];
padding.push_back(font.textLength(pad.str().c_str()));
int w = font.textLength((*it).c_str()) + padding.back();
if (w > maxWidth){
maxWidth = w;
}
height += font.getHeight();
}
location.setDimensions(maxWidth, height);
}
void Menu::InfoBox::setText(const std::string & info){
if (info.empty()){
return;
}
text.clear();
// const Font & vFont = Configuration::getMenuFont()->get(*font);
size_t start = 0;
size_t last = 0;
start = info.find("\n");
while (start != string::npos){
text.push_back(info.substr(last, start - last));
last = start + 1;
start = info.find("\n", last);
}
text.push_back(info.substr(last));
}
static std::vector<Util::ReferenceCount<ContextItem> > toContextList(const ContextBox & context, const std::vector<Util::ReferenceCount<MenuOption> > & list){
std::vector<Util::ReferenceCount<ContextItem> > contextItems;
for (std::vector<Util::ReferenceCount<MenuOption> >::const_iterator i = list.begin(); i != list.end(); ++i){
const Util::ReferenceCount<MenuOption> & option = *i;
contextItems.push_back(option);
}
return contextItems;
}
static void tryPlaySound(const Filesystem::RelativePath & path){
Util::ReferenceCount<Sound> ok = Resource::getSound(path);
if (ok != NULL){
ok->play();
}
}
/*
* FIXME Exception handling for ValueHolder*/
Menu::MenuException::MenuException(const std::string & file, int line, const std::string reason ):
Exception::Base(file, line),
reason(reason){
}
Menu::MenuException::MenuException(const MenuException & copy):
Exception::Base(copy),
reason(copy.reason){
}
Menu::MenuException::MenuException(const Exception::Base & copy):
Exception::Base(copy),
reason("unknown"){
}
Menu::MenuException::~MenuException() throw(){
}
Exception::Base * Menu::MenuException::copy() const {
return new MenuException(*this);
}
Menu::Reload::Reload(const std::string & file, int line, const std::string reason):
MenuException(file, line, reason){
}
Menu::Reload::~Reload() throw() {
}
Exception::Base * Menu::Reload::copy() const {
return new Reload(*this);
}
Menu::ValueHolder::ValueHolder(const std::string & name):
name(name),
location(0){
}
Menu::ValueHolder::~ValueHolder(){
}
Menu::ValueHolder::ValueHolder(const ValueHolder & copy){
// reset position
this->location = 0;
this->name = copy.name;
this->values = copy.values;
}
Menu::ValueHolder & Menu::ValueHolder::operator=(const ValueHolder & copy){
// reset position
this->location = 0;
this->name = copy.name;
this->values = copy.values;
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(const std::string & val){
values.push_back(val);
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(bool val){
std::ostringstream o;
o << val;
return *this << o.str();
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(int val){
std::ostringstream o;
o << val;
return *this << o.str();
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(double val){
std::ostringstream o;
o << val;
return *this << o.str();
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(TokenView & view){
std::string temp;
view >> temp;
return *this << temp;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(std::string & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
val = values[location];
next();
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(bool & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
std::istringstream i(values[location]);
i >> val;
next();
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(int & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
std::istringstream i(values[location]);
i >> val;
next();
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(double & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
std::istringstream i(values[location]);
i >> val;
next();
return *this;
}
void Menu::ValueHolder::next(){
location++;
if (location >= values.size()){
location = 0;
}
}
const std::string Menu::ValueHolder::getValues() {
std::string temp;
for (std::vector<std::string>::iterator i = values.begin(); i != values.end(); ++i){
temp += *i + "; ";
}
return temp;
}
static bool parseDisplayList(const Token * token, ContextBox & menu){
if (*token == "display-list"){
TokenView view = token->view();
while (view.hasMore()){
const Token * tok;
view >> tok;
std::string type;
bool wrap = true;
if (tok->match("type", type)){
if (type == "normal"){
menu.setListType(ContextBox::Normal);
} else if (type == "scroll"){
menu.setListType(ContextBox::Scroll);
}
} else if (tok->match("wrap", wrap)){
menu.setListWrap(wrap);
} else if ( *tok == "items"){
Gui::ListValues values;
values.getValues(tok);
menu.setListValues(values);
}
}
return true;
}
return false;
}
Menu::Renderer::Renderer(){
}
Menu::Renderer::~Renderer(){
// Kill info boxes
for (std::vector< ::Menu::InfoBox *>::iterator i = info.begin(); i != info.end();++i){
if (*i){
delete *i;
}
}
}
/*
void Menu::Renderer::setFont(const Util::ReferenceCount<FontInfo> & font){
}
*/
void Menu::Renderer::addInfo(const std::string & text, const Gui::Widget & defaults, Context & context, const Font & font){
if (!info.empty()){
info.back()->close();
}
if (text.empty()){
return;
}
::Menu::InfoBox * temp = new ::Menu::InfoBox();
// temp->setFont(context.getFont());
temp->setText(text);
temp->initialize(font);
const int width = temp->location.getWidth();
const int height = temp->location.getHeight();
temp->location.setPosition(Gui::AbsolutePoint(context.getInfoLocation().getX() - width/2, context.getInfoLocation().getY() - height/2));
// have to pass the dimensions back in to correct proper placement
temp->location.setPosition2(Gui::AbsolutePoint(temp->location.getX() + width,temp->location.getY() + height));
temp->transforms.setRadius(defaults.transforms.getRadius());
temp->colors = defaults.colors;
temp->open();
info.push_back(temp);
}
void Menu::Renderer::actInfo(const Font & font){
for (std::vector< ::Menu::InfoBox *>::iterator i = info.begin(); i != info.end();){
::Menu::InfoBox *box = *i;
box->act(font);
if (!box->isActive()){
delete box;
i = info.erase(i);
} else {
i++;
}
}
}
void Menu::Renderer::renderInfo(const Graphics::Bitmap & work, const Font & font){
for (std::vector< ::Menu::InfoBox *>::iterator i = info.begin(); i != info.end(); ++i){
::Menu::InfoBox *box = *i;
box->render(work, font);
}
}
Menu::DefaultRenderer::DefaultRenderer():
hasOverride(false),
overrideIndex(0){
// Default the menu to a certain size and details
menu.transforms.setRadius(15);
menu.location.set(-.6, -.3, .6, .8);
menu.colors.body = Graphics::makeColor(0,0,0);
menu.colors.bodyAlpha = 128;
menu.colors.border = Graphics::makeColor(200,200,200);
menu.colors.borderAlpha = 255;
}
vector<Util::ReferenceCount<MenuOption> > Menu::DefaultRenderer::getOptions() const {
return options;
}
void Menu::DefaultRenderer::setPosition(const Gui::Coordinate & coordinate){
menu.location = coordinate;
}
void Menu::DefaultRenderer::invokeOverride(const Context & context){
if (hasOverride){
options[overrideIndex]->run(context);
throw Exception::Return(__FILE__, __LINE__);
}
}
Menu::DefaultRenderer::~DefaultRenderer(){
}
Menu::Renderer::Type Menu::DefaultRenderer::getType() const {
return Default;
}
bool Menu::DefaultRenderer::readToken(const Token * token, const OptionFactory & factory){
if( *token == "option" ) {
try{
MenuOption * temp = factory.getOption(menu, token);
if (temp){
// Check for info/name
{
TokenView view = token->view();
while (view.hasMore()){
const Token * tok;
view >> tok;
try{
if (*tok == "info"){
temp->addInfo(tok);
} else if (*tok == "name"){
temp->addName(tok);
}
} catch (const TokenException & ex){
// Output something
}
}
}
options.push_back(Util::ReferenceCount<MenuOption>(temp));
if (!hasOverride){
const Token * tok;
token->view() >> tok;
if (tok->findToken("_/override") != NULL){
overrideIndex = options.size()-1;
hasOverride = true;
}
}
}
} catch (const LoadException & le){
Global::debug(0) << "Could not read option: " << le.getTrace() << endl;
token->print(" ");
}
} else if ( *token == "position" ) {
// This handles the placement of the menu list and surrounding box
menu.setCoordinates(token);
} else if ( *token == "relative-position"){
menu.setCoordinates(token);
} else if ( *token == "coordinate"){
menu.setCoordinates(token);
} else if ( *token == "position-body" ) {
// This handles the body color of the menu box
menu.setColors(token);
} else if ( *token == "position-border" ) {
// This handles the border color of the menu box
menu.setColors(token);
} else if ( *token == "transforms" ) {
// This handles the border color of the menu box
menu.setTransforms(token);
} else if ( *token == "fade-speed" ) {
// Menu fade in speed
int speed;
token->view() >> speed;
menu.setFadeSpeed(speed);
} else if ( parseDisplayList(token, menu) ){
} else {
return false;
}
return true;
}
void Menu::DefaultRenderer::initialize(Context & context){
menu.setList(toContextList(menu, options));
menu.open();
// const Font & font = Configuration::getMenuFont()->get(context.getFont()->get());
const Font & font = currentFont();
// Menu info
if (!context.getMenuInfoText().empty()){
menuInfo.setText(context.getMenuInfoText());
menuInfo.initialize(font);
const int width = menuInfo.location.getWidth();
const int height = menuInfo.location.getHeight();
menuInfo.location.setPosition(Gui::AbsolutePoint(context.getMenuInfoLocation().getX() - width/2, context.getMenuInfoLocation().getY() - height/2));
// have to pass the dimensions back in to correct proper placement
menuInfo.location.setPosition2(Gui::AbsolutePoint(menuInfo.location.getX() + width, menuInfo.location.getY() + height));
menuInfo.transforms.setRadius(menu.transforms.getRadius());
menuInfo.colors = menu.colors;
}
menuInfo.open();
// Add first info option
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
void Menu::DefaultRenderer::finish(){
menu.close();
menuInfo.close();
}
bool Menu::DefaultRenderer::active(){
return menu.isActive();
}
void Menu::DefaultRenderer::act(const Context & context){
// const Font & font = Configuration::getMenuFont()->get(context.getFont()->get());
const Font & font = currentFont();
// FIXME find a better way to get options to update this is a waste
for (std::vector<Util::ReferenceCount<MenuOption> >::iterator i = options.begin(); i != options.end(); ++i){
Util::ReferenceCount<MenuOption> & option = *i;
option->logic();
}
menu.act(font);
menuInfo.act(font);
actInfo(font);
}
void Menu::DefaultRenderer::render(const Graphics::Bitmap & bmp, const Font & font){
menu.render(bmp, font);
menuInfo.render(bmp, font);
renderInfo(bmp, font);
}
void Menu::DefaultRenderer::addOption(MenuOption * opt){
this->options.push_back(Util::ReferenceCount<MenuOption>(opt));
}
void Menu::DefaultRenderer::doAction(const Actions & action, Context & context){
const Font & font = currentFont();
switch(action){
case Up:
if (menu.previous(font)){
context.playSound(Up);
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
break;
case Down:
if (menu.next(font)){
context.playSound(Down);
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
break;
case Left:
if (options[menu.getCurrentIndex()]->leftKey()){
// setFont(context.getFont());
context.playSound(Left);
}
break;
case Right:
if (options[menu.getCurrentIndex()]->rightKey()){
// setFont(context.getFont());
context.playSound(Right);
}
break;
case Select: {
if (options[menu.getCurrentIndex()]->isRunnable()){
try{
context.playSound(Select);
options[menu.getCurrentIndex()]->run(context);
} catch (const Reload & reload){
menu.open();
menuInfo.open();
}
// setFont(context.getFont());
context.playMusic();
/* font might have been recreated */
const Font & font = currentFont();
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
menuInfo.initialize(font);
}
break;
}
case Cancel:
context.playSound(Cancel);
throw Exception::Return(__FILE__, __LINE__);
break;
default:
break;
}
}
Menu::TabInfo::TabInfo(){
}
Menu::TabInfo::~TabInfo(){
}
void Menu::TabInfo::act(){
// Miguel: FIXME find a better way to get options to update this is a waste
// Jon: Whats wrong with it?
for (std::vector<Util::ReferenceCount<MenuOption> >::iterator i = options.begin(); i != options.end(); ++i){
Util::ReferenceCount<MenuOption> & option = *i;
option->logic();
}
}
Menu::TabRenderer::TabRenderer():
hasOverride(false),
overrideIndex(0){
// Default the menu to a certain size and details
//menu.transforms.setRadius(15);
menu.location.set(-.6, -.3, .6, .8);
menu.colors.body = Graphics::makeColor(0,0,0);
menu.colors.bodyAlpha = 128;
menu.colors.border = Graphics::makeColor(200,200,200);
menu.colors.borderAlpha = 255;
}
Menu::Renderer::Type Menu::TabRenderer::getType() const {
return Tabbed;
}
void Menu::TabRenderer::setPosition(const Gui::Coordinate & coordinate){
menu.location = coordinate;
}
vector<Util::ReferenceCount<MenuOption> > Menu::TabRenderer::getOptions() const {
vector<Util::ReferenceCount<MenuOption> > options;
for (vector<TabInfo *>::const_iterator it = tabs.begin(); it != tabs.end(); ++it){
options.insert(options.end(), (*it)->options.begin(), (*it)->options.end());
}
return options;
}
/* FIXME need to implement override for tabs */
void Menu::TabRenderer::invokeOverride(const Context & context){
if (hasOverride){
//options[overrideIndex]->run(context);
throw Exception::Return(__FILE__, __LINE__);
}
}
Menu::TabRenderer::~TabRenderer(){
// Kill tabs
for (std::vector<TabInfo *>::iterator i = tabs.begin(); i != tabs.end(); ++i){
if (*i){
delete *i;
}
}
}
bool Menu::TabRenderer::readToken(const Token * token, const OptionFactory & factory){
if (*token == "menu"){
TabInfo * tabInfo = new TabInfo();
Gui::Tab * tab = new Gui::Tab();
TokenView view = token->view();
while (view.hasMore()){
const Token * tok;
view >> tok;
try{
if (*tok == "name"){
tok->view() >> tabInfo->name;
tok->view() >> tab->name;
} else if (*tok == "info"){
tok->view() >> tabInfo->info;
} else if (*tok == "menuinfo"){
tok->view() >> tabInfo->menuInfo;
} else if (parseDisplayList(tok, tab->getContext())){
} else if (*tok == "option"){
try {
MenuOption *temp = factory.getOption(tab->getContext(), tok);
if (temp){
// Check for info/name
{
TokenView info = tok->view();
while (info.hasMore()){
const Token * check;
info >> check;
try{
if (*check == "info"){
temp->addInfo(check);
} else if (*check == "name"){
temp->addName(check);
}
} catch (const TokenException & ex){
// Output something
}
}
}
Util::ReferenceCount<MenuOption> ref(temp);
tabInfo->options.push_back(ref);
tab->addOption(ref);
// tab->addOption(ref->getAsScrollItem<ContextItem>(tab->getContext()));
}
} catch (const LoadException & le){
tok->print(" ");
}
}
} catch (const TokenException & ex){
// Output something
}
}
menu.addTab(tab);
tabs.push_back(tabInfo);
} else if ( *token == "position" ) {
// This handles the placement of the menu list and surrounding box
menu.setCoordinates(token);
} else if ( *token == "relative-position"){
menu.setCoordinates(token);
} else if ( *token == "coordinate"){
menu.setCoordinates(token);
} else if ( *token == "position-body" ) {
// This handles the body color of the menu box
menu.setColors(token);
} else if ( *token == "position-border" ) {
// This handles the border color of the menu box
menu.setColors(token);
} else if ( *token == "transforms" ) {
// This handles the border color of the menu box
menu.setTransforms(token);
} else if ( *token == "tab-body" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.tabColors.bodyAlpha;
menu.tabColors.body = Graphics::makeColor(r,g,b);
} else if ( *token == "tab-border" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.tabColors.borderAlpha;
menu.tabColors.border = Graphics::makeColor(r,g,b);
} else if ( *token == "selectedtab-body" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.selectedTabColors.bodyAlpha;
menu.selectedTabColors.body = Graphics::makeColor(r,g,b);
} else if ( *token == "selectedtab-border" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.selectedTabColors.borderAlpha;
menu.selectedTabColors.border = Graphics::makeColor(r,g,b);
} else if ( *token == "runningtab-body" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.runningTabColors.bodyAlpha;
menu.runningTabColors.body = Graphics::makeColor(r,g,b);
} else if ( *token == "runningtab-border" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.runningTabColors.borderAlpha;
menu.runningTabColors.border = Graphics::makeColor(r,g,b);
} else if ( *token == "font-color" ) {
int r,g,b;
token->view() >> r >> g >> b;
menu.setTabFontColor(Graphics::makeColor(r,g,b));
} else if ( *token == "selectedfont-color" ) {
int r,g,b;
token->view() >> r >> g >> b;
menu.setSelectedTabFontColor(Graphics::makeColor(r,g,b));
} else if ( *token == "runningfont-color" ) {
} else if ( *token == "fade-speed" ) {
// Menu fade in speed
int speed;
token->view() >> speed;
//menu.setFadeSpeed(speed);
} else {
return false;
}
return true;
}
void Menu::TabRenderer::initialize(Context & context){
const Font & font = currentFont();
// Menu info
if (!context.getMenuInfoText().empty()){
menuInfo.setText(context.getMenuInfoText());
menuInfo.initialize(font);
const int width = menuInfo.location.getWidth();
const int height = menuInfo.location.getHeight();
menuInfo.location.setPosition(Gui::AbsolutePoint(context.getMenuInfoLocation().getX() - width/2, context.getMenuInfoLocation().getY() - height/2));
// have to pass the dimensions back in to correct proper placement
menuInfo.location.setPosition2(Gui::AbsolutePoint(menuInfo.location.getX() + width,menuInfo.location.getY() + height));
menuInfo.transforms.setRadius(menu.transforms.getRadius());
menuInfo.colors = menu.colors;
}
menuInfo.open();
if (tabs.size() > 0 && tabs[menu.getCurrentTab()]->options.size() > menu.getCurrentIndex()){
// Add first info option
addInfo(tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
}
void Menu::TabRenderer::finish(){
//menu.close();
menuInfo.close();
}
bool Menu::TabRenderer::active(){
return true;//menu.isActive();
}
void Menu::TabRenderer::act(const Context & context){
const Font & font = currentFont();
// FIXME find a better way to get options to update this is a waste
for (std::vector<TabInfo *>::iterator i = tabs.begin(); i != tabs.end(); ++i){
TabInfo * tab = *i;
tab->act();
}
menu.act(font);
menuInfo.act(font);
actInfo(font);
}
void Menu::TabRenderer::render(const Graphics::Bitmap & bmp, const Font & font){
menu.render(bmp, font);
menuInfo.render(bmp, font);
renderInfo(bmp, font);
}
void Menu::TabRenderer::addOption(MenuOption * opt){
//this->options.push_back(opt);
}
void Menu::TabRenderer::doAction(const Actions & action, Context & context){
const Font & font = currentFont();
bool updateInfo = false;
switch (action){
case Up:
menu.up(font);
context.playSound(Up);
updateInfo = true;
break;
case Down:
menu.down(font);
context.playSound(Down);
updateInfo = true;
break;
case Left:
menu.left(font);
context.playSound(Up);
updateInfo = true;
break;
case Right:
menu.right(font);
context.playSound(Down);
updateInfo = true;
break;
case Select:
try{
if (!menu.isInTab()){
menu.toggleTabSelect();
} else {
if (menu.getCurrentTab() < tabs.size() &&
menu.getCurrentIndex() < tabs[menu.getCurrentTab()]->options.size() &&
tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->isRunnable()){
tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->run(context);
}
// tabs[menu.getCurrentTab()]->run(menu.getCurrentIndex(), context);
}
context.playSound(Select);
} catch (const Exception::Return & ex){
menuInfo.open();
}
context.playMusic();
updateInfo = true;
break;
case Cancel:
context.playSound(Cancel);
if (menu.isInTab()){
menu.toggleTabSelect();
} else {
throw Exception::Return(__FILE__, __LINE__);
}
break;
default:
break;
}
if (updateInfo){
if (tabs.size() > menu.getCurrentTab() &&
tabs[menu.getCurrentTab()]->options.size() > menu.getCurrentIndex()){
addInfo(tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
}
}
Menu::Context::Context():
cleanup(true),
state(NotStarted),
fades(0),
font(NULL),
infoLocation(0, -.5),
menuInfoLocation(0,.95){
}
Menu::Context::Context(const Context & parent, const Context & child):
cleanup(false),
state(NotStarted),
fades(NULL),
font(NULL),
infoLocation(0,-.5),
menuInfoLocation(0,.95),
/* include child's languages?? */
languages(parent.getLanguages()){
// Update with parents info
fades = parent.fades;
background = parent.background;
sounds = parent.sounds;
music = parent.music;
// font = parent.font;
infoLocation = parent.infoLocation;
menuInfoLocation = parent.menuInfoLocation;
// Then overwrite with childs
if (child.fades != NULL){
fades = child.fades;
}
if (!child.background.empty()){
background = child.background;
}
if (!child.sounds.empty()){
sounds = child.sounds;
}
if (Storage::instance().exists(child.music)){
music = child.music;
}
if (child.hasFont()){
font = child.getFontInfo();
} else if (parent.hasFont()){
font = parent.getFontInfo();
}
/* what are these magic numbers -.5 and .95? */
if (child.infoLocation.getRelativeX() != 0 || child.infoLocation.getRelativeY() != -.5){
infoLocation = child.infoLocation;
}
if (child.menuInfoLocation.getRelativeX() != 0 || child.menuInfoLocation.getRelativeY() != .95){
menuInfoLocation = child.menuInfoLocation;
}
if (!child.menuInfo.empty()){
menuInfo = child.menuInfo;
}
}
Menu::Context::~Context(){
// Only delete if required
if (cleanup){
if (fades != NULL){
delete fades;
}
}
}
std::vector<std::string> Menu::Context::getLanguages() const {
return languages;
}
void Menu::Context::setLanguages(const std::vector<std::string> & languages){
this->languages = languages;
}
bool Menu::Context::hasFont() const {
return font != NULL;
}
void Menu::Context::parseToken(const Token * token){
if ( *token != "context" ){
throw LoadException(__FILE__, __LINE__, "Not a menu context");
} else if (!token->hasTokens()){
return;
}
// Token
const Token * tok;
token->view() >> tok;
TokenView view = tok->view();
while (view.hasMore()){
const Token * context;
view >> context;
if (*context == "fade"){
// Fade info
if (!fades){
fades = new Gui::FadeTool();
}
// Set fader default to white
fades->setFadeInColor(Graphics::makeColor(255,255,255));
fades->setFadeOutColor(Graphics::makeColor(255,255,255));
fades->setFadeInTime(25);
fades->setFadeOutTime(12);
// Load defaults
fades->parseDefaults(context);
} else if (*context == "animation" || *context == "background"){
// Backgrounds
addBackground(context);
} else if (*context == ""){
}
}
}
void Menu::Context::addBackground(const Token * token){
// Backgrounds
/*if (background == NULL){
background = new Gui::AnimationManager();
}*/
background.add(Util::ReferenceCount<Gui::Animation>(new Gui::Animation(token)));
}
+
+void Menu::Context::addBackground(const Util::ReferenceCount<Gui::Animation> & animation){
+ background.add(animation);
+}
void Menu::Context::addBackground(const Graphics::Bitmap & image){
/*if (background == NULL){
background = new Gui::AnimationManager();
}*/
background.add(Util::ReferenceCount<Gui::Animation>(new Gui::Animation(Util::ReferenceCount<Graphics::Bitmap>(new Graphics::Bitmap(image)))));
}
void Menu::Context::addBackground(const std::string & image){
// Backgrounds
/*if (background == NULL){
background = new Gui::AnimationManager();
}*/
background.add(Util::ReferenceCount<Gui::Animation>(new Gui::Animation(image)));
}
void Menu::Context::initialize(){
if (fades){
// state
state = Initializing;
// set fader state
fades->setState(FadeTool::FadeIn);
} else {
// Running
state = Running;
}
}
void Menu::Context::finish(){
if (fades){
// state
state = Finishing;
// set fader state
fades->setState(FadeTool::FadeOut);
} else {
// Completed
state = Completed;
}
}
void Menu::Context::playSound(const Actions & sound) const {
map<Actions, Filesystem::RelativePath>::const_iterator find = sounds.find(sound);
if (find != sounds.end() && Storage::instance().exists(find->second)){
tryPlaySound(find->second);
}
}
void Menu::Context::addSound(const Actions & sound, const Filesystem::RelativePath & path){
sounds[sound] = path;
}
void Menu::Context::playMusic(){
if (Storage::instance().exists(music)){
if (Music::loadSong(Storage::instance().find(music).path())){
Music::pause();
Music::play();
}
}
}
void Menu::Context::act(){
// fader
if (fades){
fades->act();
if (state == Initializing){
if(fades->getState() == Gui::FadeTool::NoFade){
state = Running;
}
} else if (state == Finishing){
if(fades->getState() == Gui::FadeTool::EndFade){
state = Completed;
}
}
}
// Backgrounds
background.act();
}
void Menu::Context::renderBackground(const Graphics::Bitmap & bmp) const {
if (!background.empty()){
// background
background.render(Gui::Animation::BackgroundBottom, bmp);
background.render(Gui::Animation::BackgroundMiddle, bmp);
background.render(Gui::Animation::BackgroundTop, bmp);
} else {
bmp.fill(Graphics::makeColor(0,0,0));
}
}
void Menu::Context::renderForeground(const Graphics::Bitmap & bmp) const {
if (!background.empty()){
// foreground
background.render(Gui::Animation::ForegroundBottom, bmp);
background.render(Gui::Animation::ForegroundMiddle, bmp);
background.render(Gui::Animation::ForegroundTop, bmp);
}
}
void Menu::Context::render(const Util::ReferenceCount<Renderer> & renderer, const Graphics::Bitmap & bmp) const {
renderBackground(bmp);
// Menu
if (renderer != NULL){
renderer->render(bmp, currentFont());
}
renderForeground(bmp);
// Fades
if (fades){
fades->draw(bmp);
}
}
void Menu::Context::setFadeTool(Gui::FadeTool *fade){
fades = fade;
}
/*
void Menu::Context::setBackground(Background *bg){
background = bg;
}
*/
/* New Menu */
// Utilizes default renderer
Menu::Menu::Menu(const Renderer::Type & type):
type(type){
setRenderer(type);
}
Menu::Menu::Menu(const Util::ReferenceCount<Renderer> & renderer):
renderer(renderer),
type(renderer->getType()){
}
Menu::Menu::Menu(const Filesystem::AbsolutePath & filename, const Renderer::Type & type):
type(type){
// Load up tokenizer
try{
Global::debug(1,"menu") << "Loading menu " << filename.path() << endl;
TokenReader tr;
Token * token = tr.readTokenFromFile(*Storage::instance().open(filename));
OptionFactory defaultFactory;
load(token, defaultFactory);
} catch (const TokenException & e){
throw LoadException(__FILE__, __LINE__, e, "Error loading menu");
}
}
Menu::Menu::Menu(const Filesystem::AbsolutePath & filename, const OptionFactory & factory, const Renderer::Type & type):
renderer(0),
type(type){
// Load up tokenizer
try{
Global::debug(1,"menu") << "Loading menu " << filename.path() << endl;
TokenReader tr;
Token * token = tr.readTokenFromFile(*Storage::instance().open(filename));
load(token, factory);
} catch (const TokenException & e){
throw LoadException(__FILE__, __LINE__, e, "Error loading menu");
}
}
Menu::Menu::Menu(const Token * token, const Renderer::Type & type):
renderer(0),
type(type){
OptionFactory defaultFactory;
load(token, defaultFactory);
}
Menu::Menu::Menu(const Token * token, const OptionFactory & factory, const Renderer::Type & type):
renderer(0),
type(type){
load(token, factory);
}
Menu::Menu::~Menu(){
// Kill values
for (std::map<string,ValueHolder *>::iterator i = data.begin(); i != data.end(); ++i){
if (i->second){
delete i->second;
}
}
}
void Menu::Menu::setPosition(const Gui::Coordinate & coordinate){
if (renderer != NULL){
renderer->setPosition(coordinate);
}
}
void Menu::Menu::setFont(const Util::ReferenceCount<FontInfo> & font){
context.setFont(font);
/*
if (renderer){
renderer->setFont(font);
}
*/
}
void Menu::Menu::load(const Token * token, const OptionFactory & factory){
// version info;
int major=0, minor=0, micro=0;
if (!token->hasTokens()){
throw LoadException(__FILE__, __LINE__, "Empty Menu");
} else {
const Token *ourToken = token->findToken("_/type");
if (ourToken != NULL){
try {
std::string menuType;
ourToken->view() >> menuType;
if (menuType == "default"){
type = Renderer::Default;
} else if (menuType == "tabbed"){
type = Renderer::Tabbed;
}
} catch (const TokenException & ex){
}
}
ourToken = token->findToken("_/version");
if (ourToken != NULL){
try {
ourToken->view() >> major >> minor >> micro;
} catch (const TokenException & ex){
}
} else {
Global::debug(0, "menu") << "No version indicated, assuming 3.3.1 or below." << endl;
major = 3;
minor = 3;
micro = 1;
}
}
setRenderer(type);
if (Version::getVersion(major, minor, micro) != Version::getVersion()){
// Do compatible translations if necessary
handleCompatibility(token, Version::getVersion(major, minor, micro), factory);
} else {
handleCurrentVersion(token);
}
}
typedef Menu::Menu MenuClass;
class LanguageMenu: public Menu::Menu {
public:
class LanguageOption: public MenuOption {
public:
LanguageOption(const Gui::ContextBox & box, const string & language):
MenuOption(box, NULL){
setText(language);
setInfoText(language);
}
virtual void logic(){
}
virtual void run(const ::Menu::Context & context){
Configuration::setLanguage(getText());
Configuration::saveConfiguration();
throw ::Menu::MenuException(__FILE__, __LINE__);
}
};
static vector<string> findLanguages(const MenuClass & original){
return original.getLanguages();
}
static vector<string> putEnglishFirst(vector<string> languages){
vector<string> out;
bool haveEnglish = false;
for (vector<string>::iterator it = languages.begin(); it != languages.end(); it++){
const string & name = *it;
if (name == "English"){
out.insert(out.begin(), name);
haveEnglish = true;
} else {
out.push_back(name);
}
}
/* We should always have at least english available */
if (!haveEnglish){
out.insert(out.begin(), "English");
}
return out;
}
LanguageMenu(const MenuClass & original){
Util::ReferenceCount< ::Menu::DefaultRenderer > renderer = getRenderer();
vector<string> languages = putEnglishFirst(findLanguages(original));
for (vector<string>::iterator it = languages.begin(); it != languages.end(); it++){
addOption(new LanguageOption(renderer->getBox(), *it));
}
}
};
void Menu::Menu::setupDefaultLanguage(const Context & context, const MenuClass & parent){
LanguageMenu menu(parent);
menu.setFont(Util::ReferenceCount<FontInfo>(new RelativeFontInfo(Font::getDefaultFontPath(), 24, 24)));
Configuration::setLanguage("English");
try{
menu.run(context);
} catch (const ::Menu::MenuException & ignore){
}
}
void Menu::Menu::openOptions(){
vector<Util::ReferenceCount<MenuOption> > options = getRenderer()->getOptions();
for (vector<Util::ReferenceCount<MenuOption> >::iterator it = options.begin(); it != options.end(); it++){
Util::ReferenceCount<MenuOption> & option = *it;
option->open();
}
}
void Menu::Menu::closeOptions(){
vector<Util::ReferenceCount<MenuOption> > options = getRenderer()->getOptions();
for (vector<Util::ReferenceCount<MenuOption> >::iterator it = options.begin(); it != options.end(); it++){
Util::ReferenceCount<MenuOption> & option = *it;
option->close();
}
}
vector<string> Menu::Menu::getLanguages() const {
return languages;
}
void Menu::Menu::run(const Context & parentContext){
/* TODO: replace with Parameter */
Keyboard::pushRepeatState(true);
try{
// Setup context from parent and this menu and initialize
Context localContext(parentContext, context);
Util::Parameter<Util::ReferenceCount<FontInfo> > currentFont(menuFontParameter);
if (context.hasFont()){
currentFont.push(context.getFontInfo());
}
localContext.initialize();
/* Not sure if this is the right place to set the languages.
* For now the semantics is that if a sub-menu specifies a set
* of languages then we will use those, otherwise the
* languages will come from the parentContext.
*
* getLanguages() is supposed to hold at least one language
* which might be English, the default.
*
* This logic is sort of lame.. fix it.
*/
if (getLanguages().size() > 1 || (getLanguages().size() == 1 && getLanguages()[0] != "English")){
localContext.setLanguages(getLanguages());
}
// Setup menu fonts etc
if (renderer != NULL){
renderer->initialize(localContext);
// Invoke Override if available
renderer->invokeOverride(localContext);
}
//Play music
localContext.playMusic();
if (Configuration::getLanguage() == ""){
setupDefaultLanguage(localContext, *this);
}
/* do any lazy loading options want to do */
openOptions();
/* vi keys -- make these optional? */
input.set(Keyboard::Key_J, 0, true, Down);
input.set(Keyboard::Key_K, 0, true, Up);
input.set(Keyboard::Key_H, 0, true, Left);
input.set(Keyboard::Key_L, 0, true, Right);
/* regular keys */
input.set(Configuration::getUp(0), 0, true, Up);
input.set(Configuration::getDown(0), 0, true, Down);
input.set(Configuration::getLeft(0), 0, true, Left);
input.set(Configuration::getRight(0), 0, true, Right);
input.set(Configuration::getAttack1(0), 0, true, Select);
/* FIXME: use configuration keys */
input.set(Keyboard::Key_ENTER, 0, true, Select);
input.set(Keyboard::Key_ESC, 0, true, Cancel);
/* joystick */
input.set(Joystick::Up, 0, true, Up);
input.set(Joystick::Down, 0, true, Down);
input.set(Joystick::Left, 0, true, Left);
input.set(Joystick::Right, 0, true, Right);
/*! FIXME this should be changed to Select/Cancel buttons, all other buttons should be Select */
input.set(Joystick::Button1, 0, true, Select);
input.set(Joystick::Button2, 0, true, Select);
input.set(Joystick::Button3, 0, true, Select);
input.set(Joystick::Button4, 0, true, Select);
input.set(Joystick::Start, 0, true, Select);
input.set(Joystick::Quit, 0, true, Cancel);
class Logic: public Util::Logic {
public:
Logic(Menu & menu, Context & localContext, const Util::ReferenceCount<Renderer> & renderer):
menu(menu),
localContext(localContext),
renderer(renderer){
}
Menu & menu;
Context & localContext;
Util::ReferenceCount<Renderer> renderer;
void run(){
try {
menu.act(localContext);
} catch (const Exception::Return & ex){
// signaled to quit current menu, closing this one out
localContext.finish();
if (renderer != NULL){
renderer->finish();
}
}
}
double ticks(double system){
return system * Global::ticksPerSecond(90);
}
bool done(){
return localContext.getState() == Context::Completed ||
!(renderer != NULL && renderer->active());
}
};
class Draw: public Util::Draw {
public:
Draw(Menu & menu, Context & localContext):
menu(menu),
localContext(localContext){
}
Menu & menu;
Context & localContext;
void draw(const Graphics::Bitmap & buffer){
Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
Util::Parameter<Util::ReferenceCount<FontInfo> > currentFont(menuFontParameter);
if (Configuration::hasMenuFont()){
currentFont.push(Configuration::getMenuFont());
}
work.start();
menu.render(localContext, work);
work.finish();
// buffer.BlitToScreen();
}
};
Logic logic(*this, localContext, renderer);
Draw draw(*this, localContext);
Util::standardLoop(logic, draw);
closeOptions();
// FIXME Menu is finished, lets return. Is this even required anymore?
throw Exception::Return(__FILE__, __LINE__);
} catch (...){
Keyboard::popRepeatState();
throw;
}
}
void Menu::Menu::act(Context & ourContext){
InputSource source(true);
// Keys
vector<InputMap<Actions>::InputEvent> events = InputManager::getEvents(input, source);
for (vector<InputMap<Actions>::InputEvent>::iterator it = events.begin(); it != events.end(); it++){
InputMap<Actions>::InputEvent event = *it;
if (!event.enabled){
continue;
}
if (event.out == Cancel){
if (renderer != NULL){
InputManager::waitForRelease(input, source, Cancel);
renderer->doAction(Cancel, ourContext);
} else {
ourContext.playSound(Cancel);
InputManager::waitForRelease(input, source, Cancel);
throw Exception::Return(__FILE__, __LINE__);
}
}
if (renderer != NULL){
switch (event.out){
case Up: renderer->doAction(Up, ourContext); break;
case Down: renderer->doAction(Down, ourContext); break;
case Left: renderer->doAction(Left, ourContext); break;
case Right: renderer->doAction(Right, ourContext); break;
case Select: renderer->doAction(Select, ourContext); break;
default: break;
}
}
}
if (renderer != NULL){
Util::Parameter<Util::ReferenceCount<FontInfo> > currentFont(menuFontParameter);
if (Configuration::hasMenuFont()){
currentFont.push(Configuration::getMenuFont());
}
renderer->act(ourContext);
}
// Act context
ourContext.act();
}
void Menu::Menu::render(const Context & ourContext, const Graphics::Bitmap & bmp) const {
// Render context
ourContext.render(renderer, bmp);
}
std::string Menu::Menu::getName(){
std::string name;
try {
if (data["name"]){
*data["name"] >> name;
}
} catch (const MenuException & ex){
}
return name;
}
std::string Menu::Menu::getInfo(){
std::string name;
try {
if (data["info"]){
*data["info"] >> name;
}
} catch (const MenuException & ex){
}
return name;
}
void Menu::Menu::addData(ValueHolder * item){
std::pair<std::map<std::string,ValueHolder *>::iterator,bool> check;
check = data.insert( std::pair<std::string,ValueHolder *>(item->getName(),item) );
if (check.second == false){
Global::debug(0,"menu") << "Value \"" << check.first->second->getName() << "\" already exists - (" << check.first->second->getValues() << ")." << endl;
Global::debug(0,"menu") << "Replacing with value \"" << item->getName() << "\" - (" << item->getValues() << ")." << endl;
data[item->getName()] = item;
}
}
void Menu::Menu::handleCurrentVersion(const Token * token){
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
// Newer items
if (*tok == "val" || *tok == "value"){
const Token * val;
tok->view() >> val;
ValueHolder * value = new ValueHolder(val->getName());
TokenView valueView = val->view();
try {
while (true){
*value << valueView;
}
} catch (const TokenException & ex){
}
addData(value);
} else if (*tok == "context"){
context.parseToken(tok);
} else {
Global::debug(3,"menu") <<"Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch (const LoadException & ex){
throw ex;
} catch (const Filesystem::NotFound & ex){
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
}
}
}
void Menu::Menu::handleCompatibility(const Token * token, int version, const OptionFactory & factory){
Global::debug(1,"menu") << "Trying version: " << version << endl;
if (version <= Version::getVersion(3, 3, 1)){
const Token * languages = token->findToken("_/languages");
if (languages != NULL){
try{
TokenView view = languages->view();
while (true){
string language;
view >> language;
this->languages.push_back(language);
}
} catch (const TokenException & fail){
}
}
if (this->languages.size() == 0){
this->languages.push_back("English");
}
TokenView view = token->view();
while (view.hasMore()){
try {
const Token * tok;
view >> tok;
if ( *tok == "name" ){
ValueHolder * value = new ValueHolder("name");
TokenView nameView = tok->view();
*value << nameView;
addData(value);
} else if ( *tok == "music" ) {
ValueHolder * value = new ValueHolder("music");
TokenView musicView = tok->view();
*value << musicView;
addData(value);
try {
std::string music;
*value >> music;
context.setMusic(Filesystem::RelativePath(music));
} catch (const MenuException & ex){
}
} else if( *tok == "select-sound" ) {
ValueHolder * value = new ValueHolder("select-sound");
TokenView soundView = tok->view();
*value << soundView;
addData(value);
try{
std::string sound;
*value >> sound;
context.addSound(Up, Filesystem::RelativePath(sound));
context.addSound(Down, Filesystem::RelativePath(sound));
} catch (const MenuException & ex){
}
} else if (*tok == "back-sound"){
ValueHolder * value = new ValueHolder("back-sound");
TokenView soundView = tok->view();
*value << soundView;
addData(value);
try{
std::string sound;
*value >> sound;
context.addSound(Back,Filesystem::RelativePath(sound));
context.addSound(Cancel,Filesystem::RelativePath(sound));
} catch (const MenuException & ex){
}
} else if (*tok == "ok-sound"){
ValueHolder * value = new ValueHolder("ok-sound");
TokenView okView = tok->view();
*value << okView;
addData(value);
try{
std::string sound;
*value >> sound;
context.addSound(Select,Filesystem::RelativePath(sound));
} catch (const MenuException & ex){
}
} else if ( *tok == "background" ) {
std::string temp;
tok->view() >> temp;
context.addBackground(temp);
} else if (*tok == "anim"){
context.addBackground(tok);
} else if ( *tok == "clear-color" ) {
// Not necessary ignore
} else if (renderer != NULL && renderer->readToken(tok, factory)){
// Nothing checks compatible version of renderer
} else if ( *tok == "font" ) {
ValueHolder * value = new ValueHolder("font");
TokenView fontView = tok->view();
*value << fontView << fontView << fontView;
addData(value);
try {
std::string font;
int w = 24, h = 24;
*value >> font >> w >> h;
/*context.setFont(Filesystem::RelativePath(font));
context.setFontWidth(w);
context.setFontHeight(h);*/
context.setFont(Util::ReferenceCount<FontInfo>(new RelativeFontInfo(Filesystem::RelativePath(font), w, h)));
} catch (const MenuException & ex){
}
} else if ( *tok == "action"){
// Set speed
//ActionAct(tok);
} else if ( *tok == "info-position"){
ValueHolder * value = new ValueHolder("info-position");
TokenView infoView = tok->view();
*value << infoView << infoView;
addData(value);
try {
double x=0, y=-.5;
*value >> x >> y;
context.setInfoLocation(x,y);
} catch (const MenuException & ex){
}
} else if (*tok == "menuinfo"){
ValueHolder * value = new ValueHolder("menuinfo");
TokenView infoView = tok->view();
*value << infoView;
addData(value);
try {
std::string info;
*value >> info;
context.setMenuInfoText(info);
} catch (const MenuException & ex){
}
} else if (*tok == "menuinfo-position"){
ValueHolder * value = new ValueHolder("menuinfo-position");
TokenView infoView = tok->view();
*value << infoView << infoView;
addData(value);
try {
double x=0, y=.95;
*value >> x >> y;
context.setMenuInfoLocation(x,y);
} catch (const MenuException & ex){
}
} else {
Global::debug(3,"menu") <<"Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch (const LoadException & ex){
throw ex;
} catch (const Filesystem::NotFound & ex){
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
}
}
}
}
void Menu::Menu::setRenderer(const Renderer::Type & type){
renderer = rendererType(type);
}
void Menu::Menu::setRenderer(const Util::ReferenceCount<Renderer> & renderer){
this->renderer = renderer;
}
void Menu::Menu::addOption(MenuOption * opt){
if (renderer != NULL){
this->renderer->addOption(opt);
}
}
Util::ReferenceCount<Menu::Renderer> Menu::Menu::rendererType(const Renderer::Type & type){
switch (type){
case Renderer::Tabbed: {
return Util::ReferenceCount<Renderer>(new TabRenderer());
break;
}
case Renderer::Default:
default: {
return Util::ReferenceCount<Renderer>(new DefaultRenderer());
break;
}
}
return Util::ReferenceCount<Renderer>(NULL);
}
diff --git a/util/menu/menu.h b/util/menu/menu.h
index af643116..b46599dc 100644
--- a/util/menu/menu.h
+++ b/util/menu/menu.h
@@ -1,574 +1,575 @@
#ifndef _paintown_Menu_h
#define _paintown_Menu_h
#include <vector>
#include <string>
#include <map>
#include "util/file-system.h"
#include "util/pointer.h"
#include "util/parameter.h"
#include "util/input/input-map.h"
#include "util/graphics/gradient.h"
#include "font-info.h"
#include "util/gui/animation.h"
#include "util/gui/box.h"
#include "util/gui/context-box.h"
#include "util/gui/fadetool.h"
#include "util/gui/popup-box.h"
#include "util/gui/tabbed-box.h"
#include "util/gui/widget.h"
#include "util/gui/coordinate.h"
#ifdef _MSC_VER
#ifndef uint32_t
typedef unsigned __int32 uint32_t;
#endif
#endif
class Font;
namespace Graphics{
class Bitmap;
}
class MenuOption;
class Token;
class TokenView;
namespace Menu{
extern Util::Parameter<Util::ReferenceCount<FontInfo> > menuFontParameter;
Effects::Gradient standardGradient();
class OptionFactory;
class Point{
public:
int x;
int y;
Point();
Point(int x, int y);
~Point();
};
class InfoBox: public Gui::Widget {
public:
InfoBox();
~InfoBox();
void act(const Font &);
virtual void render(const Graphics::Bitmap &);
void render(const Graphics::Bitmap &, const Font & font);
void open();
void close();
void setText(const std::string &);
inline bool isActive(){
return (this->state != NotActive);
}
/*
inline void setFont(const Util::ReferenceCount<FontInfo> & font){
this->font = font;
}
*/
virtual void initialize(const Font & font);
private:
enum State{
NotActive,
Opening,
Active,
Closing,
};
State state;
Gui::PopupBox popup;
// Util::ReferenceCount<FontInfo> font;
int fadeAlpha;
std::vector<std::string> text;
std::vector<int> padding;
};
class MenuException : public Exception::Base{
public:
MenuException(const std::string & file, int line, const std::string reason = "");
MenuException(const MenuException & copy);
MenuException(const Exception::Base & copy);
virtual ~MenuException() throw();
protected:
std::string reason;
virtual inline const std::string getReason() const {
return reason;
}
virtual Exception::Base * copy() const;
};
/* reload the current menu, usually thrown by some option */
class Reload: public MenuException {
public:
Reload(const std::string & file, int line, const std::string reason = "");
virtual ~Reload() throw();
protected:
virtual Exception::Base * copy() const;
};
class ValueHolder{
public:
ValueHolder(const std::string &);
ValueHolder(const ValueHolder &);
virtual ~ValueHolder();
virtual ValueHolder & operator=(const ValueHolder &);
virtual ValueHolder & operator<<(const std::string &);
virtual ValueHolder & operator<<(bool val);
virtual ValueHolder & operator<<(int val);
virtual ValueHolder & operator<<(double val);
virtual ValueHolder & operator<<(TokenView &);
virtual ValueHolder & operator>>(std::string &);
virtual ValueHolder & operator>>(bool &);
virtual ValueHolder & operator>>(int &);
virtual ValueHolder & operator>>(double &);
virtual inline const std::string & getName() const {
return this->name;
}
virtual const std::string getValues();
private:
std::string name;
std::vector<std::string> values;
unsigned int location;
void next();
};
/*! Menu actions */
enum Actions{
Up,
Down,
Left,
Right,
Select,
Back,
Cancel,
Modify,
Pause,
PlayAsteroids /* Hack for now, remove later */
};
class Context;
/*! Menu Renderer
* Allows menu to modify the way it displays it's contents
* ie regular menus, tab menus, specialized menu etc
*/
class Renderer{
public:
Renderer();
virtual ~Renderer();
enum Type{
Default,
Tabbed
};
//! Reader
virtual bool readToken(const Token *, const OptionFactory &)=0;
virtual Type getType() const = 0;
virtual void initialize(Context &)=0;
virtual void finish()=0;
virtual bool active()=0;
virtual void act(const Context &)=0;
virtual void render(const Graphics::Bitmap &, const Font & font)=0;
// Set font if applicable
// virtual void setFont(const Util::ReferenceCount<FontInfo> &);
//! Compatibility for now, remove later
virtual void addOption(MenuOption *)=0;
virtual std::vector<Util::ReferenceCount<MenuOption> > getOptions() const = 0;
/*! Handle action, with access to context
*/
virtual void doAction(const Actions &, Context &)=0;
/*! Invoke override */
virtual void invokeOverride(const Context &)=0;
virtual void setPosition(const Gui::Coordinate & coordinate) = 0;
protected:
/*! Info boxes */
std::vector <InfoBox *> info;
/*! Menu info box */
InfoBox menuInfo;
/*! Add info box */
virtual void addInfo(const std::string &, const Gui::Widget &, Context &, const Font &);
/*! act info box */
virtual void actInfo(const Font &);
/*! render info box */
virtual void renderInfo(const Graphics::Bitmap &, const Font & font);
};
/*! Regular Menu */
class DefaultRenderer: public Renderer {
public:
DefaultRenderer();
virtual ~DefaultRenderer();
// virtual void setFont(const Util::ReferenceCount<FontInfo> &);
virtual bool readToken(const Token *, const OptionFactory &);
virtual void initialize(Context &);
virtual void finish();
virtual bool active();
virtual void act(const Context &);
virtual void render(const Graphics::Bitmap &, const Font &);
virtual void addOption(MenuOption *);
virtual void doAction(const Actions &, Context &);
virtual std::vector<Util::ReferenceCount<MenuOption> > getOptions() const;
virtual void invokeOverride(const Context &);
virtual void setPosition(const Gui::Coordinate & coordinate);
virtual Type getType() const;
virtual const Gui::ContextBox & getBox() const {
return menu;
}
virtual Gui::ContextBox & getBox(){
return menu;
}
private:
/*! Options */
std::vector<Util::ReferenceCount<MenuOption> > options;
/*! Context Box */
Gui::ContextBox menu;
/*! Override */
bool hasOverride;
/*! Override index */
unsigned int overrideIndex;
};
/*! Tabbed Menu */
class TabInfo {
public:
TabInfo();
~TabInfo();
std::string name;
std::string info;
std::string menuInfo;
void act();
/*! Options */
std::vector<Util::ReferenceCount<MenuOption> > options;
};
class TabRenderer: public Renderer {
public:
TabRenderer();
virtual ~TabRenderer();
// virtual void setFont(const Util::ReferenceCount<FontInfo> &);
virtual bool readToken(const Token *, const OptionFactory &);
virtual void initialize(Context &);
virtual void finish();
virtual bool active();
virtual void act(const Context &);
virtual void render(const Graphics::Bitmap &, const Font &);
virtual void addOption(MenuOption *);
virtual void doAction(const Actions &, Context &);
virtual std::vector<Util::ReferenceCount<MenuOption> > getOptions() const;
virtual void invokeOverride(const Context &);
virtual void setPosition(const Gui::Coordinate & coordinate);
virtual Type getType() const;
virtual Gui::TabbedBox & getBox(){
return menu;
}
private:
/*! Tabs */
std::vector<TabInfo *> tabs;
/*! Tabbed Box */
Gui::TabbedBox menu;
/*! Override */
bool hasOverride;
/*! Override index */
unsigned int overrideIndex;
};
/*! Menu contexts
- Each menu has a context which it defaults to
- Sub menus will be passed the parents context when run
- Fader
- Backgrounds (No background will fall back onto a fill screen)
- Menu (options, ContextBox, etc)
- Contexts are settable to omit drawing certain items (usefull for things like in-game menus)
*/
class Context{
public:
Context();
Context(const Context &, const Context &);
virtual ~Context();
virtual void act();
/*! Pass the widget (Menu ContextBox in this case) to be drawn
* Allows for custom widget menus to be draw in place (ie for tabs or something)
*/
virtual void render(const Util::ReferenceCount<Renderer> &, const Graphics::Bitmap &) const;
virtual void renderBackground(const Graphics::Bitmap & bmp) const;
virtual void renderForeground(const Graphics::Bitmap & bmp) const;
/*! Parse data */
virtual void parseToken(const Token *);
//! Compatibility stuff
virtual void addBackground(const Token *);
virtual void addBackground(const std::string &);
virtual void addBackground(const Graphics::Bitmap & image);
+ virtual void addBackground(const Util::ReferenceCount<Gui::Animation> & animation);
/*! Initializes things like faders */
virtual void initialize();
/*! Closes things out like faders */
virtual void finish();
/*! Play sound */
virtual void playSound(const Actions &) const;
/*! Add sound */
virtual void addSound(const Actions &, const Filesystem::RelativePath &);
/*! Play music */
virtual void playMusic();
/*! set music */
virtual inline void setMusic(const Filesystem::RelativePath & music){
this->music = music;
}
/*! Current state */
enum State{
NotStarted,
Initializing,
Running,
Finishing,
Completed,
};
inline const State & getState() const {
return this->state;
}
virtual void setFadeTool(Gui::FadeTool *);
virtual inline Gui::FadeTool * getFadeTool(){
return this->fades;
}
/*
virtual void setBackground(Util::ReferenceCount<Gui::AnimationManager> background);
virtual inline Util::ReferenceCount<Gui::AnimationManager> getBackground(){
return this->background;
}
*/
virtual inline void setFont(const Util::ReferenceCount<FontInfo> & font){
this->font = font;
}
virtual bool hasFont() const;
virtual inline const Util::ReferenceCount<FontInfo> & getFont() const {
return this->font;
}
virtual inline const Util::ReferenceCount<FontInfo> & getFontInfo() const {
return this->font;
}
virtual inline void setInfoLocation(double x, double y){
this->infoLocation.set(x,y);
}
virtual inline const Gui::RelativePoint & getInfoLocation() const {
return this->infoLocation;
}
virtual inline void setMenuInfoLocation(double x, double y){
this->menuInfoLocation.set(x,y);
}
virtual inline const Gui::RelativePoint & getMenuInfoLocation() const {
return this->menuInfoLocation;
}
virtual inline void setMenuInfoText(const std::string & text){
this->menuInfo = text;
}
virtual inline const std::string & getMenuInfoText() const {
return this->menuInfo;
}
virtual std::vector<std::string> getLanguages() const;
virtual void setLanguages(const std::vector<std::string> & languages);
private:
/*! Require cleanup *default constructor only* */
bool cleanup;
/*! Current state */
State state;
/*! Fade Tool */
Gui::FadeTool * fades;
/*! Backgrounds */
Gui::AnimationManager background;
/*! Sounds */
std::map<Actions, Filesystem::RelativePath> sounds;
/*! Music */
Filesystem::RelativePath music;
/*! Font */
Util::ReferenceCount<FontInfo> font;
/*! Info Placement */
Gui::RelativePoint infoLocation;
/*! Menu Info Placement */
Gui::RelativePoint menuInfoLocation;
/*! Menu Info Text */
std::string menuInfo;
std::vector<std::string> languages;
};
/*! New Menu class */
class Menu{
public:
/* These two are basically the same but the Type version will create the renderer for you */
Menu(const Renderer::Type & type = Renderer::Default);
Menu(const Util::ReferenceCount<Renderer> & renderer);
Menu(const Filesystem::AbsolutePath &, const Renderer::Type & type = Renderer::Default);
Menu(const Filesystem::AbsolutePath &, const OptionFactory & factory, const Renderer::Type & type = Renderer::Default);
Menu(const Token *, const Renderer::Type & type = Renderer::Default);
Menu(const Token * token, const OptionFactory & factory, const Renderer::Type & type = Renderer::Default);
virtual ~Menu();
/*! Run Menu pass parent context */
virtual void run(const Context &);
/* Set position of the menu */
virtual void setPosition(const Gui::Coordinate & coordinate);
/*! Logic pass local context */
virtual void act(Context &);
/*! render pass local context and work */
virtual void render(const Context &, const Graphics::Bitmap &) const;
virtual void setRenderer(const Renderer::Type &);
virtual void setRenderer(const Util::ReferenceCount<Renderer> & renderer);
/* a list of languages (translations) supported by this menu */
virtual std::vector<std::string> getLanguages() const;
/*! Get Name */
virtual std::string getName();
virtual std::string getInfo();
virtual void setFont(const Util::ReferenceCount<FontInfo> &);
/*! Add option */
virtual void addOption(MenuOption * opt);
virtual inline Util::ReferenceCount<Renderer> getRenderer() const {
return renderer;
}
static const int Width = 640;
static const int Height = 480;
protected:
void setupDefaultLanguage(const Context & context, const Menu & parent);
virtual void openOptions();
virtual void closeOptions();
/*! Context */
Context context;
/*! Data holder */
std::map<std::string, ValueHolder *> data;
/*! Renderer */
Util::ReferenceCount<Renderer> renderer;
/*! load token */
void load(const Token * token, const OptionFactory & factory);
#if 0
/*! Handle Override
* There can only be one decleration of an override per file/token
* The first one found will be the one executed.
* If found it will return true so that the menu can handle it and avoid going to everything else
*/
virtual bool handleOverride(const Token *);
#endif
/*! Do current version */
virtual void handleCurrentVersion(const Token *);
/*! Prior token compatibility based on version Global::getVersion() */
virtual void handleCompatibility(const Token *, int version, const OptionFactory & factory);
/*! Add Data */
void addData(ValueHolder *);
/*! Keys */
InputMap<Actions> input;
/*! Type */
Renderer::Type type;
/*! Check type */
virtual Util::ReferenceCount<Renderer> rendererType(const Renderer::Type &);
std::vector<std::string> languages;
};
}
#endif

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jun 15, 11:28 PM (2 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70022
Default Alt Text
(133 KB)

Event Timeline