Exported latest revision from sourceforge cvs, discarding history.

This commit is contained in:
Jason Rohrer
2017-10-11 13:16:19 -07:00
parent 9c297bbe8b
commit cc3108fe85
1304 changed files with 79564 additions and 2 deletions
+34
View File
@@ -0,0 +1,34 @@
#include "BorderPanel.h"
BorderPanel::BorderPanel(
double inAnchorX, double inAnchorY, double inWidth,
double inHeight, Color *inColor, Color *inBorderColor,
double inBorderWidth )
// make panel contents smaller
: GUIPanelGL( inAnchorX + inBorderWidth, inAnchorY + inBorderWidth,
inWidth - 2 * inBorderWidth, inHeight - 2 * inBorderWidth,
inColor ),
// draw the exteral panel to our full dimensions
mBorder( new GUIPanelGL( inAnchorX, inAnchorY, inWidth, inHeight,
inBorderColor ) ) {
}
BorderPanel::~BorderPanel() {
delete mBorder;
}
void BorderPanel::fireRedraw() {
mBorder->fireRedraw();
// superclass redraw to fill contents
GUIPanelGL::fireRedraw();
}
+52
View File
@@ -0,0 +1,52 @@
#ifndef BORDER_PANEL_INCLUDED
#define BORDER_PANEL_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIPanelGL.h"
#include <GL/gl.h>
/**
* A panel with a colored border of a given width.
*
* @author Jason Rohrer
*/
class BorderPanel : public GUIPanelGL {
public:
/**
* Constructs a panel.
*
* Parameters are same as for GUIPanelGL.
*/
BorderPanel(
double inAnchorX, double inAnchorY, double inWidth,
double inHeight, Color *inColor, Color *inBorderColor,
double inBorderWidth );
virtual ~BorderPanel();
// override fireRedraw() in GUIPanelGL
virtual void fireRedraw();
protected:
GUIPanelGL *mBorder;
};
#endif
+299
View File
@@ -0,0 +1,299 @@
#include "ColorEditor.h"
#include "ColorWells.h"
#include "BorderPanel.h"
#include "minorGems/graphics/openGL/gui/LabelGL.h"
extern TextGL *largeText;
extern ColorWells *mainColorStack;
ColorEditor::ColorEditor( ScreenGL *inScreen )
: Editor( inScreen, false ),
mEditPalettePressed( false ),
mIgnoreEvent( false ) {
mCloseButton->setToolTip( "tip_closeEdit_color" );
mSidePanel->add( mainColorStack );
mainColorStack->addActionListener( this );
Color *thumbColor = new Color( .5, .5, .5, .5 );
Color *borderColor = new Color( .35, .35, .35, .35 );
mValueSlider = new ToolTipSliderGL( 248, 188,
64, 12,
NULL, 0,
new Color( 0, 0, 0, 1 ),
new Color( 0, 1, 0, 1 ),
thumbColor->copy(),
borderColor->copy(),
1, 4, 1 );
mSaturationSlider = new ToolTipSliderGL( 248, 175,
64, 12,
NULL, 0,
new Color( 0, 0, 0, 1 ),
new Color( 0, 1, 0, 1 ),
thumbColor->copy(),
borderColor->copy(),
1, 4, 1 );
delete thumbColor;
delete borderColor;
mSidePanel->add( mValueSlider );
mSidePanel->add( mSaturationSlider );
mValueSlider->setToolTip( "tip_valueSlider" );
mSaturationSlider->setToolTip( "tip_saturationSlider" );
mHVPicker = new HueValuePicker( 248, 203,
64, 64 );
mSidePanel->add( mHVPicker );
rgbaColor c = mainColorStack->getSelectedColor();
mWorkingColor = new Color( c.comp.r / 255.0,
c.comp.g / 255.0,
c.comp.b / 255.0,
1.0 );
float h, s, v;
mWorkingColor->makeHSV( &h, &s, &v );
// round to increments of 128 pixels to avoid round-off artifacts
mValueSlider->setThumbPosition( (int)(v * 128) / 128.0 );
mSaturationSlider->setThumbPosition( (int)(s * 128) / 128.0 );
mHVPicker->setValues( h, v );
adjustBarColors();
mValueSlider->addActionListener( this );
mSaturationSlider->addActionListener( this );
mHVPicker->addActionListener( this );
mEditPaletteButton =
new EditButtonGL(
mainColorStack->getAnchorX() - 9,
mainColorStack->getAnchorY() + mainColorStack->getHeight() - 51,
8,
8 );
mSidePanel->add( mEditPaletteButton );
mEditPaletteButton->addActionListener( this );
mEditPaletteButton->setToolTip( "tip_edit_palette" );
postConstruction();
}
ColorEditor::~ColorEditor() {
mSidePanel->remove( mainColorStack );
delete mWorkingColor;
}
void ColorEditor::setEditPaletteButtonVisible( char inIsVisible ) {
mEditPaletteButton->setEnabled( inIsVisible );
}
void ColorEditor::adjustBarColors() {
Color *valStart = Color::makeColorFromHSV(
mHVPicker->getSelectedHue(),
mSaturationSlider->getThumbPosition(),
0 );
Color *valEnd = Color::makeColorFromHSV(
mHVPicker->getSelectedHue(),
mSaturationSlider->getThumbPosition(),
1 );
mValueSlider->setBarStartColor( valStart );
mValueSlider->setBarEndColor( valEnd );
Color *satStart = Color::makeColorFromHSV(
mHVPicker->getSelectedHue(),
0,
mHVPicker->getSelectedValue() );
Color *satEnd = Color::makeColorFromHSV(
mHVPicker->getSelectedHue(),
1,
mHVPicker->getSelectedValue() );
mSaturationSlider->setBarStartColor( satStart );
mSaturationSlider->setBarEndColor( satEnd );
}
char ColorEditor::getDragging() {
return
mHVPicker->mPressed ||
mSaturationSlider->mDragging ||
mValueSlider->mDragging;
}
void ColorEditor::actionPerformed( GUIComponent *inTarget ) {
if( mIgnoreEvent ) {
return;
}
// superclass
Editor::actionPerformed( inTarget );
if( inTarget == mEditPaletteButton ) {
// close ourself
mEditPalettePressed = true;
mCloseButton->fireActionPerformed( mCloseButton );
mEditPalettePressed = false;
}
char colorChange = false;
if( inTarget == mSaturationSlider ||
inTarget == mHVPicker ) {
float h = mHVPicker->getSelectedHue();
float s = mSaturationSlider->getThumbPosition();
float v = mHVPicker->getSelectedValue();
mIgnoreEvent = true;
mValueSlider->setThumbPosition( (int)( v* 128 ) / 128.0f);
mIgnoreEvent = false;
Color *newWorking = Color::makeColorFromHSV( h, s, v );
mWorkingColor->setValues( newWorking );
delete newWorking;
mIgnoreEvent = true;
addColor();
mIgnoreEvent = false;
colorChange = true;
}
if( inTarget == mValueSlider ) {
float h = mHVPicker->getSelectedHue();
float s = mSaturationSlider->getThumbPosition();
float v = mValueSlider->getThumbPosition();
//normalize( h, s, v, &h, &s, &v );
mIgnoreEvent = true;
mHVPicker->setValues( h, v );
mIgnoreEvent = false;
Color *newWorking = Color::makeColorFromHSV( h, s, v );
mWorkingColor->setValues( newWorking );
delete newWorking;
// ignore color change to prevent sliders from jumping
// (each HSV is not a unique RGB)
mIgnoreEvent = true;
addColor();
mIgnoreEvent = false;
colorChange = true;
}
if( inTarget == mainColorStack ) {
// new color picked on stack
rgbaColor c = mainColorStack->getSelectedColor();
mWorkingColor->r = c.comp.r / 255.0f;
mWorkingColor->g = c.comp.g / 255.0f;
mWorkingColor->b = c.comp.b / 255.0f;
float h, s, v;
mWorkingColor->makeHSV( &h, &s, &v );
mIgnoreEvent = true;
// round to increments of 128 pixels to avoid round-off artifacts
mValueSlider->setThumbPosition( (int)( v* 128 ) / 128.0f);
mSaturationSlider->setThumbPosition( (int)( s* 128 ) / 128.0f);
mHVPicker->setValues( h, v );
mIgnoreEvent = false;
colorChange = true;
}
if( colorChange ) {
adjustBarColors();
}
}
void ColorEditor::editorClosing() {
addColor();
}
void ColorEditor::addColor() {
rgbaColor c;
c.comp.r = (unsigned char)( mWorkingColor->r * 255 );
c.comp.g = (unsigned char)( mWorkingColor->g * 255 );
c.comp.b = (unsigned char)( mWorkingColor->b * 255 );
c.comp.a = 255;
// replace current well
mainColorStack->pushColor( c, true );
}
+63
View File
@@ -0,0 +1,63 @@
#ifndef COLOR_EDITOR_INCLUDED
#define COLOR_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "HueValuePicker.h"
#include "ToolTipSliderGL.h"
class ColorEditor : public Editor {
public:
ColorEditor( ScreenGL *inScreen );
~ColorEditor();
virtual void actionPerformed( GUIComponent *inTarget );
// triggered by add button or close
// can be called externally to force edited color to top of stack
void addColor();
// true if controls are currently being dragged
char getDragging();
// true if closed due to press of edit palette
char mEditPalettePressed;
void setEditPaletteButtonVisible( char inIsVisible );
protected:
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
void adjustBarColors();
ToolTipSliderGL *mValueSlider;
ToolTipSliderGL *mSaturationSlider;
HueValuePicker *mHVPicker;
Color *mWorkingColor;
EditButtonGL *mEditPaletteButton;
char mIgnoreEvent;
};
#endif
+446
View File
@@ -0,0 +1,446 @@
#include "ColorWells.h"
#include "ColorEditor.h"
extern ColorEditor *mainColorEditor;
Color selectedBorder( 1, 1, 1, 1 );
Color unselectedBorder( 0.35, 0.35, 0.35, 1 );
static const char *wellFileName = "palette.txt";
ColorWellButtonGL::ColorWellButtonGL( double inAnchorX, double inAnchorY,
double inWidth, double inHeight )
: ToolTipButtonGL( inAnchorX, inAnchorY, inWidth, inHeight ),
mSelected( false ), mColor( 0, 0, 0, 1 ) {
}
void ColorWellButtonGL::setColor( rgbaColor inColor ) {
mColor.setValues( inColor.comp.r / 255.0f,
inColor.comp.g / 255.0f,
inColor.comp.b / 255.0f,
inColor.comp.a / 255.0f );
}
void ColorWellButtonGL::setSelected( char inSelected ) {
mSelected = inSelected;
}
void ColorWellButtonGL::mouseDragged( double inX, double inY ) {
ToolTipButtonGL::mouseDragged( inX, inY );
if( isEnabled() && isInside( inX, inY ) ) {
// fire an event
fireActionPerformed( this );
}
}
void ColorWellButtonGL::drawPressed() {
Color *borderColor = &unselectedBorder;
if( mSelected ) {
borderColor = &selectedBorder;
}
// 1 pixel wide
glColor4f( borderColor->r,
borderColor->g,
borderColor->b, 1 );
glBegin( GL_QUADS ); {
glVertex2d( mAnchorX, mAnchorY );
glVertex2d( mAnchorX + mWidth, mAnchorY );
glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight );
glVertex2d( mAnchorX, mAnchorY + mHeight );
}
glEnd();
// black fill
glColor4f( 0, 0, 0, 1 );
glBegin( GL_QUADS ); {
glVertex2d( mAnchorX + 1, mAnchorY + 1 );
glVertex2d( mAnchorX + mWidth - 1, mAnchorY + 1 );
glVertex2d( mAnchorX + mWidth - 1, mAnchorY + mHeight - 1 );
glVertex2d( mAnchorX + 1 , mAnchorY + mHeight - 1 );
}
glEnd();
// center color block
glColor4f( mColor.r,
mColor.g,
mColor.b, 1);
glBegin( GL_QUADS ); {
glVertex2d( mAnchorX + 2, mAnchorY + 2 );
glVertex2d( mAnchorX + mWidth - 2, mAnchorY + 2 );
glVertex2d( mAnchorX + mWidth - 2, mAnchorY + mHeight - 2 );
glVertex2d( mAnchorX + 2 , mAnchorY + mHeight - 2 );
}
glEnd();
}
void ColorWellButtonGL::drawUnpressed() {
drawPressed();
}
ColorWells::ColorWells( double inAnchorX, double inAnchorY )
: BorderPanel( inAnchorX, inAnchorY,50, 124,
new Color( 0, 0, 0, 1 ),
new Color( 0.35, 0.35, 0.35, 1 ),
1 ),
mLastActionWellChange( false ) {
int buttonW = 10;
FILE *f = fopen( wellFileName, "r" );
SimpleVector<rgbaColor> savedColors;
if( f != NULL ) {
rgbaColor c;
c.comp.a = 255;
int numRead = 1;
while( numRead == 1 ) {
int r, g, b;
numRead = fscanf( f, "%d", &r );
numRead = fscanf( f, "%d", &g );
numRead = fscanf( f, "%d", &b );
c.comp.r = (unsigned char)r;
c.comp.g = (unsigned char)g;
c.comp.b = (unsigned char)b;
if( numRead == 1 ) {
savedColors.push_back( c );
}
}
fclose( f );
}
int numSavedColors = savedColors.size();
int nextSavedColor = 0;
for( int y=0; y<mColorGridH; y++ ) {
for( int x=0; x<mColorGridW; x++ ) {
mColorButtons[y][x] =
new ColorWellButtonGL(
inAnchorX + x * buttonW,
inAnchorY + y * buttonW,
buttonW, buttonW );
add( mColorButtons[y][x] );
mColorButtons[y][x]->addActionListener( this );
if( nextSavedColor < numSavedColors ) {
mColorWells[y][x] =
*( savedColors.getElement( nextSavedColor ) );
nextSavedColor++;
}
else {
// random
mColorWells[y][x].comp.r = rand() % 255;
mColorWells[y][x].comp.g = rand() % 255;
mColorWells[y][x].comp.b = rand() % 255;
mColorWells[y][x].comp.a = 255;
}
mColorButtons[y][x]->setColor( mColorWells[y][x] );
mColorButtons[y][x]->setToolTip( "tip_colorWell" );
}
}
mSelected.y = mColorGridH - 1;
mSelected.x = 0;
mColorButtons[mSelected.y][mSelected.x]->setSelected( true );
mMainColorButton =
new ColorWellButtonGL( inAnchorX,
// right to top of panel, cover border
inAnchorY + mHeight - 2 * buttonW + 2,
mColorGridW * buttonW,
2 * buttonW );
add( mMainColorButton );
mMainColor = mColorWells[mSelected.y][mSelected.x];
mMainColorButton->setColor( mMainColor );
mMainColorButton->setSelected( false );
mMainColorButton->setToolTip( "tip_colorCurrent" );
// center below main color
mAddButton = new AddButtonGL( inAnchorX + (mWidth - 16)/2 + 1,
mMainColorButton->getAnchorY() - 20,
16, 16 );
add( mAddButton );
mAddButton->addActionListener( this );
mAddButton->setToolTip( "tip_addColor" );
}
ColorWells::~ColorWells() {
}
rgbaColor ColorWells::getSelectedColor() {
return mMainColor;
}
#include "musicPlayer.h"
void ColorWells::pushColor( rgbaColor inColor, char inForceReplace ) {
mMainColorButton->setColor( inColor );
mMainColor = inColor;
/*
// set timbre from this color
double coeffs[256];
int numUsed = 0;
rgbaColor c = getSelectedColor();
float r = c.comp.r / 255.0f;
float g = c.comp.g / 255.0f;
float b = c.comp.b / 255.0f;
// r for damping amount for higher harmonics
// g for uniform damping of even harmonics
// b for uniform damping of odd harmonics
numUsed = 40;
coeffs[0]=1;
for( int i=1; i<numUsed; i++ ) {
int harmNumber = i + 1;
coeffs[i]= 1.0/ pow( harmNumber, (1-r) );
if( harmNumber %2 == 0 ) {
// even
coeffs[i] *= g;
}
else {
coeffs[i] *= b;
}
}
setTimbre( 0, coeffs, numUsed );
*/
/*
Color c2( r, g, b );
float h, s, v;
c2.makeHSV( &h, &s, &v );
setEnvelope( 0, 1-r, 1-s );
*/
fireActionPerformed( this );
if( true ) {
return;
}
if( !inForceReplace ) {
// first, check if color already exists, and select it
for( int y=0; y<mColorGridH; y++ ) {
for( int x=0; x<mColorGridW; x++ ) {
if( equal( mColorWells[y][x], inColor ) ) {
// clear border of last selected
mColorButtons[mSelected.y][mSelected.x]->setSelected(
false );
mSelected.x = x;
mSelected.y = y;
mColorButtons[mSelected.y][mSelected.x]->setSelected(
true );
fireActionPerformed( this );
return;
}
}
}
}
// else replace current well
mColorWells[mSelected.y][mSelected.x] = inColor;
mColorButtons[mSelected.y][mSelected.x]->setColor( inColor );
fireActionPerformed( this );
}
Palette ColorWells::getPalette() {
Palette p;
for( int y=0; y<mColorGridH; y++ ) {
for( int x=0; x<mColorGridW; x++ ) {
int i = y * mColorGridW + x;
p.editPalette( i, mColorWells[y][x] );
}
}
return p;
}
void ColorWells::setPalette( Palette inPalette ) {
for( int y=0; y<mColorGridH; y++ ) {
for( int x=0; x<mColorGridW; x++ ) {
int i = y * mColorGridW + x;
mColorWells[y][x] = inPalette.getColor( i );
mColorButtons[y][x]->setColor( mColorWells[y][x] );
}
}
mMainColor = mColorWells[mSelected.y][mSelected.x];
mMainColorButton->setColor( mMainColor );
mLastActionWellChange = false;
fireActionPerformed( this );
}
void ColorWells::actionPerformed( GUIComponent *inTarget ) {
if( inTarget == mAddButton ) {
// replace well
mColorWells[mSelected.y][mSelected.x] = mMainColor;
mColorButtons[mSelected.y][mSelected.x]->setColor( mMainColor );
// save wells to file
FILE *f = fopen( wellFileName, "w" );
if( f != NULL ) {
for( int y=0; y<mColorGridH; y++ ) {
for( int x=0; x<mColorGridW; x++ ) {
fprintf( f, "%d %d %d\n",
mColorWells[y][x].comp.r,
mColorWells[y][x].comp.g,
mColorWells[y][x].comp.b );
}
}
fclose( f );
}
// no change to main color
// but fire action anyway, for those listening for well changes
mLastActionWellChange = true;
fireActionPerformed( this );
return;
}
// well switch?
// ignore if currently dragging in editor
if( mainColorEditor->getDragging() ) {
return;
}
for( int y=0; y<mColorGridH; y++ ) {
for( int x=0; x<mColorGridW; x++ ) {
if( inTarget == mColorButtons[y][x] ) {
// clear border of last selected
mColorButtons[mSelected.y][mSelected.x]->setSelected( false );
mSelected.x = x;
mSelected.y = y;
mColorButtons[mSelected.y][mSelected.x]->setSelected( true );
mMainColor = mColorWells[mSelected.y][mSelected.x];
mMainColorButton->setColor( mMainColor );
mLastActionWellChange = false;
fireActionPerformed( this );
return;
}
}
}
}
+114
View File
@@ -0,0 +1,114 @@
#ifndef COLOR_WELLS_INCLUDED
#define COLOR_WELLS_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIComponentGL.h"
#include "minorGems/ui/event/ActionListenerList.h"
#include "minorGems/ui/event/ActionListener.h"
#include "color.h"
#include "BorderPanel.h"
#include "buttons.h"
#include "common.h"
#include "Palette.h"
class ColorWellButtonGL : public ToolTipButtonGL {
public:
ColorWellButtonGL( double inAnchorX, double inAnchorY,
double inWidth, double inHeight );
void setColor( rgbaColor inColor );
void setSelected( char inSelected );
virtual void drawPressed();
virtual void drawUnpressed();
// override to fire on drag
virtual void mouseDragged( double inX, double inY );
protected:
char mSelected;
Color mColor;
};
class ColorWells : public BorderPanel, public ActionListenerList,
public ActionListener {
public:
/**
* Constructs a well set.
*
* @param inAnchorX the x position of the upper left corner
* of this component.
* @param inAnchorY the y position of the upper left corner
* of this component.
*
* Sets its own width and height automatically.
*/
ColorWells( double inAnchorX, double inAnchorY );
virtual ~ColorWells();
rgbaColor getSelectedColor();
// jumps to color, if it exists, or replaces current well if it
// does not
// adds color to the currently selected well, replacing existing
void pushColor( rgbaColor inColor, char inForceReplace = false );
Palette getPalette();
void setPalette( Palette inPalette );
virtual void actionPerformed( GUIComponent *inTarget );
char mLastActionWellChange;
protected:
static const int mColorGridW = 5;
static const int mColorGridH = 8;
ColorWellButtonGL *mColorButtons[mColorGridH][mColorGridW];
rgbaColor mColorWells[mColorGridH][mColorGridW];
ColorWellButtonGL *mMainColorButton;
AddButtonGL *mAddButton;
rgbaColor mMainColor;
intPair mSelected;
};
#endif
+473
View File
@@ -0,0 +1,473 @@
#include "Connection.h"
#include "minorGems/util/SettingsManager.h"
#include "minorGems/util/TranslationManager.h"
#include "minorGems/network/SocketClient.h"
#include "minorGems/util/log/AppLog.h"
extern int autoJoinPort;
int Connection::getPort() {
if( autoJoinPort != -1 ) {
return autoJoinPort;
}
char portFound;
int port = SettingsManager::getIntSetting( "port",
&portFound );
if( !portFound ) {
port = 7778;
}
return port;
}
Connection::Connection()
: mSock( NULL ),
mError( false ),
mErrorString( NULL ) {
message empty = {NULL, -1, 0, 0, 0};
mNextOutgoingMessage = empty;
mNextIncomingMessage = empty;
mServer = new SocketServer( getPort(), 1 );
}
// make a connection to an established server
Connection::Connection( char *inAddress )
: mServer( NULL ),
mError( false ),
mErrorString( NULL ) {
message empty = {NULL, -1, 0, 0, 0};
mNextOutgoingMessage = empty;
mNextIncomingMessage = empty;
HostAddress h( stringDuplicate( inAddress ), getPort() );
// non-blocking
char timedOut;
mSock = SocketClient::connectToServer( &h, 0, &timedOut );
if( mSock == NULL ) {
setError( "err_failedConnect" );
}
}
Connection::~Connection() {
if( mSock != NULL ) {
delete mSock;
}
if( mServer != NULL ) {
delete mServer;
}
if( mErrorString != NULL ) {
delete [] mErrorString;
}
}
unsigned char computeBitChecksum( unsigned char inMessage[5] ) {
int sum = 0;
for( int i=0; i<5; i++ ) {
for( int b=0; b<8; b++ ) {
sum += (int)( (inMessage[i] >> b) & 0x01 );
}
}
return (unsigned char)sum;
}
char Connection::isConnected() {
if( mSock != NULL ) {
return mSock->isConnected() == 1;
}
return false;
}
char Connection::step() {
if( mSock == NULL && mServer != NULL ) {
char timeout;
mSock = mServer->acceptConnection( 0, &timeout );
if( mSock == NULL && !timeout ) {
setError( "err_failedAccept" );
}
return false;
}
else if( mSock != NULL && mServer == NULL ) {
if( mSock->isConnected() == 0 ) {
// working
return true;
}
else if( mSock->isConnected() == -1 ) {
setError( "err_failedConnect" );
return false;
}
}
else if( mSock == NULL ) {
// maybe failed connect right in constructor
return false;
}
// got past here, we have a connected socket
if( mNextOutgoingMessage.length != -1 ) {
// try sending more of it
int i = mNextOutgoingMessage.nextToProcessIndex;
/*
// header packed right into send body
if( i == 0 && mNextOutgoingMessage.headerBytesProcessed != 5 ) {
// compose and send 5 header bytes
int numBytes = mNextOutgoingMessage.length;
unsigned char sendData[5];
sendData[0] = mNextOutgoingMessage.channel;
sendData[1] = ( numBytes >> 24 ) & 0xFF;
sendData[2] = ( numBytes >> 16 ) & 0xFF;
sendData[3] = ( numBytes >> 8 ) & 0xFF;
sendData[4] = ( numBytes ) & 0xFF;
int j = mNextOutgoingMessage.headerBytesProcessed;
// non-blocking, send only the remaining bytes of header
int numSent = mSock->send( &( sendData[j] ), 5 - j, false );
if( numSent == -1 ) {
setError( "err_failedSend" );
}
else if( numSent > 0 ) {
mNextOutgoingMessage.headerBytesProcessed += numSent;
}
}
*/
//else {
if( true ) {
// send data
// non-blocking
int numSent = mSock->send( &( mNextOutgoingMessage.data[ i ] ),
mNextOutgoingMessage.length - i,
false,
false ); // realtime, no buffering
if( numSent < 0 ) {
if( numSent == -1 ) {
setError( "err_failedSend" );
}
else {
// would block
// no more work to do right now
return false;
}
}
else {
i += numSent;
AppLog::getLog()->logPrintf( Log::DETAIL_LEVEL,
"Socket sent %d/%d bytes",
numSent,
mNextOutgoingMessage.length );
if( i == mNextOutgoingMessage.length ) {
// done with it!
delete [] mNextOutgoingMessage.data;
mNextOutgoingMessage.data = NULL;
mNextOutgoingMessage.length = -1;
mNextOutgoingMessage.nextToProcessIndex = 0;
mNextOutgoingMessage.headerBytesProcessed = 0;
}
else {
// more to go
mNextOutgoingMessage.nextToProcessIndex = i;
}
}
}
return true;
}
else {
// is there another in our queue that we can start sending?
if( mQueuedOutgoing.size() > 0 ) {
// grab next
message *m = mQueuedOutgoing.getElement( 0 );
mNextOutgoingMessage = *m;
mQueuedOutgoing.deleteElement( 0 );
return true;
}
}
if( mNextIncomingMessage.length != -1 &&
mNextIncomingMessage.headerBytesProcessed == 6 ) {
// try receiving more of it
int i = mNextIncomingMessage.nextToProcessIndex;
// non-blocking
int numReceived = mSock->receive( &( mNextIncomingMessage.data[ i ] ),
mNextIncomingMessage.length - i,
0 );
if( numReceived < 0 ) {
if( numReceived == -1 ) {
setError( "err_failedReceive" );
}
else {
// timeout
// no more work right now
return false;
}
}
else {
i += numReceived;
if( i == mNextIncomingMessage.length ) {
// push it onto queue
mQueuedIncoming.push_back( mNextIncomingMessage );
// clear it to make room for next one
mNextIncomingMessage.data = NULL;
mNextIncomingMessage.length = -1;
mNextIncomingMessage.nextToProcessIndex = 0;
mNextIncomingMessage.headerBytesProcessed = 0;
}
else {
// more to go
mNextIncomingMessage.nextToProcessIndex = i;
}
}
return true;
}
else {
// try to receive header of next incoming
// fill buffer with partial stuff already stored in next message
int numBytes = mNextIncomingMessage.length;
unsigned char recvData[6];
recvData[0] = mNextIncomingMessage.channel;
recvData[1] = ( numBytes >> 24 ) & 0xFF;
recvData[2] = ( numBytes >> 16 ) & 0xFF;
recvData[3] = ( numBytes >> 8 ) & 0xFF;
recvData[4] = ( numBytes ) & 0xFF;
recvData[5] = mNextIncomingMessage.bitCheckSum;
// fetch remaining part
int j = mNextIncomingMessage.headerBytesProcessed;
// non-blocking, receive only the remaining bytes of header
int numReceived = mSock->receive( &( recvData[j] ), 6 - j, 0 );
if( numReceived == -1 ) {
setError( "err_failedReceive" );
}
else if( numReceived > 0 ) {
mNextIncomingMessage.headerBytesProcessed += numReceived;
// stick back into length field
mNextIncomingMessage.channel = recvData[0];
mNextIncomingMessage.length =
recvData[1] << 24 |
recvData[2] << 16 |
recvData[3] << 8 |
recvData[4];
mNextIncomingMessage.bitCheckSum = recvData[5];
if( mNextIncomingMessage.headerBytesProcessed == 6 ) {
// done!
// verify checksum
if( computeBitChecksum( recvData ) !=
mNextIncomingMessage.bitCheckSum ) {
AppLog::error( "Error: Message checksum failed" );
// clear it to make room for next one,
// to ensure that processing of this one stops
mNextIncomingMessage.data = NULL;
mNextIncomingMessage.length = -1;
mNextIncomingMessage.nextToProcessIndex = 0;
mNextIncomingMessage.headerBytesProcessed = 0;
setError( "err_receiveCorrupted" );
}
else {
// checksum correct
// make a data buffer
mNextIncomingMessage.data =
new unsigned char[ mNextIncomingMessage.length ];
}
}
return true;
}
}
return false;
}
void Connection::sendMessage( unsigned char *inData, int inLength,
unsigned char inChannel ) {
// pack the header right into the message to avoid two consecutive sends
// (which interacts poorly with the Nagle algorithm)
message m = { new unsigned char[ inLength + 6 ],
inLength + 6, 0, 0, inChannel,
0 }; // empty checksum
// first 6 bytes of data are header
m.data[0] = m.channel;
m.data[1] = ( inLength >> 24 ) & 0xFF;
m.data[2] = ( inLength >> 16 ) & 0xFF;
m.data[3] = ( inLength >> 8 ) & 0xFF;
m.data[4] = ( inLength ) & 0xFF;
m.data[4] = ( inLength ) & 0xFF;
m.bitCheckSum = computeBitChecksum( m.data );
m.data[5] = m.bitCheckSum;
// remainder are data payload
memcpy( &( m.data[6] ), inData, inLength );
mQueuedOutgoing.push_back( m );
}
unsigned char *Connection::receiveMessage( int *outLength,
unsigned char inChannel ) {
int queueSize = mQueuedIncoming.size();
if( queueSize > 0 ) {
// skip messages that don't match our channel
int i = 0;
char chanMatch = false;
while( !chanMatch && i<queueSize ) {
message *m = mQueuedIncoming.getElement( i );
if( m->channel == inChannel ) {
chanMatch = true;
}
else {
i++;
}
}
if( chanMatch ) {
message *m = mQueuedIncoming.getElement( i );
*outLength = m->length;
unsigned char *returnValue = m->data;
mQueuedIncoming.deleteElement( i );
return returnValue;
}
else {
return NULL;
}
}
return NULL;
}
char Connection::isError() {
return mError;
}
void Connection::clearError() {
if( mErrorString != NULL ) {
delete [] mErrorString;
}
mErrorString = NULL;
mError = false;
}
char *Connection::getErrorString() {
return mErrorString;
}
void Connection::setError( const char *inErrorTranslationKey ) {
mError = true;
if( mErrorString != NULL ) {
delete [] mErrorString;
}
mErrorString = stringDuplicate( TranslationManager::translate(
(char*)inErrorTranslationKey ) );
}
+87
View File
@@ -0,0 +1,87 @@
#include "minorGems/network/Socket.h"
#include "minorGems/network/SocketServer.h"
#include "minorGems/util/SimpleVector.h"
typedef struct message {
unsigned char *data;
int length;
int nextToProcessIndex;
int headerBytesProcessed;
unsigned char channel;
unsigned char bitCheckSum;
} message;
class Connection {
public:
// start a server
Connection();
// make a connection to an established server
Connection( char *inAddress );
~Connection();
// get the port that connections use
static int getPort();
char isConnected();
// perform another step of any pending network operations
// returns true if work remains to be done
// returns false if client is done with all pending work
char step();
void sendMessage( unsigned char *inData, int inLength,
unsigned char inChannel=0 );
unsigned char *receiveMessage( int *outLength, unsigned char inChannel=0 );
char isError();
void clearError();
// destroyed internally
char *getErrorString();
protected:
SocketServer *mServer;
Socket *mSock;
SimpleVector<message> mQueuedOutgoing;
SimpleVector<message> mQueuedIncoming;
// in progress, not queued
message mNextOutgoingMessage;
message mNextIncomingMessage;
unsigned char mReceivedHeader[4];
int mNumHeaderBytesReceived;
char mError;
char *mErrorString;
void setError( const char *inErrorTranslationKey );
};
+784
View File
@@ -0,0 +1,784 @@
#include "ControllerGame.h"
#include "common.h"
#include "ColorEditor.h"
#include "TileEditor.h"
#include "StateObjectEditor.h"
#include "MusicEditor.h"
#include "TimbreEditor.h"
#include "ScaleEditor.h"
#include "SongEditor.h"
#include "PaletteEditor.h"
#include "ColorWells.h"
#include "TilePicker.h"
#include "RoomPicker.h"
#include "SpritePicker.h"
#include "StateObjectPicker.h"
#include "TimbrePicker.h"
#include "ScalePicker.h"
#include "MusicPicker.h"
#include "SongPicker.h"
#include "ScenePicker.h"
#include "PalettePicker.h"
#include "TimerDisplay.h"
#include "WarningDisplay.h"
#include "ToolTipManager.h"
#include "DragAndDropManager.h"
#include "packSaver.h"
#include "resourceImporter.h"
#include "minorGems/system/Time.h"
#include "minorGems/util/log/AppLog.h"
#include "minorGems/util/SettingsManager.h"
#include "Connection.h"
extern Connection *connection;
extern TimerDisplay *mainTimerDisplay;
extern WarningDisplay *mainWarningDisplay;
ColorWells *mainColorStack;
TilePicker *mainTilePicker;
ColorEditor *mainColorEditor;
RoomPicker *mainRoomPicker;
TileEditor *mainTileEditor;
SpritePicker *mainSpritePicker;
SpritePicker *mainSpritePickerLower;
StateObjectPicker *mainStateObjectPicker;
TimbrePicker *mainTimbrePicker;
ScalePicker *mainScalePicker;
MusicPicker *mainMusicPicker;
SongPicker *mainSongPicker = NULL;
ScenePicker *mainScenePicker;
PalettePicker *mainPalettePicker;
DragAndDropManager *mainDragAndDrop;
RoomEditor *mainRoomEditor;
SpriteEditor *mainSpriteEditor;
StateObjectEditor *mainStateObjectEditor;
MusicEditor *mainMusicEditor;
TimbreEditor *mainTimbreEditor;
ScaleEditor *mainScaleEditor;
SongEditor *mainSongEditor = NULL;
PaletteEditor *mainPaletteEditor;
PlayerMoveEditor *mainPracticePlayerEditor;
char practiceMode = false;
char practiceStop = false;
char practicePlayerTurn = false;
//#include "fixOldResources.h"
ControllerGame::ControllerGame( ScreenGL *inScreen ) {
setScreen( inScreen );
// don't fetch resources from network, since we're the only
// one using them
setUseNetwork( false );
//printf( "**** RUNNING resource fixing script\n" );
//fixOldResources();
char turnLengthFound = false;
mSecondsPerMove = SettingsManager::getIntSetting( "timeLimit",
&turnLengthFound );
if( !turnLengthFound ) {
mSecondsPerMove = 30;
}
mStepsPerSecond = 30;
resetTimer();
mainColorStack = new ColorWells( 255, 44 );
mainTilePicker = new TilePicker( 245, 48 + 128 );
mainColorEditor = new ColorEditor( inScreen );
mainRoomPicker = new RoomPicker( 245, 48 );
mainSpritePicker = new SpritePicker( 245, 48 + 128 );
mainSpritePickerLower = new SpritePicker( 245, 48 );
mainStateObjectPicker = new StateObjectPicker( 245, 48 + 128 );
mainTimbrePicker = new TimbrePicker( 245, 48 + 128 );
mainScalePicker = new ScalePicker( 245, 48 + 128 );
mainMusicPicker = new MusicPicker( 245, 48 + 128 );
mainSongPicker = new SongPicker( 245, 48 );
mainScenePicker = new ScenePicker( 245, 48 );
mainPalettePicker = new PalettePicker( 245, 48 + 128 );
AppLog::info( "Importing resource cache" );
importResources();
AppLog::info( "Done importing resource cache" );
AppLog::info( "Loading packs from loading bay" );
loadPacks();
AppLog::info( "Done loading packs" );
mainTilePicker->forceNewSearch();
mainRoomPicker->forceNewSearch();
mainSpritePicker->forceNewSearch();
mainSpritePickerLower->forceNewSearch();
mainStateObjectPicker->forceNewSearch();
mainTimbrePicker->forceNewSearch();
mainScalePicker->forceNewSearch();
mainMusicPicker->forceNewSearch();
mainSongPicker->forceNewSearch();
mainScenePicker->forceNewSearch();
mainPalettePicker->forceNewSearch();
mainDragAndDrop = new DragAndDropManager( 48, 48, 320, 240 );
mainTileEditor = new TileEditor( inScreen );
mainRoomEditor = new RoomEditor( inScreen );
mainSpriteEditor = new SpriteEditor( inScreen );
mainStateObjectEditor = new StateObjectEditor( inScreen );
mainMusicEditor = new MusicEditor( inScreen );
mainTimbreEditor = new TimbreEditor( inScreen );
mainScaleEditor = new ScaleEditor( inScreen );
mainSongEditor = new SongEditor( inScreen );
mainPaletteEditor = new PaletteEditor( inScreen );
mGameStateEditor = new GameStateEditor( inScreen );
mainPracticePlayerEditor = new PlayerMoveEditor( inScreen );
//mCurrentEditor = mRoomEditor;
//mCurrentEditor = mTileEditor;
//mCurrentEditor = mColorEditor;
//mCurrentEditor = mSpriteEditor;
mCurrentEditor = mGameStateEditor;
mCurrentEditor->setVisible( true );
mCurrentEditor->addActionListener( this );
mainPracticePlayerEditor->addActionListener( this );
mainPracticePlayerEditor->setMovesDisabled( true );
practiceMode = false;
practicePlayerTurn = false;
}
ControllerGame::~ControllerGame() {
mCurrentEditor->setVisible( false );
delete mGameStateEditor;
delete mainColorEditor;
delete mainTileEditor;
delete mainRoomEditor;
delete mainSpriteEditor;
delete mainStateObjectEditor;
delete mainSongEditor;
// re-NULL, in case a Player game started later
mainSongEditor = NULL;
delete mainMusicEditor;
delete mainTimbreEditor;
delete mainScaleEditor;
delete mainPaletteEditor;
delete mainPracticePlayerEditor;
delete mainDragAndDrop;
delete mainColorStack;
delete mainTilePicker;
delete mainRoomPicker;
delete mainSpritePicker;
delete mainSpritePickerLower;
delete mainStateObjectPicker;
delete mainSongPicker;
delete mainTimbrePicker;
delete mainScalePicker;
delete mainPalettePicker;
delete mainMusicPicker;
delete mainScenePicker;
}
void ControllerGame::resetTimer() {
mainTimerDisplay->setTime( mSecondsPerMove );
mStepsLeft = mStepsPerSecond * mSecondsPerMove;
mLastStepTime = Time::getCurrentTime();
}
void ControllerGame::step() {
if( ( connection != NULL && connection->isConnected() )
|| practiceMode ) {
// display and update timer
decrementStepCount();
if( mStepsLeft == mStepsPerSecond * 6 - 1) {
mainWarningDisplay->show( "warning_sending_soon", mStepsLeft );
}
mainTimerDisplay->freeze( false );
mainTimerDisplay->setTime( mStepsLeft / mStepsPerSecond );
if( mStepsLeft > 0 ) {
mGameStateEditor->enableSend( true );
}
}
else {
// switch tip on timer
mainTimerDisplay->freeze( true );
// no sending
mGameStateEditor->enableSend( false );
}
if( mStepsLeft == 0 ) {
// force a step here, regardless of system time
// we NEVER want to run step 0 more than once (send same
// state more than once)
mStepsLeft --;
if( connection != NULL && ! practiceMode ) {
AppLog::info( "Sending game state" );
mGameStateEditor->aboutToSend();
// pack last-saved music ID into this game state
// (to share this saved music with the player's database)
mGameStateEditor->mGameStateToEdit->mMusicID =
mainMusicPicker->getSelectedResourceID();
// same for last-selected scene ID
mGameStateEditor->mGameStateToEdit->mSceneID =
mainScenePicker->getSelectedResourceID();
int numBytes;
unsigned char *message =
mGameStateEditor->mGameStateToEdit->getStateAsMessage(
&numBytes );
// stale now, because sent to player, unless we edit it
mGameStateEditor->mGameStateToEdit->markNonPlayerSpeechStale();
connection->sendMessage( message, numBytes );
delete [] message;
}
else if( practiceMode &&
! mainPracticePlayerEditor->isVisible() ) {
mGameStateEditor->aboutToSend();
mGameStateEditor->mGameStateToEdit->markNonPlayerSpeechStale();
GameState *copyState = mGameStateEditor->mGameStateToEdit->copy();
copyState->setSelectedObject( 0 );
mainPracticePlayerEditor->setGameStateToEdit( copyState );
mainPracticePlayerEditor->mGameStateToEdit
->markNonPlayerSpeechStale();
practicePlayerTurn = true;
// do this *after* taking flip book frame
//mainPracticePlayerEditor->setMovesDisabled( false );
// "player" (in practice mode) gets to move again
mStepsLeft = mStepsPerSecond * mSecondsPerMove;
mLastStepTime = Time::getCurrentTime();
// clear waiting tip
ToolTipManager::freeze( false );
ToolTipManager::setTip( NULL );
mainPracticePlayerEditor->enableSend( true );
mGameStateEditor->showPracticePlayerEditor();
}
else if( practiceMode &&
mainPracticePlayerEditor->isVisible() ) {
// "player" (in practice mode) is sending
mainPracticePlayerEditor->clearNonPlayerSpeech();
mainPracticePlayerEditor->setMovesDisabled( true );
// preserve selected object
int oldSelected =
mGameStateEditor->mGameStateToEdit->getSelectedObject();
GameState *copyState =
mainPracticePlayerEditor->mGameStateToEdit->copy();
copyState->setSelectedObject( oldSelected );
mGameStateEditor->setGameStateToEdit( copyState );
practicePlayerTurn = false;
// do this *after* taking flip book frame
//mGameStateEditor->hidePracticePlayerEditor();
mGameStateEditor->enableSend( true );
mGameStateEditor->mGameStateToEdit->deleteAllNonPlayerSpeech();
// controller gets to move again
mStepsLeft = mStepsPerSecond * mSecondsPerMove;
mLastStepTime = Time::getCurrentTime();
if( practiceStop ) {
// end of practice mode
practiceStop = false;
practiceMode = false;
mainTimerDisplay->setTime( mStepsLeft / mStepsPerSecond );
// switch tip on timer
mainTimerDisplay->freeze( true );
// no sending
mGameStateEditor->enableSend( false );
mGameStateEditor->hidePracticePlayerEditor();
}
else {
// simulate player move received
mainWarningDisplay->show( "warning_move_received",
mStepsPerSecond * 3 );
}
}
}
else if( mStepsLeft < 0 ) {
// try to receive response
if( connection != NULL ) {
int numBytes;
unsigned char *message = connection->receiveMessage( &numBytes );
if( message != NULL ) {
AppLog::info( "Got message" );
GameState *state = new GameState( message, numBytes );
delete [] message;
// only pay attention to speech, action/pos, and position for
// obj0 as sent by player
StateObjectInstance *playerObjZero =
*( state->mObjects.getElement( 0 ) );
GameState *ourState = mGameStateEditor->mGameStateToEdit;
// first, clear all other speech (it's stale after player sees
// it one time)
ourState->deleteAllNonPlayerSpeech();
StateObjectInstance *ourObjZero =
*( ourState->mObjects.getElement( 0 ) );
ourObjZero->mAnchorPosition = playerObjZero->mAnchorPosition;
delete [] ourObjZero->mSpokenMessage;
ourObjZero->mSpokenMessage =
stringDuplicate( playerObjZero->mSpokenMessage );
ourObjZero->mActionOffset = playerObjZero->mActionOffset;
memcpy( ourObjZero->mAction, playerObjZero->mAction, 11 );
delete state;
// don't update pickers, since controller might be in the
// middle of editing something else
mGameStateEditor->setGameStateToEdit( ourState->copy(),
false );
mGameStateEditor->enableSend( true );
// we get to move again
mStepsLeft = mStepsPerSecond * mSecondsPerMove;
mLastStepTime = Time::getCurrentTime();
mainWarningDisplay->show( "warning_move_received",
mStepsPerSecond * 3 );
}
}
}
if( connection != NULL ) {
// check for any resource requests (channel 1)
int numBytes;
unsigned char *message = connection->receiveMessage( &numBytes, 1 );
if( message != NULL ) {
// got one!
unsigned long sec, ms;
Time::getCurrentTime( &sec, &ms );
AppLog::getLog()->logPrintf(
Log::DETAIL_LEVEL,
"Got a resource request (%d:%d)...", (int)sec, (int)ms );
int bytesLeft = numBytes;
unsigned char *messageLeft = message;
if( bytesLeft < 1 ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: bad resource request received (l:%d)",
__LINE__ );
delete [] message;
return;
}
int numChunks = messageLeft[0];
bytesLeft -= 1;
messageLeft = &( messageLeft[1] );
AppLog::getLog()->logPrintf(
Log::DETAIL_LEVEL,
" Request contains %d chunks, %d bytes", numChunks,
numBytes );
SimpleVector<unsigned char> responseAccum;
for( int i=0; i<numChunks; i++ ) {
// decode chunk request and add data to response
if( bytesLeft < 1 ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: bad resource request received (l:%d)",
__LINE__ );
delete [] message;
return;
}
int typeLength = messageLeft[0];
bytesLeft -= 1;
messageLeft = &( messageLeft[1] );
if( bytesLeft < typeLength ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: bad resource request received (l:%d),"
" error on chunk %d",
__LINE__, i );
delete [] message;
return;
}
char *typeString = new char[ typeLength + 1];
memcpy( typeString, messageLeft, typeLength );
typeString[ typeLength ] = '\0';
bytesLeft -= typeLength;
messageLeft = &( messageLeft[ typeLength ] );
int numUsed;
uniqueID id = readUniqueID( messageLeft, bytesLeft,
&numUsed );
if( numUsed != U ) {
AppLog::error( "Error: bad resource request received" );
delete [] message;
delete [] typeString;
return;
}
bytesLeft -= numUsed;
messageLeft = &( messageLeft[ numUsed ] );
int dataLength;
char fromNetwork;
unsigned char *data = loadResourceData( typeString,
id,
&dataLength,
&fromNetwork );
if( data == NULL ) {
char *idString = getHumanReadableString( id );
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: request for resource %s of type %s "
"that failed to load.", idString, typeString );
delete [] idString;
uniqueID defaultResourceID;
char foundDefault = true;
if( strcmp( typeString, "music" ) == 0 ) {
defaultResourceID =
Music::getDefaultResource().getUniqueID();
}
else if( strcmp( typeString, "scale" ) == 0 ) {
defaultResourceID =
Scale::getDefaultResource().getUniqueID();
}
else if( strcmp( typeString, "timbre" ) == 0 ) {
defaultResourceID =
TimbreResource::getDefaultResource().getUniqueID();
}
else if( strcmp( typeString, "song" ) == 0 ) {
defaultResourceID =
Song::getDefaultResource().getUniqueID();
}
else if( strcmp( typeString, "object" ) == 0 ) {
defaultResourceID =
StateObject::getDefaultResource().getUniqueID();
}
else if( strcmp( typeString, "room" ) == 0 ) {
defaultResourceID =
Room::getDefaultResource().getUniqueID();
}
else if( strcmp( typeString, "scene" ) == 0 ) {
defaultResourceID =
Scene::getDefaultResource().getUniqueID();
}
else if( strcmp( typeString, "sprite" ) == 0 ) {
defaultResourceID =
SpriteResource::getDefaultResource().getUniqueID();
}
else if( strcmp( typeString, "tile" ) == 0 ) {
defaultResourceID =
Tile::getDefaultResource().getUniqueID();
}
else if( strcmp( typeString, "palette" ) == 0 ) {
defaultResourceID =
Palette::getDefaultResource().getUniqueID();
}
else {
foundDefault = false;
AppLog::error(
"Error, unknown resource type, cannot send "
"default" );
}
if( foundDefault ) {
AppLog::info(
"Sending default resource data instead" );
data = loadResourceData( typeString,
defaultResourceID,
&dataLength,
&fromNetwork );
}
}
if( data != NULL ) {
// chunk length
responseAccum.push_back( getChars( dataLength ), 4 );
// chunk data
responseAccum.push_back( data, dataLength );
delete [] data;
}
else {
AppLog::criticalError(
"CRITICAL ERROR: failed to load any resource "
"data in response to client request" );
}
delete [] typeString;
}
delete [] message;
unsigned char *responseMessage = responseAccum.getElementArray();
// send the data back directly
connection->sendMessage( responseMessage, responseAccum.size(),
1 );
delete [] responseMessage;
// step network to force send through
char workLeft = true;
while( workLeft ) {
workLeft = connection->step();
}
}
}
}
void ControllerGame::drawScene() {
}
void ControllerGame::postRedraw() {
if( practiceMode ) {
if( mStepsLeft == mStepsPerSecond * mSecondsPerMove ) {
mainPracticePlayerEditor->saveFlipBookImage();
if( practicePlayerTurn ) {
// just received a move, and took a snapshot of it...
// now enable player to move
mainPracticePlayerEditor->setMovesDisabled( false );
}
else {
// end of practice player turn
mGameStateEditor->hidePracticePlayerEditor();
}
}
}
}
void ControllerGame::actionPerformed( GUIComponent *inTarget ) {
// close button
if( inTarget == mCurrentEditor && mCurrentEditor != mGameStateEditor ) {
mCurrentEditor->setVisible( false );
mCurrentEditor->removeActionListener( this );
mCurrentEditor = NULL;
}
else if( inTarget == mGameStateEditor ||
inTarget == mainPracticePlayerEditor) {
// event from this means "send"
// from either StateEditor or practice player
if( inTarget == mGameStateEditor ) {
AppLog::info( "Send button pressed in GameStateEditor" );
}
else {
AppLog::info(
"Send (or stop practice) button pressed in "
"PracticePlayerEditor" );
}
if( mStepsLeft > 0 ) {
// will send on very next step
mStepsLeft = 1;
mainTimerDisplay->setTime( 0 );
}
else {
AppLog::error(
"ERROR: Send button pressed when mStepsLeft not positive" );
}
}
}
+40
View File
@@ -0,0 +1,40 @@
#include "GameHalf.h"
#include "Editor.h"
#include "ColorEditor.h"
#include "TileEditor.h"
#include "RoomEditor.h"
#include "SpriteEditor.h"
#include "GameStateEditor.h"
#include "PlayerMoveEditor.h"
#include "minorGems/ui/event/ActionListenerList.h"
class ControllerGame : public GameHalf, public ActionListener {
public:
ControllerGame( ScreenGL *inScreen );
~ControllerGame();
virtual void resetTimer();
virtual void step();
virtual void drawScene();
// pay attention to redraws
virtual void postRedraw();
// implements ActionListener
virtual void actionPerformed( GUIComponent *inTarget );
protected:
Editor *mCurrentEditor;
TileEditor *mTileEditor;
RoomEditor *mRoomEditor;
SpriteEditor *mSpriteEditor;
GameStateEditor *mGameStateEditor;
};
+181
View File
@@ -0,0 +1,181 @@
#include "DemoCodeChecker.h"
#include "minorGems/util/stringUtils.h"
#include "minorGems/util/TranslationManager.h"
#include "minorGems/crypto/hashes/sha1.h"
#include "minorGems/util/log/AppLog.h"
#include <time.h>
// secret shared with demo server (so that we can detect that it's a real
// demo server)
static const char *sharedSecret = "control_this";
static const char *demoServerAddress = "http://sleepisdeath.net/demoServer/server.php";
DemoCodeChecker::DemoCodeChecker( char *inCode )
: mError( false ),
mErrorString( NULL ) {
char *challengeString = autoSprintf( "%d%s", time( NULL ), sharedSecret );
mChallenge = computeSHA1Digest( challengeString );
delete [] challengeString;
char *url = autoSprintf( "%s?action=check_permitted&demo_id=%s"
"&challenge=%s",
demoServerAddress, inCode, mChallenge );
AppLog::getLog()->logPrintf(
Log::INFO_LEVEL, "Checking demo code with URL %s", url );
mRequest = new WebRequest( "GET", url, NULL );
delete [] url;
mPermitted = false;
mStepCount = 0;
}
DemoCodeChecker::~DemoCodeChecker() {
if( mErrorString != NULL ) {
delete [] mErrorString;
}
if( mRequest != NULL ) {
delete mRequest;
}
delete [] mChallenge;
}
char DemoCodeChecker::step() {
int result = mRequest->step();
mStepCount ++;
if( mStepCount < 45 ) {
// wait a bit to avoid instant, flicker-inducing response
return true;
}
if( mPermitted || isError() ) {
// don't do any more processing
return false;
}
if( result == -1 ) {
setError( "err_webRequest" );
// done
return false;
}
if( result == 1 ) {
// check
char *resultString = mRequest->getResult();
AppLog::getLog()->logPrintf(
Log::INFO_LEVEL,
"Result from demo code server:\n%s", resultString );
if( strstr( resultString, "permitted" ) != NULL ) {
char *correctResponseString = autoSprintf( "%s%s", mChallenge,
sharedSecret );
char *correctResponse = computeSHA1Digest( correctResponseString );
delete [] correctResponseString;
char containsResponse =
( strstr( resultString, correctResponse ) != NULL );
delete[] correctResponse;
if( containsResponse ) {
mPermitted = true;
delete [] resultString;
return false;
}
}
delete [] resultString;
// else permission failed
setError( "err_codeFailed" );
return false;
}
if( result == 0 ) {
// request in progress
return true;
}
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Unexpecte result code from WebRequest: %d", result );
setError( "err_webRequest" );
return false;
}
char DemoCodeChecker::codePermitted() {
return mPermitted;
}
char DemoCodeChecker::isError() {
return mError;
}
void DemoCodeChecker::clearError() {
if( mErrorString != NULL ) {
delete [] mErrorString;
}
mErrorString = NULL;
mError = false;
}
char *DemoCodeChecker::getErrorString() {
return mErrorString;
}
void DemoCodeChecker::setError( const char *inErrorTranslationKey ) {
mError = true;
if( mErrorString != NULL ) {
delete [] mErrorString;
}
mErrorString = stringDuplicate( TranslationManager::translate(
(char*)inErrorTranslationKey ) );
}
+44
View File
@@ -0,0 +1,44 @@
#include "minorGems/network/web/WebRequest.h"
class DemoCodeChecker {
public:
// start a checker connection
DemoCodeChecker( char *inCode );
~DemoCodeChecker();
// perform another step of any pending network operations
// returns true if work remains to be done
// returns false if client is done with all pending work
char step();
// after steps done, check result
char codePermitted();
char isError();
void clearError();
// destroyed internally
char *getErrorString();
protected:
char mError;
char *mErrorString;
void setError( const char *inErrorTranslationKey );
WebRequest *mRequest;
char *mChallenge;
char mPermitted;
int mStepCount;
};
+157
View File
@@ -0,0 +1,157 @@
#include "DragAndDropManager.h"
#include "ToolTipManager.h"
DragAndDropManager::DragAndDropManager( double inAnchorX, double inAnchorY,
double inWidth, double inHeight )
: GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ),
mNumSprites( 0 ),
mSprites( NULL ),
mOffsets( NULL ),
mTrans( NULL ),
mGlows( NULL ),
mDragging( false ) {
}
DragAndDropManager::~DragAndDropManager() {
clear();
}
void DragAndDropManager::clear() {
if( mSprites != NULL ) {
for( int i=0; i<mNumSprites; i++ ) {
delete mSprites[i];
}
delete [] mSprites;
delete [] mOffsets;
delete [] mTrans;
delete [] mGlows;
}
mSprites = NULL;
mOffsets = NULL;
mTrans = NULL;
mGlows = NULL;
mNumSprites = 0;
}
void DragAndDropManager::setSprite( Sprite *inSprite, double inZoom ) {
clear();
mNumSprites = 1;
mSprites = new Sprite*[1];
mOffsets = new intPair[1];
mTrans = new float[1];
mGlows = new char[1];
mSprites[0] = inSprite;
mOffsets[0].x = 0;
mOffsets[0].y = 0;
mTrans[0] = 1.0f;
mGlows[0] = false;
mZoom = inZoom;
}
void DragAndDropManager::setSprites( int inNumSprites,
Sprite **inSprites, intPair *inOffsets,
float *inTrans,
char *inGlows,
double inZoom ) {
clear();
mNumSprites = inNumSprites;
mSprites = inSprites;
mOffsets = inOffsets;
mTrans = inTrans;
mGlows = inGlows;
mZoom = inZoom;
}
char DragAndDropManager::isDragging() {
return mDragging && ( mSprites != NULL );
}
void DragAndDropManager::mousePressed( double inX, double inY ) {
mX = inX;
mY = inY;
mDragging = false;
}
void DragAndDropManager::mouseDragged( double inX, double inY ) {
mX = inX;
mY = inY;
mDragging = true;
}
void DragAndDropManager::mouseReleased( double inX, double inY ) {
clear();
ToolTipManager::freeze( false );
ToolTipManager::setTip( NULL );
mDragging = false;
}
void DragAndDropManager::fireRedraw() {
if( mSprites != NULL && mDragging ) {
double extraX = 0;
double extraY = 0;
if( mZoom == 3 ) {
extraY = -1;
extraX = 1;
}
// stick to nearest zoom grid
double pixelX = floor( (mX + extraX) / mZoom ) * mZoom;
double pixelY = floor( (mY + extraY) / mZoom ) * mZoom + mZoom;
if( mZoom == 3 ) {
pixelX --;
pixelY ++;
}
for( int i=0; i<mNumSprites; i++ ) {
double thisSpriteX = pixelX + mOffsets[i].x * mZoom;
double thisSpriteY = pixelY + mOffsets[i].y * mZoom;
Vector3D pos( thisSpriteX, thisSpriteY, 0 );
if( mGlows[i] ) {
// brighten only
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
}
mSprites[i]->draw( 0, 0, &pos, mZoom, mTrans[i] );
if( mGlows[i] ) {
// back to normal blend
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
}
}
}
}
+68
View File
@@ -0,0 +1,68 @@
#ifndef DRAG_AND_DROP_MANAGER_INCLUDED
#define DRAG_AND_DROP_MANAGER_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIComponentGL.h"
#include "Sprite.h"
#include "common.h"
// transparent and covers whole screen.
// should be added last in root panel so that it is on top
class DragAndDropManager : public GUIComponentGL {
public:
DragAndDropManager( double inAnchorX, double inAnchorY,
double inWidth, double inHeight );
virtual ~DragAndDropManager();
char isDragging();
// sets one sprite with 0 offset
// destroyed internally.
// set to NULL to clear
virtual void setSprite( Sprite *inSprite, double inZoom );
// set of sprites with specific offsets
// arrays and sprites destroyed internally
// cleared by passing null to above function
virtual void setSprites( int inNumSprites,
Sprite **inSprites, intPair *inOffsets,
float *inTrans,
char *inGlow,
double inZoom );
// override
virtual void mousePressed( double inX, double inY );
virtual void mouseDragged( double inX, double inY );
virtual void mouseReleased( double inX, double inY );
virtual void fireRedraw();
protected:
void clear();
int mNumSprites;
Sprite **mSprites;
intPair *mOffsets;
float *mTrans;
char *mGlows;
double mZoom;
double mX;
double mY;
char mDragging;
};
#endif
+150
View File
@@ -0,0 +1,150 @@
#include "DrawToolSet.h"
DrawToolSet::DrawToolSet( double inAnchorX, double inAnchorY,
const char *inPickButtonTipKey,
char inShowStamp )
: GUIPanelGL( inAnchorX, inAnchorY, 120, 20,
// transparent bg
new Color( 0, 0, 0, 0 ) ) {
mPenButton = new SelectableButtonGL(
new Sprite( "pen.tga", true ),
1,
inAnchorX, inAnchorY, 20, 20 );
mHorLineButton = new SelectableButtonGL(
new Sprite( "horLine.tga", true ),
1,
inAnchorX + 20, inAnchorY, 20, 20 );
mVerLineButton = new SelectableButtonGL(
new Sprite( "verLine.tga", true ),
1,
inAnchorX + 40, inAnchorY, 20, 20 );
mFillButton = new SelectableButtonGL(
new Sprite( "fill.tga", true ),
1,
inAnchorX + 60, inAnchorY, 20, 20 );
mPickColorButton = new SelectableButtonGL(
new Sprite( "pickColor.tga", true ),
1,
inAnchorX + 80, inAnchorY, 20, 20 );
mStampButton = new SelectableButtonGL(
new Sprite( "stamp.tga", true ),
1,
inAnchorX + 100, inAnchorY, 20, 20 );
mPenButton->setToolTip( "tip_pen" );
mHorLineButton->setToolTip( "tip_horLine" );
mVerLineButton->setToolTip( "tip_verLine" );
mFillButton->setToolTip( "tip_fill" );
mPickColorButton->setToolTip( inPickButtonTipKey );
mStampButton->setToolTip( "tip_stamp" );
add( mPenButton );
add( mHorLineButton );
add( mVerLineButton );
add( mFillButton );
add( mPickColorButton );
add( mStampButton );
mPenButton->addActionListener( this );
mHorLineButton->addActionListener( this );
mVerLineButton->addActionListener( this );
mFillButton->addActionListener( this );
mPickColorButton->addActionListener( this );
mStampButton->addActionListener( this );
mStampButton->setEnabled( inShowStamp );
mPenButton->setSelected( true );
mLastSelectedBeforeShift = mPenButton;
}
drawTool DrawToolSet::getSelected() {
if( mPenButton->getSelected() ) {
return pen;
}
if( mHorLineButton->getSelected() ) {
return horLine;
}
if( mVerLineButton->getSelected() ) {
return verLine;
}
if( mFillButton->getSelected() ) {
return fill;
}
if( mPickColorButton->getSelected() ) {
return pickColor;
}
if( mStampButton->getSelected() ) {
return stamp;
}
return pen;
}
void DrawToolSet::actionPerformed( GUIComponent *inTarget ) {
// select hit, turn others off
mPenButton->setSelected( inTarget == mPenButton );
mHorLineButton->setSelected( inTarget == mHorLineButton );
mVerLineButton->setSelected( inTarget == mVerLineButton );
mFillButton->setSelected( inTarget == mFillButton );
mPickColorButton->setSelected( inTarget == mPickColorButton );
mStampButton->setSelected( inTarget == mStampButton );
fireActionPerformed( this );
}
char DrawToolSet::isFocused() {
return true;
}
void DrawToolSet::specialKeyPressed( int inKey, double inX, double inY ) {
if( inKey == MG_KEY_RSHIFT || inKey == MG_KEY_LSHIFT ) {
// save last selected
if( mPenButton->getSelected() ) {
mLastSelectedBeforeShift = mPenButton;
}
else if( mHorLineButton->getSelected() ) {
mLastSelectedBeforeShift = mHorLineButton;
}
else if( mVerLineButton->getSelected() ) {
mLastSelectedBeforeShift = mVerLineButton;
}
else if( mFillButton->getSelected() ) {
mLastSelectedBeforeShift = mFillButton;
}
else if( mPickColorButton->getSelected() ) {
mLastSelectedBeforeShift = mPickColorButton;
}
else if( mStampButton->getSelected() ) {
mLastSelectedBeforeShift = mStampButton;
}
actionPerformed( mPickColorButton );
}
}
void DrawToolSet::specialKeyReleased( int inKey, double inX, double inY ) {
if( inKey == MG_KEY_RSHIFT || inKey == MG_KEY_LSHIFT ) {
actionPerformed( mLastSelectedBeforeShift );
}
}
+54
View File
@@ -0,0 +1,54 @@
#ifndef DRAW_TOOL_SET_INCLUDED
#define DRAW_TOOL_SET_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIPanelGL.h"
#include "minorGems/ui/event/ActionListener.h"
#include "minorGems/ui/event/ActionListenerList.h"
#include "buttons.h"
enum drawTool { pen, horLine, verLine, fill, pickColor, stamp };
class DrawToolSet : public GUIPanelGL, public ActionListener,
public ActionListenerList {
public:
// sets its width/height automatically
// also can specify translation key for pick button tool tip
DrawToolSet( double inAnchorX, double inAnchorY,
const char *inPickButtonTipKey = "tip_pickColor",
char inShowStamp = true );
drawTool getSelected();
// implements ActionListener
virtual void actionPerformed( GUIComponent *inTarget );
// always focused, listens for shift to switch to picker tool
virtual char isFocused();
virtual void specialKeyPressed( int inKey, double inX, double inY );
virtual void specialKeyReleased( int inKey, double inX, double inY );
protected:
SelectableButtonGL *mPenButton;
SelectableButtonGL *mHorLineButton;
SelectableButtonGL *mVerLineButton;
SelectableButtonGL *mFillButton;
SelectableButtonGL *mPickColorButton;
SelectableButtonGL *mStampButton;
// for when shift is released
SelectableButtonGL *mLastSelectedBeforeShift;
};
#endif
+549
View File
@@ -0,0 +1,549 @@
#include "Editor.h"
#include "BorderPanel.h"
#include "buttons.h"
#include "ColorEditor.h"
#include "TileEditor.h"
#include "RoomEditor.h"
#include "SpriteEditor.h"
#include "StateObjectEditor.h"
#include "SongEditor.h"
#include "MusicEditor.h"
#include "TimbreEditor.h"
#include "ScaleEditor.h"
#include "PaletteEditor.h"
#include "PlayerMoveEditor.h"
#include "SpritePicker.h"
#include "ToolTipDisplay.h"
#include "TimerDisplay.h"
#include "WarningDisplay.h"
extern int gameWidth, gameHeight;
extern ColorEditor *mainColorEditor;
extern TileEditor *mainTileEditor;
extern RoomEditor *mainRoomEditor;
extern SpriteEditor *mainSpriteEditor;
extern StateObjectEditor *mainStateObjectEditor;
extern MusicEditor *mainMusicEditor;
extern TimbreEditor *mainTimbreEditor;
extern ScaleEditor *mainScaleEditor;
extern SongEditor *mainSongEditor;
extern PaletteEditor *mainPaletteEditor;
extern PlayerMoveEditor *mainPracticePlayerEditor;
extern SpritePicker *mainSpritePicker;
extern SpritePicker *mainSpritePickerLower;
extern ToolTipDisplay *mainTipDisplay;
extern TimerDisplay *mainTimerDisplay;
extern WarningDisplay *mainWarningDisplay;
Editor::Editor( ScreenGL *inScreen, char inHasMainArea, char inHasSidePanel )
: mScreen( inScreen ),
mVisible( false ),
mHasSidePanel( inHasSidePanel ),
mHasMainArea( inHasMainArea ) {
double mainPanelXOffset = 0;
if( !mHasMainArea ) {
mainPanelXOffset = gameHeight;
}
// panel covering whole screen
mMainPanel = new PressActionGUIPanelGL( mainPanelXOffset, 0,
gameWidth - mainPanelXOffset,
gameWidth,
new Color( 0, 0, 0, 1.0 ) );
mMainPanelGuiTranslator = new GUITranslatorGL( mMainPanel, mScreen,
gameWidth );
mSidePanel = NULL;
if( mHasSidePanel ) {
// panel covering right edge of screen,
// leaving a perfect square to left
mSidePanel = new BorderPanel( gameHeight, 40,
gameWidth - gameHeight, gameHeight,
new Color( 0, 0, 0, 1.0 ),
new Color( .4, .4, .4, 1.0 ), 2 );
}
mCloseButton = NULL;
if( ! mHasMainArea && mHasSidePanel ) {
// don't attach key equivalent for button that won't close whole
// window
mCloseButton = new NoKeyCloseButtonGL( 242, 270, 8, 8 );
mSidePanel->add( mCloseButton );
}
else {
// whole window close... key equivalent
mCloseButton = new CloseButtonGL( 0, 272, 8, 8 );
// add to Main on top of all else
}
if( mCloseButton != NULL ) {
mCloseButton->addActionListener( this );
}
}
void Editor::postConstruction() {
// put side panel and main widgets on top of everything else
postConstructionSide();
postConstructionMain();
}
void Editor::postConstructionSide() {
if( mHasSidePanel ) {
// on top of everything else
mSidePanel->add( mainWarningDisplay );
mMainPanel->add( mSidePanel );
}
}
void Editor::postConstructionMain() {
if( ! mHasMainArea && mHasSidePanel ) {
// side-panel only, nothing left to add
}
else {
mMainPanel->add( mCloseButton );
mMainPanel->add( mainTipDisplay );
mMainPanel->add( mainTimerDisplay );
}
}
Editor::~Editor() {
setVisible( false );
if( mSidePanel != NULL ) {
mSidePanel->remove( mainWarningDisplay );
if( ! mMainPanel->contains( mSidePanel ) ) {
delete mSidePanel;
}
}
if( mMainPanel->contains( mainTipDisplay ) ) {
// we don't destroy this
mMainPanel->remove( mainTipDisplay );
mMainPanel->remove( mainTimerDisplay );
}
// recursively deletes all sub-components
delete mMainPanelGuiTranslator;
}
void Editor::setVisible( char inIsVisible ) {
if( inIsVisible && !mVisible) {
mScreen->addSceneHandler( mMainPanelGuiTranslator );
mScreen->addKeyboardHandler( mMainPanelGuiTranslator );
mScreen->addMouseHandler( mMainPanelGuiTranslator );
}
if( !inIsVisible && mVisible) {
mScreen->removeSceneHandler( mMainPanelGuiTranslator );
mScreen->removeKeyboardHandler( mMainPanelGuiTranslator );
mScreen->removeMouseHandler( mMainPanelGuiTranslator );
}
mVisible = inIsVisible;
}
char Editor::isVisible() {
return mVisible;
}
void Editor::showColorEditor() {
mainColorEditor->addActionListener( this );
if( mSidePanel != NULL ) {
mMainPanel->remove( mSidePanel );
}
mainColorEditor->setVisible( true );
}
void Editor::showTileEditor() {
setVisible( false );
mainTileEditor->addActionListener( this );
mainTileEditor->setVisible( true );
}
void Editor::showRoomEditor() {
setVisible( false );
mainRoomEditor->addActionListener( this );
mainRoomEditor->setVisible( true );
}
void Editor::showSpriteEditor() {
setVisible( false );
// make sure pickers match (editor shows mainSpritePicker)
mainSpritePickerLower->cloneState( mainSpritePicker );
mainSpriteEditor->addActionListener( this );
mainSpriteEditor->setVisible( true );
}
void Editor::showStateObjectEditor() {
setVisible( false );
mainStateObjectEditor->addActionListener( this );
mainStateObjectEditor->setVisible( true );
}
void Editor::showMusicEditor() {
setVisible( false );
mainMusicEditor->addActionListener( this );
mainMusicEditor->setVisible( true );
}
void Editor::showTimbreEditor() {
setVisible( false );
mainTimbreEditor->addActionListener( this );
mainTimbreEditor->setVisible( true );
}
void Editor::showScaleEditor() {
setVisible( false );
mainScaleEditor->addActionListener( this );
mainScaleEditor->setVisible( true );
}
void Editor::showSongEditor() {
setVisible( false );
mainSongEditor->addActionListener( this );
mainSongEditor->setVisible( true );
}
void Editor::showPaletteEditor() {
setVisible( false );
mainPaletteEditor->addActionListener( this );
mainPaletteEditor->setVisible( true );
}
void Editor::showPracticePlayerEditor() {
setVisible( false );
// any sub-editor open?
if( mainPaletteEditor->isVisible() ) {
if( mainColorEditor->isVisible() ) {
mainColorEditor->setVisible( false );
mainColorEditor->removeActionListener( mainPaletteEditor );
if( mainPaletteEditor->mSidePanel != NULL ) {
mainPaletteEditor->mMainPanel->add(
mainPaletteEditor->mSidePanel );
}
}
mainPaletteEditor->setVisible( false );
if( mainPaletteEditor->isListening( mainTileEditor ) ) {
mainPaletteEditor->removeActionListener( mainTileEditor );
mainTileEditor->setVisible( true );
}
else if( mainPaletteEditor->isListening( mainRoomEditor ) ) {
mainPaletteEditor->removeActionListener( mainRoomEditor );
mainRoomEditor->setVisible( true );
}
}
if( mainTileEditor->isVisible() ) {
if( mainColorEditor->isVisible() ) {
mainColorEditor->setVisible( false );
mainColorEditor->removeActionListener( mainTileEditor );
if( mainTileEditor->mSidePanel != NULL ) {
mainTileEditor->mMainPanel->add( mainTileEditor->mSidePanel );
}
}
mainTileEditor->setVisible( false );
mainTileEditor->removeActionListener( mainRoomEditor );
mainRoomEditor->setVisible( true );
}
if( mainSpriteEditor->isVisible() ) {
if( mainColorEditor->isVisible() ) {
mainColorEditor->setVisible( false );
mainColorEditor->removeActionListener( mainSpriteEditor );
if( mainSpriteEditor->mSidePanel != NULL ) {
mainSpriteEditor->mMainPanel->add(
mainSpriteEditor->mSidePanel );
}
}
mainSpriteEditor->setVisible( false );
mainSpriteEditor->removeActionListener( mainStateObjectEditor );
mainStateObjectEditor->setVisible( true );
}
if( mainTimbreEditor->isVisible() ) {
mainTimbreEditor->setVisible( false );
mainTimbreEditor->removeActionListener( mainSongEditor );
mainSongEditor->setVisible( true );
}
if( mainScaleEditor->isVisible() ) {
mainScaleEditor->setVisible( false );
mainScaleEditor->removeActionListener( mainSongEditor );
mainSongEditor->setVisible( true );
}
if( mainMusicEditor->isVisible() ) {
mainMusicEditor->setVisible( false );
mainMusicEditor->removeActionListener( mainSongEditor );
mainSongEditor->setVisible( true );
}
if( mainRoomEditor->isVisible() ) {
// special case for RoomEditor, so it can reset shared
// GameStateDisplay
mainRoomEditor->editorClosing();
mainRoomEditor->setVisible( false );
mainRoomEditor->removeActionListener( this );
}
if( mainStateObjectEditor->isVisible() ) {
mainStateObjectEditor->setVisible( false );
mainRoomEditor->removeActionListener( this );
}
if( mainSongEditor->isVisible() ) {
mainSongEditor->setVisible( false );
mainSongEditor->removeActionListener( this );
}
mainPracticePlayerEditor->setVisible( true );
}
void Editor::hidePracticePlayerEditor() {
mainPracticePlayerEditor->setVisible( false );
setVisible( true );
}
void Editor::redrawMainPanel() {
mMainPanel->fireRedraw();
}
void Editor::actionPerformed( GUIComponent *inTarget ) {
if( mCloseButton != NULL && inTarget == mCloseButton ) {
// tell subclass about it
editorClosing();
if( this != mainColorEditor &&
mainColorEditor->isVisible() ) {
// color editor on top of our side panel
// force it to close and redisplay our side panel
mainColorEditor->setVisible( false );
mainColorEditor->removeActionListener( this );
if( mSidePanel != NULL ) {
mMainPanel->add( mSidePanel );
}
}
// fire action telling others that we got a close-button press
fireActionPerformed( this );
}
else if( inTarget == mainColorEditor ) {
// color editor closed
mainColorEditor->setVisible( false );
mainColorEditor->removeActionListener( this );
if( mSidePanel != NULL ) {
mMainPanel->add( mSidePanel );
}
colorEditorClosed();
}
else if( inTarget == mainTileEditor ) {
// tile editor closed
mainTileEditor->setVisible( false );
mainTileEditor->removeActionListener( this );
setVisible( true );
}
else if( inTarget == mainRoomEditor ) {
// room editor closed
mainRoomEditor->setVisible( false );
mainRoomEditor->removeActionListener( this );
setVisible( true );
}
else if( inTarget == mainSpriteEditor ) {
// sprite editor closed
mainSpriteEditor->setVisible( false );
mainSpriteEditor->removeActionListener( this );
// make sure pickers match (editor showed mainSpritePicker, so copy
// changes from there back into Lower picker)
mainSpritePicker->cloneState( mainSpritePickerLower );
setVisible( true );
}
else if( inTarget == mainStateObjectEditor ) {
// object editor closed
mainStateObjectEditor->setVisible( false );
mainStateObjectEditor->removeActionListener( this );
setVisible( true );
}
else if( inTarget == mainMusicEditor ) {
// music editor closed
mainMusicEditor->setVisible( false );
mainMusicEditor->removeActionListener( this );
setVisible( true );
}
else if( inTarget == mainTimbreEditor ) {
// music editor closed
mainTimbreEditor->setVisible( false );
mainTimbreEditor->removeActionListener( this );
setVisible( true );
}
else if( inTarget == mainScaleEditor ) {
// music editor closed
mainScaleEditor->setVisible( false );
mainScaleEditor->removeActionListener( this );
setVisible( true );
}
else if( inTarget == mainSongEditor ) {
// song editor closed
mainSongEditor->setVisible( false );
mainSongEditor->removeActionListener( this );
setVisible( true );
}
else if( inTarget == mainPaletteEditor ) {
// music editor closed
mainPaletteEditor->setVisible( false );
mainPaletteEditor->removeActionListener( this );
setVisible( true );
}
else if( inTarget == mainPracticePlayerEditor ) {
// practice player editor closed
mainPracticePlayerEditor->setVisible( false );
mainPracticePlayerEditor->removeActionListener( this );
setVisible( true );
}
}
+126
View File
@@ -0,0 +1,126 @@
#ifndef EDITOR_INCLUDED
#define EDITOR_INCLUDED
#include "buttons.h"
#include "minorGems/graphics/openGL/ScreenGL.h"
#include "minorGems/graphics/openGL/gui/GUIPanelGL.h"
#include "minorGems/graphics/openGL/gui/GUITranslatorGL.h"
#include "minorGems/ui/event/ActionListener.h"
#include "minorGems/ui/event/ActionListenerList.h"
#include "PressActionGUIPanelGL.h"
#include "BorderPanel.h"
class Editor : public GUIComponent,
public ActionListener, public ActionListenerList {
public:
virtual ~Editor();
// defaults to invisible
virtual void setVisible( char inIsVisible );
virtual char isVisible();
// implements ActionListener
virtual void actionPerformed( GUIComponent *inTarget );
// hides our side panel and shows color editor until close
virtual void showColorEditor();
// hides our panels and shows tile editor until it is closed
virtual void showTileEditor();
// hides our panels and shows room editor until it is closed
virtual void showRoomEditor();
// hides our panels and shows sprite editor until it is closed
virtual void showSpriteEditor();
// hides our panels and shows Object editor until it is closed
virtual void showStateObjectEditor();
// hides our panels and shows music editor until it is closed
virtual void showMusicEditor();
// hides our panels and shows timbre editor until it is closed
virtual void showTimbreEditor();
// hides our panels and shows scale editor until it is closed
virtual void showScaleEditor();
// hides our panels and shows song editor until it is closed
virtual void showSongEditor();
// hides our panels and shows palette editor until it is closed
virtual void showPaletteEditor();
virtual void showPracticePlayerEditor();
virtual void hidePracticePlayerEditor();
// hack to draw main panel NOW... useful for making flip books
virtual void redrawMainPanel();
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing() = 0;
protected:
Editor( ScreenGL *inScreen, char inHasMainArea = true,
char inHasSidePanel = true );
// must be called by sub-classes at end of their constructors
// (adds GUI components that must go on top of everything else)
void postConstruction();
// gives more control over overlay order
// those that don't need it call above
void postConstructionSide();
void postConstructionMain();
// can be overridden by subclasses to receive notification that
// an overlying color editor closed
virtual void colorEditorClosed() {};
ScreenGL *mScreen;
char mVisible;
BorderPanel *mSidePanel;
PressActionGUIPanelGL *mMainPanel;
char mHasSidePanel, mHasMainArea;
GUITranslatorGL *mMainPanelGuiTranslator;
SpriteButtonGL *mCloseButton;
};
#endif
+209
View File
@@ -0,0 +1,209 @@
#include "Envelope.h"
#include <stdio.h>
#include "minorGems/util/log/AppLog.h"
// #include <assert.h>
Envelope::Envelope( double inAttackTime, double inDecayTime,
double inSustainLevel, double inReleaseTime,
int inMinNoteLengthInGridSteps,
int inMaxNoteLengthInGridSteps,
int inGridStepDurationInSamples )
: mActiveNoteCount( 0 ),
mGridStepDurationInSamples( inGridStepDurationInSamples ),
mMinEnvNum( inMinNoteLengthInGridSteps ),
mNumComputedEnvelopes( inMaxNoteLengthInGridSteps ),
mEvelopeLengths( new int[ inMaxNoteLengthInGridSteps ] ),
mComputedEnvelopes( new double*[ inMaxNoteLengthInGridSteps ] ) {
for( int i=mMinEnvNum-1; i<mNumComputedEnvelopes; i++ ) {
int length = (i+1) * inGridStepDurationInSamples;
mEvelopeLengths[i] = length;
mComputedEnvelopes[i] = new double[ length ];
for( int s=0; s<length; s++ ) {
double t = s / (double)( length - 1 );
if( t < inAttackTime ) {
// assert( inAttackTime > 0 );
mComputedEnvelopes[i][s] = t / inAttackTime;
}
else if( t < inAttackTime + inDecayTime ) {
// assert( inDecayTime > 0 );
// decay down to sustain level
mComputedEnvelopes[i][s] =
( 1.0 - inSustainLevel ) *
( inAttackTime + inDecayTime - t ) /
( inDecayTime )
+ inSustainLevel;
}
else if( 1.0 - t > inReleaseTime ) {
mComputedEnvelopes[i][s] = inSustainLevel;
}
else {
if( inReleaseTime > 0 ) {
mComputedEnvelopes[i][s] =
inSustainLevel -
inSustainLevel *
( inReleaseTime - ( 1.0 - t ) ) / inReleaseTime;
}
else {
// release time 0
// hold sustain until end
mComputedEnvelopes[i][s] = inSustainLevel;
}
}
}
// test code to output evelopes for plotting in gnuplot
if( false && i == 0 ) {
FILE *file = fopen( "env0.txt", "w" );
for( int s=0; s<length; s++ ) {
fprintf( file, "%f %f\n",
s / (double)( length - 1 ),
mComputedEnvelopes[i][s] );
}
fclose( file );
}
}
}
Envelope::Envelope( double inAttackTime, double inHoldTime,
double inReleaseTime,
int inMinNoteLengthInGridSteps,
int inMaxNoteLengthInGridSteps,
int inGridStepDurationInSamples )
: mActiveNoteCount( 0 ),
mGridStepDurationInSamples( inGridStepDurationInSamples ),
mMinEnvNum( inMinNoteLengthInGridSteps ),
mNumComputedEnvelopes( inMaxNoteLengthInGridSteps ),
mEvelopeLengths( new int[ inMaxNoteLengthInGridSteps ] ),
mComputedEnvelopes( new double*[ inMaxNoteLengthInGridSteps ] ) {
for( int i=mMinEnvNum-1; i<mNumComputedEnvelopes; i++ ) {
int length = (i+1) * inGridStepDurationInSamples;
mEvelopeLengths[i] = length;
mComputedEnvelopes[i] = new double[ length ];
double *thisEnv = mComputedEnvelopes[i];
// do boundary checking in sample space... faster
int attackSamples = (int)( length * inAttackTime );
int attackHoldSamples = (int)( length * (inAttackTime + inHoldTime) );
int attackHoldReleaseSamples =
(int)( length * (inAttackTime + inHoldTime + inReleaseTime ) );
int releaseSamples =
(int)( length * inReleaseTime );
for( int s=0; s<attackSamples; s++ ) {
thisEnv[s] = s / (double)attackSamples;
}
for( int s=attackSamples; s<attackHoldSamples; s++ ) {
thisEnv[s] = 1.0;
}
for( int s=attackHoldSamples; s<attackHoldReleaseSamples; s++ ) {
thisEnv[s] = 1.0 -
(s - attackHoldSamples) / (double)releaseSamples;
}
for( int s=attackHoldReleaseSamples; s<length; s++ ) {
thisEnv[s] = 0;
}
/*
for( int s=0; s<length; s++ ) {
double t = s / (double)( length - 1 );
//if( t < inAttackTime ) {
if( s < attackSamples ) {
// assert( inAttackTime > 0 );
thisEnv[s] = t / inAttackTime;
}
//else if( t < inAttackTime + inHoldTime ) {
else if( s < attackHoldSamples ) {
// assert( inDecayTime > 0 );
// hold at 1
thisEnv[s] = 1.0;
}
else {
if( inReleaseTime > 0 &&
//t < inAttackTime + inHoldTime + inReleaseTime ) {
s < attackHoldReleaseSamples ) {
thisEnv[s] =
1.0 -
( t - ( inAttackTime + inHoldTime ) )
/ inReleaseTime;
}
else {
// release time 0
// end immediately after hold
thisEnv[s] = 0;
}
}
}
*/
// test code to output evelopes for plotting in gnuplot
if( false && i == 0 ) {
FILE *file = fopen( "env0.txt", "w" );
for( int s=0; s<length; s++ ) {
fprintf( file, "%f %f\n",
s / (double)( length - 1 ),
mComputedEnvelopes[i][s] );
}
fclose( file );
}
}
}
Envelope::~Envelope() {
for( int i=mMinEnvNum-1; i<mNumComputedEnvelopes; i++ ) {
delete [] mComputedEnvelopes[i];
}
delete [] mEvelopeLengths;
delete [] mComputedEnvelopes;
}
double *Envelope::getEnvelope( int inNoteLengthInGridSteps ) {
if( inNoteLengthInGridSteps > mNumComputedEnvelopes ||
inNoteLengthInGridSteps < mMinEnvNum ) {
AppLog::error( "Error: evelope for unsupported number of"
" grid steps requested" );
inNoteLengthInGridSteps = mMinEnvNum;
}
return mComputedEnvelopes[ inNoteLengthInGridSteps - 1 ];
}
+80
View File
@@ -0,0 +1,80 @@
class Envelope {
public:
/**
* Constructs an ADSR envelope.
*
* All parameters are in range 0..1, and sum of the three time
* parameters must be <= 1.
*
* @param inMinNoteLengthInGridSteps the minimum note length to
* cover. Exact envelopes will be generated for notes starting at
* this length.
* @param inMaxNoteLengthInGridSteps the maximum note length to
* cover. Exact envelopes will be generated for notes up
* to this length.
* @param inGridStepDurationInSamples the number of samples
* per grid step.
*/
Envelope( double inAttackTime, double inDecayTime,
double inSustainLevel, double inReleaseTime,
int inMinNoteLengthInGridSteps,
int inMaxNoteLengthInGridSteps,
int inGridStepDurationInSamples );
/**
* Constructs an AHR envelope (sustain at 1 during hold)
*
* All parameters are in range 0..1, and sum of the three time
* parameters must be <= 1.
*
* @param inMinNoteLengthInGridSteps the minimum note length to
* cover. Exact envelopes will be generated for notes starting at
* this length.
* @param inMaxNoteLengthInGridSteps the maximum note length to
* cover. Exact envelopes will be generated for notes up
* to this length.
* @param inGridStepDurationInSamples the number of samples
* per grid step.
*/
Envelope( double inAttackTime, double inHoldTime,
double inReleaseTime,
int inMinNoteLengthInGridSteps,
int inMaxNoteLengthInGridSteps,
int inGridStepDurationInSamples );
~Envelope();
/**
* Gets an evenlope for a given note length.
*
* @return an evelope of values in [0,1] that can be indexed by
* sample number. Will be destroyed when this class is destroyed.
*/
double *getEnvelope( int inNoteLengthInGridSteps );
// how many playing notes are still using this envelope?
int mActiveNoteCount;
int mGridStepDurationInSamples;
private:
int mMinEnvNum;
int mNumComputedEnvelopes;
int *mEvelopeLengths;
double **mComputedEnvelopes;
};
+37
View File
@@ -0,0 +1,37 @@
#include "FixedTipDisplay.h"
#include "minorGems/util/stringUtils.h"
#include <stdio.h>
FixedTipDisplay::FixedTipDisplay( double inAnchorX, double inAnchorY )
: ToolTipDisplay( inAnchorX, inAnchorY ),
mTip( NULL ) {
}
FixedTipDisplay::~FixedTipDisplay() {
if( mTip != NULL ) {
delete [] mTip;
mTip = NULL;
}
}
char *FixedTipDisplay::getTip() {
return mTip;
}
void FixedTipDisplay::setTip( char *inTip ) {
if( mTip != NULL ) {
delete [] mTip;
mTip = NULL;
}
if( inTip != NULL ) {
mTip = stringDuplicate( inTip );
}
}
+26
View File
@@ -0,0 +1,26 @@
#include "ToolTipDisplay.h"
class FixedTipDisplay : public ToolTipDisplay {
public:
FixedTipDisplay( double inAnchorX, double inAnchorY );
virtual ~FixedTipDisplay();
// can be NULL, copied internally, so destroyed by caller
void setTip( char *inTip );
protected:
// override to take tip from Fixed manager
virtual char *getTip();
char *mTip;
};
+295
View File
@@ -0,0 +1,295 @@
#include "GameHalf.h"
#include "resourceManager.h"
#include "resourceDatabase.h"
#include "usageDatabase.h"
#include "speechHints.h"
#include "SpriteResource.h"
#include "Room.h"
#include "StateObject.h"
#include "TimbreResource.h"
#include "Scale.h"
#include "Music.h"
#include "Song.h"
#include "Scene.h"
#include "Palette.h"
#include "ToolTipManager.h"
#include "ToolTipDisplay.h"
#include "TimerDisplay.h"
#include "WarningDisplay.h"
#include "imageCache.h"
#include "packSaver.h"
#include "minorGems/system/Time.h"
#include "minorGems/util/TranslationManager.h"
ToolTipDisplay *mainTipDisplay;
TimerDisplay *mainTimerDisplay;
WarningDisplay *mainWarningDisplay;
GameHalf::GameHalf()
: mQuitting( false ) {
initImageCache();
// init this first so it creates dir where stringDB can go.
initResourceManager();
initDatabase();
initUsageDatabase();
initSpeechHints();
/*
for( int i=0; i<5; i++ ) {
initUsageDatabase();
freeUsageDatabase();
}
exit( 0 );
*/
Room::staticInit();
SpriteResource::staticInit();
ToolTipManager::staticInit();
StateObject::staticInit();
TimbreResource::staticInit();
Scale::staticInit();
Music::staticInit();
Song::staticInit();
Scene::staticInit();
Palette::staticInit();
initPackSaver();
mainTimerDisplay = new TimerDisplay( 16, 272 );
mainTipDisplay = new ToolTipDisplay( 48, 272 );
mainWarningDisplay = new WarningDisplay( 299, 150 );
}
GameHalf::~GameHalf() {
delete mainTipDisplay;
delete mainTimerDisplay;
delete mainWarningDisplay;
ToolTipManager::staticFree();
Room::staticFree();
SpriteResource::staticFree();
StateObject::staticFree();
Song::staticFree();
Music::staticFree();
TimbreResource::staticFree();
Scale::staticFree();
Scene::staticFree();
Palette::staticFree();
freePackSaver();
freeSpeechHints();
freeUsageDatabase();
freeDatabase();
freeResourceManager();
freeImageCache();
}
char GameHalf::isQuitting() {
return mQuitting;
}
extern char hardToQuitMode;
static unsigned char lastKeyPressed = '\0';
extern char practiceMode;
static void cancelOurTip() {
// is our tip still showing?
char *tip = ToolTipManager::getTip();
char *ourTip = (char *)TranslationManager::translate(
"quitQuestion" );
if( tip != NULL &&
strcmp( tip, ourTip ) == 0 ) {
// unfreeze
ToolTipManager::freeze( false );
// clear it instantly (no fade)
ToolTipManager::setTip( "" );
}
}
void GameHalf::keyPressed( unsigned char inKey, int inX, int inY ) {
//if( inKey == 'q' || inKey == 'Q' || inKey == 27 ) {
if( !hardToQuitMode ) {
if( lastKeyPressed == 27 ) {
// is our tip no longer showing?
char *tip = ToolTipManager::getTip();
char *ourTip = (char *)TranslationManager::translate(
"quitQuestion" );
if( tip == NULL ||
strcmp( tip, ourTip ) != 0 ) {
// reset last key to look for another ESC press
lastKeyPressed = '\0';
}
}
// ESC
if( lastKeyPressed == 27 && ( inKey == 'y' || inKey == 'Y' ) ) {
//exit( 0 );
mQuitting = true;
practiceMode = false;
// block "y" event from going to other handlers and showing
// up in a speech bubble or search box
mEatEvent = true;
}
else if( lastKeyPressed == 27 && ( inKey != 'y' && inKey != 'Y' ) ) {
// cancel it
cancelOurTip();
}
else {
if( inKey == 27 ) {
// override freeze
ToolTipManager::freeze( false );
ToolTipManager::setTip(
(char *)TranslationManager::translate(
"quitQuestion" ) );
// re-freeze until Y or another key pressed
ToolTipManager::freeze( true );
}
}
lastKeyPressed = inKey;
}
else {
// # followed by ESC
if( lastKeyPressed == '#' && inKey == 27 ) {
//exit( 0 );
mQuitting = true;
practiceMode = false;
}
lastKeyPressed = inKey;
}
}
void GameHalf::mouseDragged( int inX, int inY ) {
if( lastKeyPressed == 27 ) {
cancelOurTip();
}
}
void GameHalf::mousePressed( int inX, int inY ) {
if( lastKeyPressed == 27 ) {
cancelOurTip();
}
}
void GameHalf::mouseReleased( int inX, int inY ) {
if( lastKeyPressed == 27 ) {
cancelOurTip();
}
}
void GameHalf::decrementStepCount() {
if( mStepsLeft == mStepsPerSecond * mSecondsPerMove ) {
// first step, just take a single step regardless of time
mStepsLeft --;
mLastStepTime = Time::getCurrentTime();
}
else if( Time::getCurrentTime() - mLastStepTime >=
1.0 / mStepsPerSecond ) {
// subsequent steps based on time
// at least one real-time step has passed
double numPassed = ( Time::getCurrentTime() - mLastStepTime )
* mStepsPerSecond ;
// round down
int wholeNumPassed = (int)numPassed;
double extraTime =
( numPassed - wholeNumPassed ) / mStepsPerSecond;
char passingZero = false;
if( mStepsLeft > 0 ) {
passingZero = true;
}
mStepsLeft -= wholeNumPassed;
mLastStepTime = Time::getCurrentTime();
// add extra for next step
mLastStepTime -= extraTime;
if( mStepsLeft < 0 && passingZero) {
mStepsLeft = 0;
}
}
}
+92
View File
@@ -0,0 +1,92 @@
#ifndef GAME_HALF_INCLUDED
#define GAME_HALF_INCLUDED
#include "minorGems/graphics/openGL/MouseHandlerGL.h"
#include "minorGems/graphics/openGL/KeyboardHandlerGL.h"
#include "minorGems/graphics/openGL/SceneHandlerGL.h"
#include "minorGems/graphics/openGL/RedrawListenerGL.h"
#include "minorGems/graphics/openGL/ScreenGL.h"
// superclass for each half of the game (player or controller)
class GameHalf : public MouseHandlerGL,
public KeyboardHandlerGL,
public SceneHandlerGL,
public RedrawListenerGL {
public:
GameHalf();
virtual ~GameHalf();
virtual void setScreen( ScreenGL *inScreen ) {
mScreen = inScreen;
}
// true if game is trying to quit
virtual char isQuitting();
virtual void resetTimer() = 0;
virtual void step() = 0;
// implements the SceneHandlerGL interface
virtual void drawScene() = 0;
// redraw listener, defaults to doing nothing
virtual void fireRedraw() {}
virtual void postRedraw() {}
// empty implementations of each input function,
// in case subclasses don't need them
// implements the MouseHandlerGL interface
virtual void mouseMoved( int inX, int inY ) {}
virtual void mouseDragged( int inX, int inY );
virtual void mousePressed( int inX, int inY );
virtual void mouseReleased( int inX, int inY );
// implements the KeyboardHandlerGL interface
virtual char isFocused() {
return true;
}
// handles quit and hardQuit mode
virtual void keyPressed( unsigned char inKey, int inX, int inY );
virtual void specialKeyPressed( int inKey, int inX, int inY ) {}
virtual void keyReleased( unsigned char inKey, int inX, int inY ) {}
virtual void specialKeyReleased( int inKey, int inX, int inY ) {}
protected:
ScreenGL *mScreen;
int mSecondsPerMove;
int mStepsPerSecond;
int mStepsLeft;
double mLastStepTime;
void decrementStepCount();
char mQuitting;
};
#endif
+956
View File
@@ -0,0 +1,956 @@
#include "GameState.h"
#include "Music.h"
#include "Song.h"
#include "Scene.h"
#include "SongPicker.h"
#include "SongEditor.h"
#include "speechHints.h"
#include "minorGems/util/stringUtils.h"
#include "minorGems/util/log/AppLog.h"
#include <stdio.h>
extern SongPicker *mainSongPicker;
extern SongEditor *mainSongEditor;
char GameState::sNoteToggleBuffer[N][N];
GameState::GameState() {
mRoomTrans = 255;
mObjectZeroFrozen = false;
mLocksOn = true;
// must have object 0 always
StateObject obj;
newObject( G / 2, G / 2, obj );
mSelectedObject = 0;
}
GameState::~GameState() {
for( int i=0; i<mObjects.size(); i++ ) {
delete *( mObjects.getElement( i ) );
}
}
GameState *GameState::copy() {
GameState *s = new GameState();
s->mRoom = mRoom;
s->mRoomTrans = mRoomTrans;
s->mObjectZeroFrozen = mObjectZeroFrozen;
s->mLocksOn = mLocksOn;
// default s has one object in it
// clear it
int i;
for( i=0; i<s->mObjects.size(); i++ ) {
delete *( s->mObjects.getElement( i ) );
}
s->mObjects.deleteAll();
for( i=0; i<mObjects.size(); i++ ) {
StateObjectInstance *otherObject = *( mObjects.getElement( i ) );
s->mObjects.push_back( otherObject->copy() );
}
s->mSelectedObject = mSelectedObject;
return s;
}
GameState::GameState( unsigned char *inMessage, int inLength ) {
int numUsed;
if( inLength < N * N ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
int i=0;
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
sNoteToggleBuffer[y][x] = inMessage[i];
i++;
}
}
numUsed = N * N;
inLength -= numUsed;
inMessage = &( inMessage[ numUsed ] );
// now music unique id:
// do this *just* to fetch a copy of any saved remote music into the
// local database. Don't actually load it into our game state (because
// live music grid loaded above)
mMusicID = readUniqueID( inMessage, inLength, &numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
inLength -= numUsed;
inMessage = &( inMessage[ numUsed ] );
Music m( mMusicID );
// now scene unique id:
// do this *just* to fetch a copy of any saved remote scene into the
// local database. Don't actually load it into our game state (because
// we're loading the live state here, which may differ)
mSceneID = readUniqueID( inMessage, inLength, &numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
inLength -= numUsed;
inMessage = &( inMessage[ numUsed ] );
Scene s( mSceneID );
uniqueID id = readUniqueID( inMessage, inLength, &numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
inLength -= numUsed;
inMessage = &( inMessage[ numUsed ] );
Room r( id );
mRoom = r;
if( inLength <= 0 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
mRoomTrans = (char)inMessage[0];
inLength -= 1;
inMessage = &( inMessage[ 1 ] );
if( inLength <= 0 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
mObjectZeroFrozen = (char)inMessage[0];
inLength -= 1;
inMessage = &( inMessage[ 1 ] );
if( inLength <= 0 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
int numObjects = inMessage[0];
inLength -= 1;
inMessage = &( inMessage[ 1 ] );
AppLog::getLog()->logPrintf(
Log::DETAIL_LEVEL,
"Parsing message for state with %d objects\n", numObjects );
for( int i=0; i<numObjects; i++ ) {
id = readUniqueID( inMessage, inLength, &numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
inLength -= numUsed;
inMessage = &( inMessage[ numUsed ] );
StateObject obj( id );
StateObjectInstance *o = new StateObjectInstance( obj );
mObjects.push_back( o );
if( inLength <= 0 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
o->mObjectTrans = (char)inMessage[0];
inLength -= 1;
inMessage = &( inMessage[ 1 ] );
o->mAnchorPosition = readIntPair( inMessage, inLength,
&numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
inLength -= numUsed;
inMessage = &( inMessage[ numUsed ] );
o->mSpeechOffset = readIntPair( inMessage, inLength,
&numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
inLength -= numUsed;
inMessage = &( inMessage[ numUsed ] );
o->mActionOffset = readIntPair( inMessage, inLength,
&numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
inLength -= numUsed;
inMessage = &( inMessage[ numUsed ] );
if( inLength < 4 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
o->mSpeechFlip = (char)( inMessage[0] );
o->mSpeechBox = (char)( inMessage[1] );
o->mLocked = (char)( inMessage[2] );
int speechLength = inMessage[3];
inLength -= 4;
inMessage = &( inMessage[4] );
setSpeechHint( id, o->mSpeechOffset, o->mSpeechFlip );
if( inLength < speechLength ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
delete [] o->mSpokenMessage;
o->mSpokenMessage = new char[ speechLength + 1 ];
memcpy( o->mSpokenMessage, inMessage, speechLength );
o->mSpokenMessage[speechLength] = '\0';
inLength -= speechLength;
inMessage = &( inMessage[ speechLength ] );
if( inLength < 11 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
memcpy( o->mAction, inMessage, 11 );
inLength -= 11;
inMessage = &( inMessage[ 11 ] );
//printf( " " );
//o->print();
}
mHasSong = false;
// ignore the possibility of this being present if we are Controller
// song should never be sent by Player, but is sometimes due to a v14
// bug.
if( inLength >= U && mainSongEditor == NULL ) {
// song ID present?
uniqueID id = readUniqueID( inMessage, inLength, &numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse GameState from message\n" );
return;
}
inLength -= numUsed;
inMessage = &( inMessage[ numUsed ] );
// load this Song, to force it to be fetched from network
Song s( id );
// from network, but don't save it, because it was live on Controller
// side (repeat same ID, even though it doesn't match---this
// live song has no ID, because it's never been saved)
Song liveSong( id, inMessage, inLength, true, false );
mLiveSongReceived = liveSong;
mHasSong = true;
}
/*
AppLog::getLog()->logPrintf(
Log::DETAIL_LEVEL,
"After parse, %d bytes left over\n", inLength );
*/
mSelectedObject = 0;
}
char GameState::hasSong() {
// for now
return mHasSong;
}
// from musicPlayer.cpp
extern char noteToggles[PARTS][S][N][N];
unsigned char *GameState::getStateAsMessage( int *outLength ) {
// NOTE: music is a little inelegant, but that's because
// it is *separate* from the game state (not undone, not bookmarked,
// etc, along with the game state)
// format:
// NxN note toggles (actual, live music state, maybe not yet saved)
// Music ID (saved music selected in music picker)
// Room ID
// num objects
// each object:
// StateObject ID
// object trans (0..255, 1 char)
// anchor pos (as 8-byte int pair)
// speech offset (as 8-byte int pair)
// speech flip (1 char)
// speech length (1 char)
// speech chars
// -- v14 stuff at end (so v13 can ignore it): --
// NOTE: these are ONLY present in messages sent FROM Controller
// Song ID of last saved song
// Inline version of live (perhaps not saved) Song object
SimpleVector<unsigned char> messageAccum;
// for v13 backwards compat
// send across first bar of first instrument
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
messageAccum.push_back(
(unsigned char)( noteToggles[0][0][y][x] ) );
}
}
messageAccum.push_back( mMusicID.bytes, U );
messageAccum.push_back( mSceneID.bytes, U );
uniqueID id = mRoom.getUniqueID();
messageAccum.push_back( id.bytes, U );
messageAccum.push_back( mRoomTrans );
messageAccum.push_back( (unsigned char)mObjectZeroFrozen );
int numObjects = mObjects.size();
messageAccum.push_back( (unsigned char)( numObjects ) );
AppLog::getLog()->logPrintf(
Log::DETAIL_LEVEL,
"Generating message for state with %d objects\n",
numObjects );
for( int i=0; i<numObjects; i++ ) {
StateObjectInstance *o = *( mObjects.getElement( i ) );
//printf( " " );
//o->print();
id = o->mObject.getUniqueID();
messageAccum.push_back( id.bytes, U );
messageAccum.push_back( o->mObjectTrans );
messageAccum.push_back( getChars( o->mAnchorPosition ), 8 );
messageAccum.push_back( getChars( o->mSpeechOffset ), 8 );
messageAccum.push_back( getChars( o->mActionOffset ), 8 );
messageAccum.push_back( (unsigned char)( o->mSpeechFlip ) );
messageAccum.push_back( (unsigned char)( o->mSpeechBox ) );
messageAccum.push_back( (unsigned char)( o->mLocked ) );
int speechLength = strlen( o->mSpokenMessage );
messageAccum.push_back( (unsigned char)speechLength );
messageAccum.push_back( (unsigned char *)( o->mSpokenMessage ),
speechLength );
messageAccum.push_back( (unsigned char *)( o->mAction ),
11 );
}
if( mainSongEditor != NULL ) {
// ID of selected, saved song
uniqueID songID = mainSongPicker->getSelectedResourceID();
messageAccum.push_back( songID.bytes, U );
int numBytes;
unsigned char *liveSongBytes =
mainSongEditor->getLiveSong().makeBytes( &numBytes );
messageAccum.push_back( liveSongBytes, numBytes );
delete [] liveSongBytes;
}
*outLength = messageAccum.size();
return messageAccum.getElementArray();
}
int GameState::getHitObject( int inGridX, int inGridY ) {
// top objects first
int numObjects = mObjects.size();
// but consider 0 first
StateObjectInstance *o = *( mObjects.getElement( 0 ) );
if( o->mAnchorPosition.x / P == inGridX
&&
o->mAnchorPosition.y / P == inGridY ) {
return 0;
}
// always consider unlocked before locked
for( int i=numObjects-1; i>=0; i-- ) {
StateObjectInstance *o = *( mObjects.getElement( i ) );
if( ! o->mLocked ) {
if( o->mAnchorPosition.x / P == inGridX
&&
o->mAnchorPosition.y / P == inGridY ) {
return i;
}
}
}
if( !mLocksOn ) {
// now consider locked, since no unlocked were hit
for( int i=numObjects-1; i>=0; i-- ) {
StateObjectInstance *o = *( mObjects.getElement( i ) );
if( o->mLocked ) {
if( o->mAnchorPosition.x / P == inGridX
&&
o->mAnchorPosition.y / P == inGridY ) {
return i;
}
}
}
}
return -1;
}
int GameState::getSelectedObject() {
return mSelectedObject;
}
void GameState::setSelectedObject( int inSelected ) {
mSelectedObject = inSelected;
}
// to right a bit, centered vertically
static intPair defaultAnchorOffset = { P/2 + P, P/2 };
void GameState::moveSelectedObject( int inGridX, int inGridY ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
intPair oldPos = o->mAnchorPosition;
intPair newPos = { inGridX * P, inGridY * P };
intPair deltaPos = subtract( newPos, oldPos );
// subtract this delta from action offset so that it does NOT move
// along with object (only if action has text in it AND anchor is not
// at starting pos [if at starting pos, it moves with object])
if( strlen( o->mAction ) > 0 &&
! equals( o->mActionOffset, defaultAnchorOffset ) ) {
o->mActionOffset = subtract( o->mActionOffset, deltaPos );
}
o->mAnchorPosition = newPos;
}
void GameState::moveSelectedSpeechAnchor( int inPixelX, int inPixelY ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
intPair absPos = { inPixelX, inPixelY };
o->mSpeechOffset = subtract( absPos, o->mAnchorPosition );
setSpeechHint( o->mObject.getUniqueID(), o->mSpeechOffset,
o->mSpeechFlip );
}
void GameState::moveSelectedActionAnchor( int inPixelX, int inPixelY ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
intPair absPos = { inPixelX, inPixelY };
o->mActionOffset = subtract( absPos, o->mAnchorPosition );
}
void GameState::resetActionAnchor( int inObjectIndex ) {
StateObjectInstance *o = *( mObjects.getElement( inObjectIndex ) );
// to right a bit, centered vertically
o->mActionOffset = defaultAnchorOffset;
}
char GameState::canAdd() {
return( mObjects.size() < 255 );
}
void GameState::newObject( int inGridX, int inGridY, StateObject inObject ) {
if( mObjects.size() >= 255 ) {
AppLog::error( "Error: adding another object to a full state!\n" );
}
else {
StateObjectInstance *o = new StateObjectInstance( inObject );
// centered
o->mAnchorPosition.x = P * inGridX;
o->mAnchorPosition.y = P * inGridY;
o->mSpeechOffset = getSpeechHint( inObject.getUniqueID(),
&( o->mSpeechFlip ) );
//o->mSpeechOffset.y = P/2;
// no sprites
mObjects.push_back( o );
mSelectedObject = mObjects.size() - 1;
resetActionAnchor( mSelectedObject );
}
}
void GameState::deleteSelectedObject() {
// never delete obj 0
if( mSelectedObject > 0 ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
delete o;
mObjects.deleteElement( mSelectedObject );
setSelectedObject( mSelectedObject - 1 );
if( mLocksOn ) {
// look for an unlocked object to make selected
while( getSelectedLocked() ) {
setSelectedObject( mSelectedObject - 1 );
}
}
}
}
void GameState::changeSelectedObject( StateObject inObject ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
o->mObject = inObject;
}
void GameState::adjustSelectedTrans( unsigned char inTrans ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
o->mObjectTrans = inTrans;
}
unsigned char GameState::getSelectedTrans() {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
return o->mObjectTrans;
}
void GameState::addCharToSelectedSpeech( char inChar ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
if( strlen( o->mSpokenMessage ) < 255 ) {
char *newString = autoSprintf( "%s%c", o->mSpokenMessage, inChar );
delete [] o->mSpokenMessage;
o->mSpokenMessage = newString;
o->mSpeechStale = false;
//printf( "Adding %d char\n", strlen( o->mSpokenMessage ) );
}
else {
AppLog::error( "Error: adding 256th character to speech\n" );
}
}
void GameState::deleteCharFromSelectedSpeech() {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
int oldLength = strlen( o->mSpokenMessage );
if( oldLength > 0 ) {
// terminate one char sooner
o->mSpokenMessage[ oldLength - 1 ] = '\0';
o->mSpeechStale = false;
}
}
int GameState::getSelectedSpeechLength() {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
return strlen( o->mSpokenMessage );
}
void GameState::deleteAllCharsFromSelectedSpeech() {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
delete [] o->mSpokenMessage;
o->mSpokenMessage = stringDuplicate( "" );
o->mSpeechStale = false;
}
void GameState::flipSelectedSpeech() {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
o->mSpeechFlip = ! o->mSpeechFlip;
setSpeechHint( o->mObject.getUniqueID(),
o->mSpeechOffset, o->mSpeechFlip );
}
void GameState::setSelectedSpeechBox( char inBox ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
o->mSpeechBox = inBox;
}
void GameState::setSelectedLocked( char inLocked ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
o->mLocked = inLocked;
}
char GameState::getSelectedLocked() {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
return o->mLocked;
}
void GameState::setObjectHeld( int inObject, char inHeld ) {
StateObjectInstance *o = *( mObjects.getElement( inObject ) );
o->mHeld = inHeld;
}
char GameState::getObjectHeld( int inObject ) {
StateObjectInstance *o = *( mObjects.getElement( inObject ) );
return o->mHeld;
}
int GameState::getAutoOffsetOnFlip( int inObject ) {
// heuristic:
// if offset is less that one gridspace away from object center,
// in direction of manual flip, reverse the offset on the other side
// when we auto-flip
StateObjectInstance *o = *( mObjects.getElement( inObject ) );
int offset = o->mSpeechOffset.x;
int autoFlipExtraOffset = 0;
if( !o->mSpeechFlip && offset >=P/2 && offset <= P + P/2 ) {
autoFlipExtraOffset = -2 * ( offset - P/2) + 1 - 2;
}
else if( o->mSpeechFlip && offset <=P/2 && offset >= P/2 - P ) {
autoFlipExtraOffset = -2 * ( offset - P/2) + 1 - 2;
}
return autoFlipExtraOffset;
}
void GameState::deleteAllNonPlayerSpeech() {
int numObjects = mObjects.size();
for( int i=1; i<numObjects; i++ ) {
StateObjectInstance *o = *( mObjects.getElement( i ) );
if( o->mSpeechStale ) {
delete [] o->mSpokenMessage;
o->mSpokenMessage = stringDuplicate( "" );
o->mSpeechStale = false;
}
}
}
void GameState::markNonPlayerSpeechStale() {
int numObjects = mObjects.size();
for( int i=1; i<numObjects; i++ ) {
StateObjectInstance *o = *( mObjects.getElement( i ) );
o->mSpeechStale = true;
}
}
void GameState::addCharToSelectedAction( char inChar ) {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
int oldLen = strlen( o->mAction );
if( oldLen < 10 ) {
o->mAction[ oldLen ] = inChar;
o->mAction[ oldLen + 1 ] = '\0';
}
else {
//printf( "Error: adding 11th character to action\n" );
}
}
void GameState::deleteCharFromSelectedAction() {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
int oldLength = strlen( o->mAction );
if( oldLength > 0 ) {
// terminate one char sooner
o->mAction[ oldLength - 1 ] = '\0';
}
}
int GameState::getSelectedActionLength() {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
return strlen( o->mAction );
}
void GameState::deleteAllCharsFromSelectedAction() {
StateObjectInstance *o = *( mObjects.getElement( mSelectedObject ) );
o->mAction[0] = '\0';
}
void GameState::freezeObjectZero( char inFrozen ) {
mObjectZeroFrozen = inFrozen;
}
char GameState::isObjectZeroFrozen() {
return mObjectZeroFrozen;
}
static intPair zeroPair = {0,0};
StateObjectInstance::StateObjectInstance( StateObject inObject )
: mObject( inObject ), mObjectTrans( 255 ),
mAnchorPosition( zeroPair ),
mSpeechOffset( zeroPair ),
mActionOffset( zeroPair ),
mSpeechFlip( 0 ),
mSpeechBox( 0 ),
mLocked( 0 ),
mHeld( 0 ),
mSpokenMessage( stringDuplicate( "" ) ),
mSpeechStale( false ) {
mAction[0] = '\0';
}
StateObjectInstance::~StateObjectInstance() {
delete [] mSpokenMessage;
}
StateObjectInstance *StateObjectInstance::copy() {
StateObjectInstance *o = new StateObjectInstance( mObject );
delete [] o->mSpokenMessage;
o->mObjectTrans = mObjectTrans;
o->mAnchorPosition = mAnchorPosition;
o->mSpeechOffset = mSpeechOffset;
o->mActionOffset = mActionOffset;
o->mSpeechFlip = mSpeechFlip;
o->mSpeechBox = mSpeechBox;
o->mLocked = mLocked;
o->mHeld = mHeld;
o->mSpokenMessage = stringDuplicate( mSpokenMessage );
o->mSpeechStale = mSpeechStale;
// init mAction first so that we don't leave uninitialized values in
// end of buffer
memset( (void *)( o->mAction ), '\0', 11 );
memcpy( o->mAction, mAction, strlen( mAction ) + 1 );
return o;
}
void StateObjectInstance::print() {
printf( "State object at " );
::print( mAnchorPosition );
printf( "\n" );
}
+202
View File
@@ -0,0 +1,202 @@
#ifndef GAME_STATE_INCLUDED
#define GAME_STATE_INCLUDED
#include "color.h"
#include "common.h"
#include "uniqueID.h"
#include "Room.h"
#include "StateObject.h"
#include "Song.h"
#include "musicPlayer.h"
#include "minorGems/util/SimpleVector.h"
class StateObjectInstance {
public:
StateObjectInstance( StateObject inObject );
~StateObjectInstance();
StateObjectInstance *copy();
StateObject mObject;
unsigned char mObjectTrans;
intPair mAnchorPosition;
intPair mSpeechOffset;
intPair mActionOffset;
char mSpeechFlip;
char mSpeechBox;
char mLocked;
char mHeld;
char *mSpokenMessage;
char mAction[11];
void print();
char mSpeechStale;
};
class GameState {
public:
// default empty state
GameState();
// load state from a message (destroyed by caller)
GameState( unsigned char *inMessage, int inLength );
~GameState();
GameState *copy();
Room mRoom;
unsigned char mRoomTrans;
SimpleVector<StateObjectInstance*> mObjects;
char mObjectZeroFrozen;
// music ID to be sent along with this state when it is encoded
// as a message
uniqueID mMusicID;
// scene ID to be sent along with this state when it is encoded
// as a message---last selected scene from picker, even if it
// doesn't match current scene state
uniqueID mSceneID;
char mLocksOn;
unsigned char *getStateAsMessage( int *outLength );
int getSelectedObject();
void setSelectedObject( int inSelected );
int getHitObject( int inGridX, int inGridY );
void moveSelectedObject( int inGridX, int inGridY );
void moveSelectedSpeechAnchor( int inPixelX, int inPixelY );
void moveSelectedActionAnchor( int inPixelX, int inPixelY );
void resetActionAnchor( int inObjectIndex );
// room for one more?
char canAdd();
// becomes selected
void newObject( int inGridX, int inGridY, StateObject inObject );
void deleteSelectedObject();
void changeSelectedObject( StateObject inObject );
void adjustSelectedTrans( unsigned char inTrans );
unsigned char getSelectedTrans();
void addCharToSelectedSpeech( char inChar );
void deleteCharFromSelectedSpeech();
int getSelectedSpeechLength();
void deleteAllCharsFromSelectedSpeech();
void flipSelectedSpeech();
void setSelectedSpeechBox( char inBox );
void setSelectedLocked( char inLocked );
char getSelectedLocked();
void setObjectHeld( int inObject, char inHeld );
char getObjectHeld( int inObject );
// amount x position of speech should be shifted on a flip,
// based on a heuristic
int getAutoOffsetOnFlip( int inObject );
// deletes speech from every object but 0
void deleteAllNonPlayerSpeech();
// sets as stale, for clear by deleteAllNonPlayerSpeech later
// however, if speech is edited after this, it becomes non-stale again,
// and won't be deleted by deleteAllNonPlayerSpeech
void markNonPlayerSpeechStale();
void addCharToSelectedAction( char inChar );
void deleteCharFromSelectedAction();
int getSelectedActionLength();
void deleteAllCharsFromSelectedAction();
void freezeObjectZero( char inFrozen );
char isObjectZeroFrozen();
// true if this received game state has a Song in it (v14)
// or false if it uses the old (v13) note grid
// So v14 and v13 can play together
char hasSong();
// where last received note toggles are stored
// (from states parsed from messages)
static char sNoteToggleBuffer[N][N];
// where last received live song is stored
Song mLiveSongReceived;
protected:
int mSelectedObject;
char mHasSong;
};
#endif
File diff suppressed because it is too large Load Diff
+194
View File
@@ -0,0 +1,194 @@
#ifndef GAME_STATE_DISPLAY_INCLUDED
#define GAME_STATE_DISPLAY_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIComponentGL.h"
#include "minorGems/ui/event/ActionListenerList.h"
#include "GameState.h"
#include "common.h"
class GameStateDisplay : public GUIComponentGL, public ActionListenerList {
public:
// sets its own width and height
GameStateDisplay( int inAnchorX, int inAnchorY,
char inShowObjectToolTips = false );
virtual ~GameStateDisplay();
// only for Controller game
// used to send redraw events back to editor
void setEditor( void *inEditor );
// destroyed by this class
void setState( GameState *inState );
// updates sprite positions from data in inState
// inState assumed to be indentical to last setState otherwise
// note that inState is still destroyed by this class!
void updateSpritePositions( GameState *inState );
// turns grid/anchor drawing on and off
void showGrid( char inShow );
// shows dark, transparent hints for object positions only
// doesn't draw rooms
void setHintMode( char inHintsOnly );
// this class auto-adjusts speech flips and offsets
// for display purposes
// this function allows the new ones to be copied into another
// game state
// inDestinationState must have the same number of objects, etc
void copySpeechCoordinates( GameState *inDestinationState );
// if true, then above function should be called
char mHasAnySpeechBeenAutoFlipped;
// set to true if currently in the middle of manual bubble positioning
// with mouse
// this is auto-set to false whenever mouse released
char mManualBubblePositioningLive;
char getSpeechOffTop( int inObject );
// override
virtual void fireRedraw();
virtual void mouseDragged( double inX, double inY );
virtual void mousePressed( double inX, double inY );
virtual void mouseReleased( double inX, double inY );
virtual void mouseMoved( double inX, double inY );
virtual void setFocus( char inFocus );
virtual char isFocused();
virtual void keyPressed( unsigned char inKey,
double inX, double inY );
// after this display fires an action, find out
// where the mouse event happened
int mLastPixelClickX, mLastPixelClickY;
int mLastGridClickX, mLastGridClickY;
char mLastActionRelease;
char mLastActionPress;
char mLastActionKeyPress;
unsigned char mLastKeyPressed;
int mLastMouseoverObjectIndex;
char mMouseHover;
int mLastHoverGridX, mLastHoverGridY;
char mEditingSpeech;
char mEditingAction;
char mHighlightEditedBubble;
protected:
char mShowObjectToolTips;
void setLastMouse( double inX, double inY );
void redoToolTip( int inGridX, int inGridY );
void *mEditor;
char mFocused;
char mShowGrid;
char mHintMode;
GameState *mState;
Sprite *mSprites[G][G];
SimpleVector<char> mSpeechOffTop;
SimpleVector<Sprite*> mObjectSprites;
SimpleVector<intPair> mObjectSpritePositions;
SimpleVector<int> mObjectSpriteOwners;
SimpleVector<double> mObjectSpriteTrans;
SimpleVector<char> mObjectSpriteGlows;
// depth from 0 to G (where G is above everything else)
// allows quick sorting into horizontal depth bands
SimpleVector<int> mObjectSpriteDepth;
// we draw locked ones behind others at same depth
SimpleVector<char> mObjectSpriteLocked;
// maps i=[0,numSprites) to indices in depth-draw order
SimpleVector<int> mDrawOrderMap;
void recomputeDrawOrderMap();
Sprite *mBubbleTopSprite;
Sprite *mBubbleMiddleSprite;
Sprite *mBubbleMiddleExtraSprite;
Sprite *mBubbleMiddleExtraThinSprite;
Sprite *mBubbleBottomSprite;
Sprite *mBubbleBottomSpriteFlip;
Sprite *mBubbleBottomNoTailSprite;
Sprite *mBubbleMiddleTailSprite;
Sprite *mBubbleMiddleTailFlipSprite;
Sprite *mSpeechBoxTopSprite;
Sprite *mSpeechBoxBottomSprite;
Sprite *mActionBoxStartSprite;
Sprite *mActionBoxMiddleSprite;
Sprite *mActionBoxMiddleExtraSprite;
Sprite *mActionBoxEndSprite;
Sprite *mActionBoxEndFlipSprite;
Sprite *mActionBoxStartTallSprite;
Sprite *mActionBoxMiddleTallSprite;
Sprite *mActionBoxMiddleExtraTallSprite;
Sprite *mActionBoxEndTallSprite;
Sprite *mActionBoxEndFlipTallSprite;
// for blinking selected anchor
int mLastSelected;
int mBlinkCycle;
private:
void addSpritesFromStateInstance( int inIndex );
// returns new sprite index
int updatePositionFromStateInstance( int inIndex, int inSpriteIndex );
void drawSpeech( int inIndex );
};
#endif
File diff suppressed because it is too large Load Diff
+157
View File
@@ -0,0 +1,157 @@
#ifndef GAME_STATE_EDITOR_INCLUDED
#define GAME_STATE_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "GameState.h"
#include "GameStateDisplay.h"
#include "StateToolSet.h"
#include "SizeLimitedVector.h"
#include "minorGems/graphics/openGL/gui/TextFieldGL.h"
#include "ToolTipSliderGL.h"
class GameStateEditor : public Editor {
public:
GameStateEditor( ScreenGL *inScreen );
~GameStateEditor();
virtual void actionPerformed( GUIComponent *inTarget );
GameState *mGameStateToEdit;
void setGameStateToEdit( GameState *inGameState,
char inUpdatePickers = true );
void enableSend( char inEnabled );
void aboutToSend();
// to receive redraw pings from display
void displayRedrawed();
protected:
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
GameStateDisplay *mStateDisplay;
char mEditingSelectedObject;
EditButtonGL *mEditStateObjectButton;
UndoButtonGL *mUndoButton;
RedoButtonGL *mRedoButton;
ClearButtonGL *mClearButton;
char mUndoOrRedoOrClearAction;
char mIgnoreSliders;
void clearRedoStack();
void saveUndoPoint();
StateToolSet *mToolSet;
SizeLimitedVector<GameState*> mUndoStack;
SimpleVector<GameState*> mRedoStack;
AddButtonGL *mAddSceneButton;
char mAddSceneAction;
QuickDeleteButtonGL *mSpeechDeleteButton;
ToggleSpriteButtonGL *mSpeechBubbleBoxButton;
FlipHButtonGL *mFlipSpeechButton;
// checks based on currently-selected object and how
// close it is to screen (also whether speech bubble is even visible)
// also checks cases for enabling speech delete button
void checkFlipAndDeleteSpeechButtonEnabled();
void setSelectedObject( int inIndex );
void getTileUnder();
SelectableButtonGL *mGridButton;
SelectableButtonGL *mFreezeButton;
SelectableButtonGL *mLockGlobalButton;
SelectableButtonGL *mLockSelectedButton;
SelectableButtonGL *mHoldObjectButton;
SpriteButtonGL *mSetObjectButton;
KeyEquivButtonGL *mAddObjectButton;
KeyEquivButtonGL *mEditObjectButton;
KeyEquivButtonGL *mRemoveObjectButton;
SpriteButtonGL *mEditRoomButton;
SendButtonGL *mSendButton;
SpriteButtonGL *mPracticeButton;
SpriteButtonGL *mEditMusicButton;
ToolTipSliderGL *mRoomTransSlider;
ToolTipSliderGL *mObjectTransSlider;
TextFieldGL *mNameField;
ToggleSpriteButtonGL *mAddToPackButton;
SpriteButtonGL *mSavePackButton;
Image *mNoDropImage;
Image *mCanDropImage;
char mIgnoreObjPickerEvents;
// don't move speech anchor while this is happening
char mObjectChanging;
// for usage tracking of objects in current state
uniqueID mCurrentWorkingStateID;
};
#endif
+38
View File
@@ -0,0 +1,38 @@
#include "GridOverlay.h"
#include <GL/gl.h>
GridOverlay::GridOverlay( double inAnchorX, double inAnchorY,
double inWidth, double inHeight,
int inNumCells, Color inColor )
: GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ),
mNumCells( inNumCells ),
mColor( inColor ) {
}
void GridOverlay::fireRedraw() {
double cellWidth = mWidth / mNumCells;
double cellHeight = mHeight / mNumCells;
glColor4f( mColor.r, mColor.g, mColor.b, mColor.a );
glBegin( GL_LINES ); {
for( int x=0; x<mNumCells+1; x++ ) {
glVertex2d( mAnchorX + x * cellWidth, mAnchorY );
glVertex2d( mAnchorX + x * cellWidth, mAnchorY + mHeight );
}
for( int y=0; y<mNumCells+1; y++ ) {
glVertex2d( mAnchorX, mAnchorY + y * cellHeight );
glVertex2d( mAnchorX + mWidth, mAnchorY + y * cellHeight );
}
}
glEnd();
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef GRID_OVERLAY_INCLUDED
#define GRID_OVERLAY_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIComponentGL.h"
#include "minorGems/graphics/Color.h"
static Color defaultGridColor( 1, 1, 1, 0.25f );
// transparent with white grid lines over component area.
// should be added last in root panel so that it is on top
class GridOverlay : public GUIComponentGL {
public:
GridOverlay( double inAnchorX, double inAnchorY,
double inWidth, double inHeight,
int inNumCells, Color inColor = defaultGridColor );
virtual void fireRedraw();
protected:
int mNumCells;
Color mColor;
};
#endif
+107
View File
@@ -0,0 +1,107 @@
#ifndef HIGHLIGHT_LABEL_GL_INCLUDED
#define HIGHLIGHT_LABEL_GL_INCLUDED
#include "minorGems/graphics/openGL/gui/LabelGL.h"
#include "minorGems/graphics/Color.h"
// a label with a highlight that can be turned on and off
class HighlightLabelGL : public LabelGL, public ActionListenerList {
public:
// same params as LabelGL
HighlightLabelGL( double inAnchorX, double inAnchorY, double inWidth,
double inHeight, const char *inString,
TextGL *inText )
: LabelGL( inAnchorX, inAnchorY, inWidth,
inHeight, inString, inText ),
mHighlightOn( false ), mHighlightColor( 1.0, 0.5, 0 ) {
}
void setHighlight( char inHighlightOn ) {
mHighlightOn = inHighlightOn;
}
char isHighlightOn() {
return mHighlightOn;
}
// override fireRedraw in LabelGL
virtual void fireRedraw();
virtual void mousePressed( double inX, double inY );
protected:
char mHighlightOn;
Color mHighlightColor;
};
inline void HighlightLabelGL::fireRedraw() {
if( ! mEnabled ) {
// invisible if disabled
return;
}
Color *oldColor = NULL;
if( mHighlightOn ) {
oldColor = mText->getFontColor()->copy();
mText->setFontColor( mHighlightColor.copy() );
// lighten background to further highlight label
glColor4f( 1, 1, 1, 0.25 );
double drawWidth = mText->measureTextWidth( mString ) * mHeight;
glColor4f( mHighlightColor.r, mHighlightColor.g, mHighlightColor.b,
0.125 );
glBegin( GL_QUADS ); {
glVertex2d( mAnchorX - 1, mAnchorY - 1 );
glVertex2d( mAnchorX + drawWidth + 1, mAnchorY - 1 );
glVertex2d( mAnchorX + drawWidth + 1, mAnchorY + mHeight + 1 );
glVertex2d( mAnchorX - 1, mAnchorY + mHeight + 1 );
}
glEnd();
}
mText->drawText( mString, mAnchorX, mAnchorY,
mWidth, mHeight );
if( mHighlightOn ) {
mText->setFontColor( oldColor );
}
}
inline void HighlightLabelGL::mousePressed( double inX, double inY ) {
if( isEnabled() ) {
// fire an event
fireActionPerformed( this );
}
}
#endif
+146
View File
@@ -0,0 +1,146 @@
#include "HueValuePicker.h"
#include "common.h"
HueValuePicker::HueValuePicker( double inAnchorX, double inAnchorY,
double inWidth, double inHeight )
: BorderPanel( inAnchorX - 2, inAnchorY - 2,
inWidth + 4, inHeight + 4,
new Color( 0, 0, 0, 1 ),
new Color( 0.35, 0.35, 0.35, 1 ),
1 ),
mPressed( false ) {
mH = 0;
mV = 1;
double innerW = mWidth -2;
double innerH = mHeight -2;
mFieldImage = new Image( (int)innerW, (int)innerH, 3, false );
double *channels[3] =
{ mFieldImage->getChannel( 0 ),
mFieldImage->getChannel( 1 ),
mFieldImage->getChannel( 2 ) };
for( int y=0; y<innerH; y++ ) {
float V = (innerH - y - 1) / innerH;
for( int x=0; x<innerW; x++ ) {
int index = y * (int)innerW + x;
float H = x / innerW;
Color *c = Color::makeColorFromHSV( H, 1, V );
channels[0][index] = c->r;
channels[1][index] = c->g;
channels[2][index] = c->b;
delete c;
}
}
//writeTGA( mFieldImage, "testHV.tga" );
mFieldSprite = new Sprite( mFieldImage );
//mFieldSprite = new Sprite( "../testHV.tga" );
mColorSpot = new Sprite( "colorSpot.tga", true );
}
HueValuePicker::~HueValuePicker() {
delete mFieldImage;
delete mFieldSprite;
delete mColorSpot;
}
float HueValuePicker::getSelectedHue() {
return mH;
}
float HueValuePicker::getSelectedValue() {
return mV;
}
void HueValuePicker::setValues( float inHue, float inValue ) {
mH = inHue;
mV = inValue;
}
void HueValuePicker::mouseActivity( double inX, double inY ) {
if( isInside( inX, inY ) && mPressed ) {
inY --;
double innerW = mWidth -3;
double innerH = mHeight -3;
if( inX < mAnchorX + 1 ) {
inX = mAnchorX + 1;
}
if( inY < mAnchorY + 1 ) {
inY = mAnchorY + 1;
}
if( inX > mAnchorX + 1 + innerW ) {
inX = mAnchorX + 1 + innerW;
}
if( inY > mAnchorY + 1 + innerH ) {
inY = mAnchorY + 1 + innerH;
}
mH = (float)(inX - (mAnchorX + 1)) / innerW;
mV = (float)(inY - (mAnchorY + 1)) / innerH;
fireActionPerformed( this );
}
}
void HueValuePicker::mousePressed( double inX, double inY ) {
mPressed = true;
mouseActivity( inX, inY );
}
void HueValuePicker::mouseDragged( double inX, double inY ) {
mouseActivity( inX, inY );
}
void HueValuePicker::mouseReleased( double inX, double inY ) {
mouseActivity( inX, inY );
mPressed = false;
}
void HueValuePicker::fireRedraw() {
BorderPanel::fireRedraw();
Vector3D pos( mAnchorX + mWidth / 2, mAnchorY + mHeight / 2, 0 );
mFieldSprite->draw( 0, 0, &pos );
double innerW = mWidth -3;
double innerH = mHeight -3;
Vector3D spotPos( mAnchorX + 1 + innerW * mH,
mAnchorY + 2 + innerH * mV, 0 );
mColorSpot->draw( 0, 0, &spotPos );
}
+73
View File
@@ -0,0 +1,73 @@
#ifndef HUE_VALUE_PICKER_INCLUDED
#define HUE_VALUE_PICKER_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIComponentGL.h"
#include "minorGems/ui/event/ActionListenerList.h"
#include "minorGems/graphics/Image.h"
#include "color.h"
#include "BorderPanel.h"
#include "Sprite.h"
class HueValuePicker : public BorderPanel, public ActionListenerList {
public:
// w and h MUST be powers of two!
HueValuePicker( double inAnchorX, double inAnchorY,
double inWidth, double inHeight );
virtual ~HueValuePicker();
// 0-1
float getSelectedHue();
float getSelectedValue();
void setValues( float inHue, float inValue );
// override fireRedraw in GUIComponentGL
virtual void mousePressed( double inX, double inY );
virtual void mouseDragged( double inX, double inY );
virtual void mouseReleased( double inX, double inY );
virtual void fireRedraw();
// true if currently active from an on-picker press
char mPressed;
protected:
void mouseActivity( double inX, double inY );
Image *mFieldImage;
Sprite *mFieldSprite;
Sprite *mColorSpot;
float mH;
float mV;
};
#endif
+104
View File
@@ -0,0 +1,104 @@
#
# Modification History
#
# 2006-June-27 Jason Rohrer
# Created. Copied from Transcend.
#
##
# The portion of SleepIsDeath Makefiles common to all platforms.
#
# Should not be made manually---
# used by SleepIsDeath/configure to build Makefiles.
##
ROOT_PATH = ../..
include makeFileList
LAYER_OBJECTS = ${LAYER_SOURCE:.cpp=.o}
TEST_SOURCE =
TEST_OBJECTS = ${TEST_SOURCE:.cpp=.o}
# separate dependency files
LAYER_DEPENDS = ${LAYER_SOURCE:.cpp=.dep}
# targets
all: SleepIsDeath ${GAME_GRAPHICS}
clean:
rm -f ${LAYER_DEPENDS} ${LAYER_OBJECTS} ${TEST_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} SleepIsDeath ${GAME_GRAPHICS}
graphics: ${GAME_GRAPHICS}
SleepIsDeath: ${LAYER_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} ${ICON_FILE}
${EXE_LINK} -o SleepIsDeath ${LAYER_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} ${ICON_FILE} ${COMMON_LIBS} ${PLATFORM_LINK_FLAGS}
# layer objects w/out game.o
LAYER_OBJECTS_NO_MAIN = ${LAYER_OBJECTS:game.o=gameNoMain.o}
testUsage: ${LAYER_OBJECTS_NO_MAIN} ${NEEDED_MINOR_GEMS_OBJECTS} testUsage.o
${EXE_LINK} -o testUsage testUsage.o ${LAYER_OBJECTS_NO_MAIN} ${NEEDED_MINOR_GEMS_OBJECTS} ${COMMON_LIBS} ${PLATFORM_LINK_FLAGS}
# add this on Unix to support JPEG video frame output
# -ljpeg ${ROOT_PATH}/minorGems/graphics/converters/unix/JPEGImageConverterUnix.cpp
#
# Generic:
#
# Map all .cpp files into .dep files
#
# $@ represents the name.dep file
# $< represents the name.cpp file
#
%.dep: %.cpp
${COMPILE} -MM $< >> $@
# include them all
include ${LAYER_DEPENDS}
#
# Generic:
#
# Map all png files into .tga files
#
# $@ represents the name.tga file
# $< represents the name.png file
#
graphics/%.tga: %.png
convert $< $@
music/%.tga: %.png
convert $< $@
+95
View File
@@ -0,0 +1,95 @@
#include "MoveToolSet.h"
MoveToolSet::MoveToolSet( double inAnchorX, double inAnchorY )
: GUIPanelGL( inAnchorX, inAnchorY, 60, 20,
new Color( 0, 0, 0, 0 ) ) {
mActButton = new SelectableButtonGL(
new Sprite( "act.tga", true ),
1,
inAnchorX, inAnchorY, 20, 20 );
mMoveButton = new SelectableButtonGL(
new Sprite( "move.tga", true ),
1,
inAnchorX + 20, inAnchorY, 20, 20 );
mSpeakButton = new SelectableButtonGL(
new Sprite( "speak.tga", true ),
1,
inAnchorX + 40, inAnchorY, 20, 20 );
mActButton->setToolTip( "tip_playerAct" );
mMoveButton->setToolTip( "tip_playerMove" );
mSpeakButton->setToolTip( "tip_playerSpeak" );
add( mActButton );
add( mMoveButton );
add( mSpeakButton );
mActButton->addActionListener( this );
mMoveButton->addActionListener( this );
mSpeakButton->addActionListener( this );
mMoveButton->setSelected( true );
}
void MoveToolSet::setMoveAllowed( char inAllowed ) {
if( !inAllowed && getSelected() == playerMove ) {
setSelected( playerSpeak );
//setSelected( playerAct );
// change
fireActionPerformed( this );
}
mMoveButton->setEnabled( inAllowed );
}
moveTool MoveToolSet::getSelected() {
if( mActButton->getSelected() ) {
return playerAct;
}
if( mMoveButton->getSelected() ) {
return playerMove;
}
if( mSpeakButton->getSelected() ) {
return playerSpeak;
}
return playerMove;
}
void MoveToolSet::setSelected( moveTool inTool ) {
mActButton->setSelected( inTool == playerAct );
mMoveButton->setSelected( inTool == playerMove );
mSpeakButton->setSelected( inTool == playerSpeak );
}
void MoveToolSet::actionPerformed( GUIComponent *inTarget ) {
// select hit, turn others off
mActButton->setSelected( inTarget == mActButton );
mMoveButton->setSelected( inTarget == mMoveButton );
mSpeakButton->setSelected( inTarget == mSpeakButton );
fireActionPerformed( this );
}
void MoveToolSet::setEnabled( char inEnabled ) {
GUIPanelGL::setEnabled( inEnabled );
mActButton->setEnabled( inEnabled );
mMoveButton->setEnabled( inEnabled );
mSpeakButton->setEnabled( inEnabled );
}
+48
View File
@@ -0,0 +1,48 @@
#ifndef MOVE_TOOL_SET_INCLUDED
#define MOVE_TOOL_SET_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIPanelGL.h"
#include "minorGems/ui/event/ActionListener.h"
#include "minorGems/ui/event/ActionListenerList.h"
#include "buttons.h"
enum moveTool { playerMove, playerSpeak, playerAct };
class MoveToolSet : public GUIPanelGL, public ActionListener,
public ActionListenerList {
public:
// sets its width/height automatically
MoveToolSet( double inAnchorX, double inAnchorY );
moveTool getSelected();
void setSelected( moveTool inTool );
void setMoveAllowed( char inAllowed );
// implements ActionListener
virtual void actionPerformed( GUIComponent *inTarget );
virtual void setEnabled( char inEnabled );
protected:
SelectableButtonGL *mActButton;
SelectableButtonGL *mMoveButton;
SelectableButtonGL *mSpeakButton;
};
#endif
+349
View File
@@ -0,0 +1,349 @@
#include "Music.h"
#include "resourceManager.h"
#include "imageCache.h"
#include "packSaver.h"
#include "minorGems/util/log/AppLog.h"
// for grid display, color subsequent octaves
Color lowColor( 0, 146.0/255, 219.0/255 );
Color medColor( 146.0/255, 219.0/255, 109/255 );
Color hiColor( 219.0/255, 146.0/255, 0 );
// for sprites
// from Scale.cpp
extern Color toneLowColor;
extern Color toneHighColor;
// static inits
Music *Music::sBlankMusic = NULL;
void Music::staticInit() {
sBlankMusic = new Music();
// ensure that blank music is saved at least once
sBlankMusic->finishEdit();
}
void Music::staticFree() {
delete sBlankMusic;
}
void Music::setupDefault() {
memset( (void *)mNotes, 0, mNoteDataLength );
const char *defaultName = "default";
memcpy( mName, defaultName, strlen( defaultName ) + 1 );
makeUniqueID();
}
Music::Music() {
setupDefault();
}
char Music::initFromData( unsigned char *inData, int inLength ) {
if( inLength >= mNoteDataLength ) {
memcpy( (void *)mNotes, inData, mNoteDataLength );
inLength -= mNoteDataLength;
// remainder is name
if( inLength <= 11 ) {
memcpy( mName, &inData[mNoteDataLength], inLength );
return true;
}
}
return false;
}
Music::Music( uniqueID inID, unsigned char *inData, int inLength )
: mID( inID ) {
if( !initFromData( inData, inLength ) ) {
// fail
setupDefault();
}
}
Music::Music( uniqueID inID )
: mID( inID ) {
int length;
char fromNetwork;
unsigned char *data = loadResourceData( "music", mID, &length,
&fromNetwork );
if( data != NULL ) {
if( ! initFromData( data, length ) ) {
// failure
setupDefault();
}
delete [] data;
data = NULL;
if( fromNetwork ) {
// save to disk using the ID that we fetched it with
finishEdit( false );
}
}
else {
// music failed to load
setupDefault();
}
if( data != NULL ) {
delete [] data;
}
}
void Music::editMusic( int inX, int inY, char inNoteOn ) {
mNotes[inY][inX] = inNoteOn;
}
char Music::getNoteOn( int inX, int inY ) {
return mNotes[inY][inX];
}
void Music::editMusicName( const char *inName ) {
int newLength = strlen( inName );
if( newLength > 10 ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: Music set name %s is too long (10 max)\n", inName );
}
else {
memcpy( mName, inName, newLength + 1 );
}
}
#include "minorGems/util/stringUtils.h"
char *Music::getMusicName() {
return stringDuplicate( mName );
}
#include "minorGems/util/SimpleVector.h"
unsigned char *Music::makeBytes( int *outLength ) {
SimpleVector<unsigned char> dataAccum;
dataAccum.push_back( (unsigned char *)mNotes,
mNoteDataLength );
dataAccum.push_back( (unsigned char *)mName,
strlen( mName ) + 1 );
*outLength = dataAccum.size();
return dataAccum.getElementArray();
}
void Music::finishEdit( char inGenerateNewID ) {
uniqueID oldID = mID;
if( inGenerateNewID ) {
makeUniqueID();
}
if( ! equal( oldID, mID ) ||
! resourceExists( "music", mID ) ) {
// change
int dataSize;
unsigned char *data = makeBytes( &dataSize );
saveResourceData( "music", mID,
mName,
data, dataSize );
delete [] data;
}
}
void Music::saveToPack() {
int dataSize;
unsigned char *data = makeBytes( &dataSize );
addToPack( "music", mID,
mName,
data, dataSize );
delete [] data;
}
uniqueID Music::getUniqueID() {
return mID;
}
const char *Music::getResourceType() {
return "music";
}
Music Music::getDefaultResource() {
return *( Music::sBlankMusic );
}
void Music::makeUniqueID() {
partialUniqueID p = startUniqueID();
p = addToUniqueID( p,
(unsigned char*)mNotes, mNoteDataLength );
p = addToUniqueID( p, (unsigned char*)mName, strlen( mName ) + 1 );
mID = ::makeUniqueID( p );
}
Image *Music::getImage() {
Image *image = new Image( N, N, 4, true );
double *channels[4];
int i;
for( i=0; i<4; i++ ) {
channels[i] = image->getChannel( i );
}
Color *drawColor = &lowColor;
//Color *drawColorA = &lowColor;//&toneLowColor;
//Color *drawColorB = &medColor;//&toneHighColor;
Color drawColorA( 0.50, 0.50, 0.50 );
Color drawColorB( 1.0, 1.0, 1.0 );
for( int y=0; y<N; y++ ) {
if( y>4 ) {
drawColor = &medColor;
}
if( y>9 ) {
drawColor = &hiColor;
}
Color *levelColor = Color::linearSum( &drawColorB, &drawColorA,
y / (float)N );
int pixY = N - y - 1;
for( int x=0; x<N; x++ ) {
int pixel = pixY * N + x;
if( mNotes[y][x] ) {
for( i=0; i<3; i++ ) {
channels[i][pixel] = (*levelColor)[i];
}
channels[3][pixel] = 1.0;
}
}
delete levelColor;
}
return image;
}
Sprite *Music::getSprite( char inUseTrans, char inCacheOK ) {
// ignore inUseTrans
if( inCacheOK ) {
Image *cachedImage = getCachedImage( mID, false );
if( cachedImage == NULL ) {
cachedImage = getImage();
addCachedImage( mID, false, cachedImage );
}
return new Sprite( cachedImage );
}
else {
// ignore cache
Image *image = getImage();
Sprite *sprite = new Sprite( image );
delete image;
return sprite;
}
}
void Music::print() {
char *idString = getHumanReadableString( mID );
printf( "Music %s:\n", idString );
delete [] idString;
printf( " set name: %s\n", mName );
for( int y=0; y<N; y++ ) {
printf( " " );
for( int x=0; x<N; x++ ) {
printf( "%c ", mNotes[y][x] );
}
printf( "\n" );
}
}
+104
View File
@@ -0,0 +1,104 @@
#ifndef MUSIC_INCLUDED
#define MUSIC_INCLUDED
#include "color.h"
#include "common.h"
#include "uniqueID.h"
#include "Sprite.h"
#include "musicPlayer.h"
class Music {
public:
// blank music
Music();
// music loaded from resource manager
Music( uniqueID inID );
// music loaded from data string
Music( uniqueID inID, unsigned char *inData, int inLength );
// changes the on/off status of a music note
void editMusic( int inX, int inY, char inNoteOn );
char getNoteOn( int inX, int inY );
// name has at most 10 chars
void editMusicName( const char *inName );
// destroyed by caller
char *getMusicName();
// finishes the edit, generates a new unique ID, saves result
void finishEdit( char inGenerateNewID=true );
// recursively saves to current resource pack
void saveToPack();
// get an image of this music
Image *getImage();
// implements ResourceType functions as needed by ResourcePicker
uniqueID getUniqueID();
Sprite *getSprite( char inUseTrans=false, char inCacheOK=true );
static const char *getResourceType();
static Music getDefaultResource();
char *getName() {
return getMusicName();
}
void print();
// blank sprite
static Music *sBlankMusic;
static void staticInit();
static void staticFree();
protected:
void setupDefault();
char initFromData( unsigned char *inData, int inLength );
// result destroyed by caller
unsigned char *makeBytes( int *outLength );
void makeUniqueID();
// notes on or off, indexed as [y][x]
char mNotes[N][N];
const static int mNoteDataLength = N * N;
char mName[11];
uniqueID mID;
};
#endif
+381
View File
@@ -0,0 +1,381 @@
#include "MusicEditor.h"
#include "MusicPicker.h"
#include "Song.h"
#include "BorderPanel.h"
#include "labels.h"
#include "SelectionManager.h"
#include "musicPlayer.h"
#include "minorGems/util/log/AppLog.h"
// from musicPlayer.cpp
extern char noteToggles[PARTS][S][N][N];
extern int partLengths[PARTS];
extern MusicPicker *mainMusicPicker;
extern int gameWidth, gameHeight;
extern TextGL *largeTextFixed;
template <>
void SizeLimitedVector<Music>::deleteElementOfType(
Music inElement ) {
// no delete necessary
}
MusicEditor::MusicEditor( ScreenGL *inScreen )
: Editor( inScreen ),
mUndoStack( MAX_UNDOS, false ) {
mCloseButton->setToolTip( "tip_closeEdit_music" );
mSidePanel->add( mainMusicPicker );
mainMusicPicker->addActionListener( this );
mNoteDisplay = new NoteGridDisplay( 8, gameWidth - 48 - N * W );
mMainPanel->add( mNoteDisplay );
mNoteDisplay->addActionListener( this );
EightPixelLabel *fieldLabel = new EightPixelLabel( 150, 54,
"musicName" );
mMainPanel->add( fieldLabel );
int fieldHeight = 8;
int fieldWidth = 8 * 10;
const char *defaultText = "default";
mNameField = new TextFieldGL( 150,
43,
fieldWidth,
fieldHeight,
1,
defaultText,
largeTextFixed,
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.15, 0.15, 0.15 ),
new Color( 0.75, 0.75, 0.75 ),
10,
false );
mMainPanel->add( mNameField );
mNameField->setFocus( true );
//mNameField->lockFocus( true );
mNameField->setCursorPosition( strlen( defaultText ) );
mNameField->addActionListener( this );
// center add button
double gridEdge = 8 + N * W;
double extra = gameHeight - gridEdge;
// center it vertically on music picker
double addY = mainMusicPicker->getAnchorY() +
mainMusicPicker->getHeight() - 15;
double sideButtonsX = gridEdge + (extra - 16) / 2;
mAddButton = new AddButtonGL( sideButtonsX,
addY,
16, 16 );
mMainPanel->add( mAddButton );
mAddButton->addActionListener( this );
mAddButton->setToolTip( "tip_addMusic" );
mAddAction = false;
double miniButtonSize = P + 4;
mMiniViewButton = new SpriteButtonGL(
NULL, 1,
gridEdge + ( extra - miniButtonSize ) / 2,
addY - 24,
miniButtonSize,
miniButtonSize );
mMainPanel->add( mMiniViewButton );
mTransformToolSet = new TransformToolSet( sideButtonsX,
mMiniViewButton->getAnchorY()
- 4 - 100 ) ;
mMainPanel->add( mTransformToolSet );
mTransformToolSet->addActionListener( this );
double undoButtonY = gameWidth - ( 48 + N * W );
mUndoButton = new UndoButtonGL( sideButtonsX, undoButtonY, 16, 16 );
mMainPanel->add( mUndoButton );
mUndoButton->addActionListener( this );
mUndoButton->setEnabled( false );
mRedoButton = new RedoButtonGL( sideButtonsX, undoButtonY + 19, 16, 16 );
mMainPanel->add( mRedoButton );
mRedoButton->addActionListener( this );
mRedoButton->setEnabled( false );
setMusicToEdit( mainMusicPicker->getSelectedResource() );
postConstruction();
}
MusicEditor::~MusicEditor() {
mSidePanel->remove( mainMusicPicker );
}
void MusicEditor::setMusicToEdit( Music inMusic ) {
mMusicToEdit = inMusic;
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
// last instrument for part editing
noteToggles[SI][0][y][x] = mMusicToEdit.getNoteOn( x, y );
}
}
refreshMiniView();
char *name = mMusicToEdit.getMusicName();
mNameField->setText( name );
mNameField->setCursorPosition( strlen( name ) );
delete [] name;
}
void MusicEditor::refreshMiniView() {
// don't use cached version
mMiniViewButton->setSprite( mMusicToEdit.getSprite( false, false ) );
}
void MusicEditor::actionPerformed( GUIComponent *inTarget ) {
// superclass
Editor::actionPerformed( inTarget );
if( inTarget == mainMusicPicker ) {
if( ! mAddAction &&
! mainMusicPicker->wasLastActionFromPress() ) {
// will change music
mUndoStack.push_back( mMusicToEdit );
mUndoButton->setEnabled( true );
setMusicToEdit( mainMusicPicker->getSelectedResource() );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
}
}
else if( inTarget == mNameField ) {
mUndoStack.push_back( mMusicToEdit );
mUndoButton->setEnabled( true );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
mMusicToEdit.editMusicName( mNameField->getText() );
}
else if( inTarget == mAddButton ) {
addMusic();
}
else if( inTarget == mUndoButton ) {
int lastIndex = mUndoStack.size() - 1;
Music last = *( mUndoStack.getElement( lastIndex ) );
mUndoStack.deleteElement( lastIndex );
if( mUndoStack.size() == 0 ) {
mUndoButton->setEnabled( false );
}
mRedoStack.push_back( mMusicToEdit );
mRedoButton->setEnabled( true );
setMusicToEdit( last );
}
else if( inTarget == mRedoButton ) {
int nextIndex = mRedoStack.size() - 1;
Music next = *( mRedoStack.getElement( nextIndex ) );
mRedoStack.deleteElement( nextIndex );
if( mRedoStack.size() == 0 ) {
mRedoButton->setEnabled( false );
}
mUndoStack.push_back( mMusicToEdit );
mUndoButton->setEnabled( true );
setMusicToEdit( next );
}
else if( inTarget == mTransformToolSet ) {
mUndoStack.push_back( mMusicToEdit );
mUndoButton->setEnabled( true );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
char oldToggles[N][N];
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
oldToggles[y][x] = noteToggles[SI][0][y][x];
}
}
switch( mTransformToolSet->getLastPressed() ) {
case flipH: {
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
noteToggles[SI][0][y][x] = oldToggles[y][N - x - 1];
}
}
}
break;
case flipV: {
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
noteToggles[SI][0][y][x] = oldToggles[N - y - 1][x];
}
}
}
break;
case rotateCCW: {
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
noteToggles[SI][0][y][x] = oldToggles[N - x - 1][y];
}
}
}
break;
case rotateCW: {
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
noteToggles[SI][0][y][x] = oldToggles[x][N - y - 1];
}
}
}
break;
case clear: {
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
noteToggles[SI][0][y][x] = false;
}
}
}
break;
case colorize: {
// does not apply to music
}
break;
}
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
mMusicToEdit.editMusic( x, y, noteToggles[SI][0][y][x] );
}
}
refreshMiniView();
}
else if( inTarget == mNoteDisplay ) {
if( mNoteDisplay->mLastActionPress ) {
// note grid has changed,
// but our music object hasn't been touched yet, so we can
// still save it as an undo point
mUndoStack.push_back( mMusicToEdit );
mUndoButton->setEnabled( true );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
}
// copy changes into our object
for( int y=0; y<N; y++ ) {
for( int x=0; x<N; x++ ) {
mMusicToEdit.editMusic( x, y, noteToggles[SI][0][y][x] );
}
}
refreshMiniView();
}
}
void MusicEditor::editorClosing() {
addMusic();
// redraws will be off while we are closed, glow gets out of sync
mNoteDisplay->resetColumnGlow();
// silence our extra part (only plays when editor open
partLengths[SI] = 0;
}
void MusicEditor::addMusic() {
mAddAction = true;
mMusicToEdit.finishEdit();
mainMusicPicker->setSelectedResource( mMusicToEdit, true );
mAddAction = false;
}
+70
View File
@@ -0,0 +1,70 @@
#ifndef MUSIC_EDITOR_INCLUDED
#define MUSIC_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "Music.h"
#include "TransformToolSet.h"
#include "SizeLimitedVector.h"
#include "NoteGridDisplay.h"
#include "minorGems/graphics/openGL/gui/TextFieldGL.h"
class MusicEditor : public Editor {
public:
MusicEditor( ScreenGL *inScreen );
~MusicEditor();
virtual void actionPerformed( GUIComponent *inTarget );
protected:
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
// triggered by add button or close
void addMusic();
NoteGridDisplay *mNoteDisplay;
AddButtonGL *mAddButton;
char mAddAction;
void setMusicToEdit( Music inMusic );
void refreshMiniView();
SpriteButtonGL *mMiniViewButton;
Music mMusicToEdit;
UndoButtonGL *mUndoButton;
RedoButtonGL *mRedoButton;
TransformToolSet *mTransformToolSet;
SizeLimitedVector<Music> mUndoStack;
SimpleVector<Music> mRedoStack;
TextFieldGL *mNameField;
};
#endif
+17
View File
@@ -0,0 +1,17 @@
#include "MusicPicker.h"
// all share one stack
SimpleVector<uniqueID> globalMusicStack;
MusicPicker::MusicPicker(
double inAnchorX, double inAnchorY )
: ResourcePicker<Music>( inAnchorX, inAnchorY, &globalMusicStack ) {
}
MusicPicker::~MusicPicker() {
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef MUSIC_PICKER_INCLUDED
#define MUSIC_PICKER_INCLUDED
#include "Music.h"
#include "ResourcePicker.h"
// must include this to avoid linker errors
#include "ResourcePicker.cpp"
class MusicPicker : public ResourcePicker<Music> {
public:
/**
* Constructs a picker.
*
* @param inAnchorX the x position of the upper left corner
* of this component.
* @param inAnchorY the y position of the upper left corner
* of this component.
*
* Sets its own width and height automatically.
*/
MusicPicker( double inAnchorX, double inAnchorY );
virtual ~MusicPicker();
};
#endif
+172
View File
@@ -0,0 +1,172 @@
#include "NoteGridDisplay.h"
#include "Song.h"
#include "musicPlayer.h"
#include "ScalePicker.h"
// from musicPlayer.cpp
extern char noteToggles[PARTS][S][N][N];
extern int lastNoteColumnPlayed;
extern ScalePicker *mainScalePicker;
NoteGridDisplay::NoteGridDisplay( int inAnchorX, int inAnchorY )
: GUIComponentGL( inAnchorX, inAnchorY, N * W, N * W ),
mLastActionRelease( false ), mLastActionPress( false ) {
Scale s = mainScalePicker->getSelectedResource();
mNotesInOctave = s.getNumOn();
mNoteSprite = new Sprite( "note.tga", true );
mNoteSpaceSprite = new Sprite( "noteSpace.tga", true );
for( int i=0; i<N; i++ ) {
mColumnGlow[i] = 0;
}
mainScalePicker->addActionListener( this );
}
NoteGridDisplay::~NoteGridDisplay() {
delete mNoteSprite;
delete mNoteSpaceSprite;
}
void NoteGridDisplay::resetColumnGlow() {
for( int x=0; x<N; x++ ) {
mColumnGlow[x] = 0;
}
}
extern Color lowColor;
extern Color medColor;
extern Color hiColor;
static Color *drawColors[3] = { &lowColor, &medColor, &hiColor };
void NoteGridDisplay::fireRedraw() {
Color *drawColor = &lowColor;
for( int y=0; y<N; y++ ) {
// new color band per octave, wrap around
drawColor = drawColors[ ( y / mNotesInOctave ) % 3 ];
for( int x=0; x<N; x++ ) {
Vector3D pos( mAnchorX + W * x + W/2,
mAnchorY + W * y + W/2,
0 );
double fadeFactor = 0.5;
if( x == lastNoteColumnPlayed ) {
if( mColumnGlow[x] == 0 ) {
// fresh glow here
mColumnGlow[x] = 1;
}
}
if( mColumnGlow[x] > 0 ) {
mColumnGlow[x] -= 0.0015;
if( mColumnGlow[x] < 0 ) {
mColumnGlow[x] = 0;
}
}
fadeFactor = 0.4 + 0.6 * mColumnGlow[x];
mNoteSpaceSprite->draw( 0, 0, &pos, 1, fadeFactor * 0.5,
drawColor );
// last instrument reserved for phrase display
if( noteToggles[SI][0][y][x] ) {
mNoteSprite->draw( 0, 0, &pos, 1, fadeFactor * 1.0,
drawColor );
}
}
}
}
void NoteGridDisplay::mouseDragged( double inX, double inY ) {
mLastActionRelease = false;
mLastActionPress = false;
if( isEnabled() && isInside( inX, inY ) ) {
int x = (int)( (inX - mAnchorX) / W );
int y = (int)( (inY - mAnchorY) / W );
noteToggles[SI][0][y][x] = mPressedInk;
fireActionPerformed( this );
}
}
void NoteGridDisplay::mousePressed( double inX, double inY ) {
mLastActionRelease = false;
mLastActionPress = true;
if( isEnabled() && isInside( inX, inY ) ) {
int x = (int)( (inX - mAnchorX) / W );
int y = (int)( (inY - mAnchorY) / W );
noteToggles[SI][0][y][x] = ! noteToggles[SI][0][y][x];
mPressedInk = noteToggles[SI][0][y][x];
fireActionPerformed( this );
}
}
void NoteGridDisplay::mouseReleased( double inX, double inY ) {
mLastActionRelease = true;
mLastActionPress = false;
if( isEnabled() && isInside( inX, inY ) ) {
int x = (int)( (inX - mAnchorX) / W );
int y = (int)( (inY - mAnchorY) / W );
noteToggles[SI][0][y][x] = mPressedInk;
fireActionPerformed( this );
}
}
void NoteGridDisplay::actionPerformed( GUIComponent *inTarget ) {
if( inTarget == mainScalePicker ) {
Scale s = mainScalePicker->getSelectedResource();
mNotesInOctave = s.getNumOn();
}
}
+62
View File
@@ -0,0 +1,62 @@
#ifndef NOTE_GRID_DISPLAY_INCLUDED
#define NOTE_GRID_DISPLAY_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIComponentGL.h"
#include "minorGems/ui/event/ActionListenerList.h"
#include "minorGems/ui/event/ActionListener.h"
#include "Sprite.h"
#include "musicPlayer.h"
// note width
#define W 12
class NoteGridDisplay : public GUIComponentGL, public ActionListenerList,
public ActionListener {
public:
// sets its own width and height
NoteGridDisplay( int inAnchorX, int inAnchorY );
virtual ~NoteGridDisplay();
void resetColumnGlow();
char mLastActionRelease;
char mLastActionPress;
// override
virtual void fireRedraw();
virtual void mouseDragged( double inX, double inY );
virtual void mousePressed( double inX, double inY );
virtual void mouseReleased( double inX, double inY );
virtual void actionPerformed( GUIComponent *inTarget );
protected:
int mNotesInOctave;
char mPressedInk;
Sprite *mNoteSprite;
Sprite *mNoteSpaceSprite;
double mColumnGlow[N];
};
#endif
+318
View File
@@ -0,0 +1,318 @@
#include "Palette.h"
#include "resourceManager.h"
#include "Room.h"
#include "imageCache.h"
#include "packSaver.h"
#include "minorGems/util/log/AppLog.h"
// static inits
Palette *Palette::sBlankPalette = NULL;
void Palette::staticInit() {
sBlankPalette = new Palette();
// ensure that blank palette is saved at least once
sBlankPalette->finishEdit();
}
void Palette::staticFree() {
delete sBlankPalette;
}
void Palette::setupDefault() {
memset( (void *)mWellColors, 0, mWellDataLength );
const char *defaultName = "default";
memcpy( mName, defaultName, strlen( defaultName ) + 1 );
makeUniqueID();
}
Palette::Palette() {
setupDefault();
}
char Palette::initFromData( unsigned char *inData, int inLength ) {
if( inLength >= mWellDataLength ) {
memcpy( (void *)mWellColors, inData, mWellDataLength );
inLength -= mWellDataLength;
// remainder is name
if( inLength <= 11 ) {
memcpy( mName, &inData[mWellDataLength], inLength );
return true;
}
}
return false;
}
Palette::Palette( uniqueID inID, unsigned char *inData, int inLength )
: mID( inID ) {
if( !initFromData( inData, inLength ) ) {
// fail
setupDefault();
}
}
Palette::Palette( uniqueID inID )
: mID( inID ) {
int length;
char fromNetwork;
unsigned char *data = loadResourceData( "palette", mID, &length,
&fromNetwork );
if( data != NULL ) {
if( ! initFromData( data, length ) ) {
// failure
setupDefault();
}
delete [] data;
data = NULL;
if( fromNetwork ) {
// save to disk using the ID that we fetched it with
finishEdit( false );
}
}
else {
// palette failed to load
setupDefault();
}
if( data != NULL ) {
delete [] data;
}
}
void Palette::editPalette( int inIndex, rgbaColor inNewColor ) {
mWellColors[inIndex] = inNewColor;
}
rgbaColor Palette::getColor( int inIndex ) {
return mWellColors[inIndex];
}
void Palette::editPaletteName( const char *inName ) {
int newLength = strlen( inName );
if( newLength > 10 ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: Palette set name %s is too long (10 max)\n", inName );
}
else {
memcpy( mName, inName, newLength + 1 );
}
}
#include "minorGems/util/stringUtils.h"
char *Palette::getPaletteName() {
return stringDuplicate( mName );
}
#include "minorGems/util/SimpleVector.h"
void Palette::finishEdit( char inGenerateNewID ) {
uniqueID oldID = mID;
if( inGenerateNewID ) {
makeUniqueID();
}
if( ! equal( oldID, mID ) ||
! resourceExists( "palette", mID ) ) {
// change
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
saveResourceData( "palette", mID,
mName,
bytes, numBytes );
delete [] bytes;
}
}
unsigned char *Palette::makeBytes( int *outLength ) {
SimpleVector<unsigned char> dataAccum;
dataAccum.push_back( (unsigned char *)mWellColors,
mWellDataLength );
dataAccum.push_back( (unsigned char *)mName,
strlen( mName ) + 1 );
*outLength = dataAccum.size();
return dataAccum.getElementArray();
}
void Palette::saveToPack() {
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
addToPack( "palette", mID,
mName,
bytes, numBytes );
delete [] bytes;
}
uniqueID Palette::getUniqueID() {
return mID;
}
const char *Palette::getResourceType() {
return "palette";
}
Palette Palette::getDefaultResource() {
return *( Palette::sBlankPalette );
}
void Palette::makeUniqueID() {
partialUniqueID p = startUniqueID();
p = addToUniqueID( p,
(unsigned char*)mWellColors, mWellDataLength );
p = addToUniqueID( p, (unsigned char*)mName, strlen( mName ) + 1 );
mID = ::makeUniqueID( p );
}
Image *Palette::getImage() {
Image *image = new Image( P, P, 3, false );
double *channels[3];
int i;
for( i=0; i<3; i++ ) {
channels[i] = image->getChannel( i );
}
rgbaColor black = { { 0, 0, 0, 255 } };
for( int y=0; y<P; y++ ) {
for( int x=0; x<P; x++ ) {
int pixel = y * P + x;
int wellRow = y / 2;
// invert
wellRow = 8 - wellRow - 1;
int wellColumn = (x-1)/2 - 1;
rgbaColor c = black;
if( wellColumn >=0 && wellColumn < 5 ) {
c = mWellColors[ wellRow * 5 + wellColumn ];
}
for( i=0; i<3; i++ ) {
channels[i][pixel] = c.bytes[i] / 255.0;
}
}
}
return image;
}
Sprite *Palette::getSprite( char inUseTrans, char inCacheOK ) {
// ignore inUseTrans
if( inCacheOK ) {
Image *cachedImage = getCachedImage( mID, false );
if( cachedImage == NULL ) {
cachedImage = getImage();
addCachedImage( mID, false, cachedImage );
}
return new Sprite( cachedImage );
}
else {
// ignore cache
Image *image = getImage();
Sprite *sprite = new Sprite( image );
delete image;
return sprite;
}
}
void Palette::print() {
char *idString = getHumanReadableString( mID );
printf( "Palette %s:\n", idString );
delete [] idString;
printf( " name: %s\n", mName );
for( int i=0; i<C; i++ ) {
printf( " " );
::print( mWellColors[i] );
}
printf( "\n" );
}
+101
View File
@@ -0,0 +1,101 @@
#ifndef PALETTE_INCLUDED
#define PALETTE_INCLUDED
#include "color.h"
#include "common.h"
#include "uniqueID.h"
#include "Sprite.h"
class Palette {
public:
// blank palette
Palette();
// palette loaded from resource manager
Palette( uniqueID inID );
// palette loaded from data string
Palette( uniqueID inID, unsigned char *inData, int inLength );
// changes the color of a palette pixel
void editPalette( int inIndex, rgbaColor inNewColor );
rgbaColor getColor( int inIndex );
// name has at most 10 chars
void editPaletteName( const char *inName );
// destroyed by caller
char *getPaletteName();
// finishes the edit, generates a new unique ID, saves result
void finishEdit( char inGenerateNewID=true );
// recursively saves to current resource pack
void saveToPack();
// get an image of this palette
Image *getImage();
// implements ResourceType functions as needed by ResourcePicker
uniqueID getUniqueID();
Sprite *getSprite( char inUseTrans=false, char inCacheOK=true );
static const char *getResourceType();
static Palette getDefaultResource();
char *getName() {
return getPaletteName();
}
void print();
// blank sprite
static Palette *sBlankPalette;
static void staticInit();
static void staticFree();
protected:
void setupDefault();
char initFromData( unsigned char *inData, int inLength );
// result destroyed by caller
unsigned char *makeBytes( int *outLength );
void makeUniqueID();
// all palettes are exactly one grid square in size
rgbaColor mWellColors[C];
const static int mWellDataLength = C * 4;
char mName[11];
uniqueID mID;
};
#endif
+398
View File
@@ -0,0 +1,398 @@
#include "PaletteEditor.h"
#include "PalettePicker.h"
#include "ColorWells.h"
#include "ColorEditor.h"
#include "BorderPanel.h"
#include "labels.h"
#include "SelectionManager.h"
#include "packSaver.h"
extern ColorWells *mainColorStack;
extern PalettePicker *mainPalettePicker;
extern ColorEditor *mainColorEditor;
extern int gameWidth, gameHeight;
extern TextGL *largeTextFixed;
template <>
void SizeLimitedVector<Palette>::deleteElementOfType(
Palette inElement ) {
// no delete necessary
}
PaletteEditor::PaletteEditor( ScreenGL *inScreen )
: Editor( inScreen ),
mUndoStack( MAX_UNDOS, false ) {
mCloseButton->setToolTip( "tip_closeEdit_palette" );
mSidePanel->add( mainColorStack );
mainColorStack->addActionListener( this );
mSidePanel->add( mainPalettePicker );
mainPalettePicker->addActionListener( this );
mEditColorButton =
new EditButtonGL(
mainColorStack->getAnchorX() - 9,
mainColorStack->getAnchorY() + mainColorStack->getHeight() - 7,
8,
8 );
mSidePanel->add( mEditColorButton );
mEditColorButton->addActionListener( this );
mEditColorButton->setToolTip( "tip_edit_color" );
double offset = P;
double buttonSize = (gameHeight - 2 * offset - 8) / P;
rgbaColor c = { { 0,0,0,255 } };
Color unselectedBorder( 0.35, 0.35, 0.35, 1 );
double paletteButtonSize = 16;
double paletteButtonSpace = 24;
for( int y=0; y<8; y++ ) {
for( int x=0; x<5; x++ ) {
mButtonGrid[y][x] = new SpriteButtonGL(
NULL,
1,
51 + x * paletteButtonSpace,
gameWidth - ( 48 + (y + 1) * paletteButtonSpace ),
paletteButtonSize,
paletteButtonSize );
mButtonGrid[y][x]->setBorderColor( c );
GUIPanelGL *buttonBorderPanel =
new GUIPanelGL( mButtonGrid[y][x]->getAnchorX() - 1,
mButtonGrid[y][x]->getAnchorY() - 1,
mButtonGrid[y][x]->getWidth() + 2,
mButtonGrid[y][x]->getHeight() + 2,
unselectedBorder.copy() );
buttonBorderPanel->add( mButtonGrid[y][x] );
mMainPanel->add( buttonBorderPanel );
mButtonGrid[y][x]->addActionListener( this );
}
}
EightPixelLabel *fieldLabel = new EightPixelLabel( 150, 54,
"paletteName" );
mMainPanel->add( fieldLabel );
int fieldHeight = 8;
int fieldWidth = 8 * 10;
const char *defaultText = "default";
mNameField = new TextFieldGL( 150,
43,
fieldWidth,
fieldHeight,
1,
defaultText,
largeTextFixed,
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.15, 0.15, 0.15 ),
new Color( 0.75, 0.75, 0.75 ),
10,
false );
mMainPanel->add( mNameField );
mNameField->setFocus( true );
//mNameField->lockFocus( true );
mNameField->setCursorPosition( strlen( defaultText ) );
mNameField->addActionListener( this );
// center add button
double gridEdge = 8 + P * buttonSize;
double extra = gameHeight - gridEdge;
// center it vertically on palette picker
double addY = mainPalettePicker->getAnchorY() +
mainPalettePicker->getHeight() - 15;
double sideButtonsX = gridEdge + (extra - 16) / 2;
mAddButton = new AddButtonGL( sideButtonsX,
addY,
16, 16 );
mMainPanel->add( mAddButton );
mAddButton->addActionListener( this );
mAddButton->setToolTip( "tip_addPalette" );
mAddAction = false;
double miniButtonSize = P + 4;
mMiniViewButton = new SpriteButtonGL(
NULL, 1,
gridEdge + ( extra - miniButtonSize ) / 2,
addY - 24,
miniButtonSize,
miniButtonSize );
mMainPanel->add( mMiniViewButton );
double undoButtonY = gameWidth - ( 48 + P * buttonSize );
mUndoButton = new UndoButtonGL( sideButtonsX, undoButtonY, 16, 16 );
mMainPanel->add( mUndoButton );
mUndoButton->addActionListener( this );
mUndoButton->setEnabled( false );
mRedoButton = new RedoButtonGL( sideButtonsX, undoButtonY + 19, 16, 16 );
mMainPanel->add( mRedoButton );
mRedoButton->addActionListener( this );
mRedoButton->setEnabled( false );
mAddToPackButton = new ToggleSpriteButtonGL(
new Sprite( "pack.tga", true ),
new Sprite( "packAlreadyIn.tga", true ),
1,
mainPalettePicker->getAnchorX() + mainPalettePicker->getWidth() - 22,
mainPalettePicker->getAnchorY() +
mainPalettePicker->getHeight() + 1,
8,
8 );
mSidePanel->add( mAddToPackButton );
mAddToPackButton->addActionListener( this );
mAddToPackButton->setToolTip( "tip_addPaletteToPack" );
mAddToPackButton->setSecondToolTip( "tip_paletteAlreadyInPack" );
mSavePackButton = new SpriteButtonGL(
new Sprite( "packSave.tga", true ),
1,
mainPalettePicker->getAnchorX() + mainPalettePicker->getWidth() - 7,
mainPalettePicker->getAnchorY() +
mainPalettePicker->getHeight() + 1,
8,
8 );
mSidePanel->add( mSavePackButton );
mSavePackButton->addActionListener( this );
mSavePackButton->setToolTip( "tip_savePack" );
setPaletteToEdit( mainColorStack->getPalette() );
postConstruction();
}
PaletteEditor::~PaletteEditor() {
mSidePanel->remove( mainColorStack );
mSidePanel->remove( mainPalettePicker );
}
void PaletteEditor::grabPaletteFromWells() {
Palette p = mainColorStack->getPalette();
// preserve name as wells change
p.editPaletteName( mNameField->getText() );
setPaletteToEdit( p );
}
void PaletteEditor::setPaletteToEdit( Palette inPalette ) {
mPaletteToEdit = inPalette;
for( int y=0; y<8; y++ ) {
for( int x=0; x<5; x++ ) {
mButtonGrid[y][x]->setFillColor(
mPaletteToEdit.getColor( (8 - y - 1) * 5 + x ) );
}
}
refreshMiniView();
char *name = mPaletteToEdit.getPaletteName();
mNameField->setText( name );
mNameField->setCursorPosition( strlen( name ) );
delete [] name;
mainColorStack->setPalette( mPaletteToEdit );
}
void PaletteEditor::refreshMiniView() {
// don't use cached version
mMiniViewButton->setSprite( mPaletteToEdit.getSprite( false, false ) );
}
void PaletteEditor::saveUndoPoint() {
mUndoStack.push_back( mPaletteToEdit );
mUndoButton->setEnabled( true );
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
}
void PaletteEditor::actionPerformed( GUIComponent *inTarget ) {
// superclass
Editor::actionPerformed( inTarget );
if( inTarget == mainColorStack ) {
if( mainColorStack->mLastActionWellChange ) {
// palette has changed
saveUndoPoint();
grabPaletteFromWells();
}
}
else if( inTarget == mainPalettePicker ) {
if( ! mAddAction &&
! mainPalettePicker->wasLastActionFromPress() ) {
// will change palette
saveUndoPoint();
Palette p = mainPalettePicker->getSelectedResource();
setPaletteToEdit( p );
mAddToPackButton->setState(
alreadyInPack( mainPalettePicker->getSelectedResourceID() ) );
}
}
else if( inTarget == mNameField ) {
saveUndoPoint();
mPaletteToEdit.editPaletteName( mNameField->getText() );
}
else if( inTarget == mAddButton ) {
addPalette();
}
else if( inTarget == mEditColorButton ) {
// already in palette editor!
mainColorEditor->setEditPaletteButtonVisible( false );
showColorEditor();
}
else if( inTarget == mUndoButton ) {
int lastIndex = mUndoStack.size() - 1;
Palette last = *( mUndoStack.getElement( lastIndex ) );
mUndoStack.deleteElement( lastIndex );
if( mUndoStack.size() == 0 ) {
mUndoButton->setEnabled( false );
}
mRedoStack.push_back( mPaletteToEdit );
mRedoButton->setEnabled( true );
setPaletteToEdit( last );
}
else if( inTarget == mRedoButton ) {
int nextIndex = mRedoStack.size() - 1;
Palette next = *( mRedoStack.getElement( nextIndex ) );
mRedoStack.deleteElement( nextIndex );
if( mRedoStack.size() == 0 ) {
mRedoButton->setEnabled( false );
}
mUndoStack.push_back( mPaletteToEdit );
mUndoButton->setEnabled( true );
setPaletteToEdit( next );
}
else if( inTarget == mAddToPackButton ) {
AppLog::info( "Adding palette to the current resource pack" );
mainPalettePicker->getSelectedResource().saveToPack();
mAddToPackButton->setState( true );
}
else if( inTarget == mSavePackButton ) {
AppLog::info( "Saving the current resource pack" );
savePack();
mAddToPackButton->setState( false );
}
}
void PaletteEditor::editorClosing() {
addPalette();
}
void PaletteEditor::addPalette() {
mAddAction = true;
mPaletteToEdit.finishEdit();
mainPalettePicker->setSelectedResource( mPaletteToEdit, true );
mAddAction = false;
}
+75
View File
@@ -0,0 +1,75 @@
#ifndef PALETTE_EDITOR_INCLUDED
#define PALETTE_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "Palette.h"
#include "SizeLimitedVector.h"
#include "minorGems/graphics/openGL/gui/TextFieldGL.h"
class PaletteEditor : public Editor {
public:
PaletteEditor( ScreenGL *inScreen );
~PaletteEditor();
void grabPaletteFromWells();
virtual void actionPerformed( GUIComponent *inTarget );
protected:
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
// triggered by add button or close
void addPalette();
void saveUndoPoint();
AddButtonGL *mAddButton;
char mAddAction;
void setPaletteToEdit( Palette inPalette );
void refreshMiniView();
SpriteButtonGL *mButtonGrid[8][5];
SpriteButtonGL *mMiniViewButton;
Palette mPaletteToEdit;
EditButtonGL *mEditColorButton;
UndoButtonGL *mUndoButton;
RedoButtonGL *mRedoButton;
SizeLimitedVector<Palette> mUndoStack;
SimpleVector<Palette> mRedoStack;
TextFieldGL *mNameField;
ToggleSpriteButtonGL *mAddToPackButton;
SpriteButtonGL *mSavePackButton;
};
#endif
+17
View File
@@ -0,0 +1,17 @@
#include "PalettePicker.h"
// all share one stack
SimpleVector<uniqueID> globalPaletteStack;
PalettePicker::PalettePicker(
double inAnchorX, double inAnchorY )
: ResourcePicker<Palette>( inAnchorX, inAnchorY, &globalPaletteStack ) {
}
PalettePicker::~PalettePicker() {
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef PALETTE_PICKER_INCLUDED
#define PALETTE_PICKER_INCLUDED
#include "Palette.h"
#include "ResourcePicker.h"
// must include this to avoid linker errors
#include "ResourcePicker.cpp"
class PalettePicker : public ResourcePicker<Palette> {
public:
/**
* Constructs a picker.
*
* @param inAnchorX the x position of the upper left corner
* of this component.
* @param inAnchorY the y position of the upper left corner
* of this component.
*
* Sets its own width and height automatically.
*/
PalettePicker( double inAnchorX, double inAnchorY );
virtual ~PalettePicker();
};
#endif
+313
View File
@@ -0,0 +1,313 @@
#include "PlayerGame.h"
#include "TimerDisplay.h"
#include "Connection.h"
#include "ToolTipManager.h"
#include "musicPlayer.h"
#include "resourceManager.h"
#include "Song.h"
#include "common.h"
#include <stdlib.h>
#include "minorGems/util/TranslationManager.h"
#include "minorGems/util/SettingsManager.h"
#include "minorGems/util/log/AppLog.h"
extern TimerDisplay *mainTimerDisplay;
extern Connection *connection;
PlayerGame::PlayerGame( ScreenGL *inScreen )
: mConnected( true ) {
setScreen( inScreen );
// fetch failed resources from network
setUseNetwork( true );
char turnLengthFound = false;
mSecondsPerMove = SettingsManager::getIntSetting( "timeLimit",
&turnLengthFound );
if( !turnLengthFound ) {
mSecondsPerMove = 30;
}
mStepsPerSecond = 30;
resetTimer();
ToolTipManager::freeze( false );
ToolTipManager::setTip(
(char*)TranslationManager::translate( "tip_waiting" ) );
ToolTipManager::freeze( true );
mEditor = new PlayerMoveEditor( inScreen );
mEditor->setVisible( true );
//mEditor->addActionListener( this );
// wait for first move from player....
mEditor->setMovesDisabled( true );
mEditor->enableSend( false );
// for send button
mEditor->addActionListener( this );
}
PlayerGame::~PlayerGame() {
mEditor->setVisible( false );
delete mEditor;
}
// from musicPlayer.cpp
extern char noteToggles[PARTS][S][N][N];
extern int partLengths[PARTS];
void PlayerGame::resetTimer() {
mainTimerDisplay->setTime( 0 );
mStepsLeft = -1;
mLastStepTime = Time::getCurrentTime();
}
void PlayerGame::step() {
if( connection != NULL && connection->isConnected() ) {
if( ! mConnected ) {
mConnected = true;
if( mStepsLeft < 0 ) {
ToolTipManager::setTip(
(char*)TranslationManager::translate( "tip_waiting" ) );
ToolTipManager::freeze( true );
}
else {
// clear no connection
ToolTipManager::freeze( false );
ToolTipManager::setTip( NULL );
}
}
decrementStepCount();
mainTimerDisplay->freeze( false );
mainTimerDisplay->setTime( mStepsLeft / mStepsPerSecond );
if( mStepsLeft > 0 ) {
mEditor->enableSend( true );
}
}
else {
if( mConnected ) {
mConnected = false;
if( mStepsLeft < 0 ) {
ToolTipManager::freeze( false );
ToolTipManager::setTip(
(char*)TranslationManager::translate(
"tip_noConnection" ) );
ToolTipManager::freeze( true );
}
}
mainTimerDisplay->freeze( true );
mEditor->enableSend( false );
}
// printf( "Waiting for message\n" );
if( mStepsLeft == 0 ) {
// force a step here, regardless of system time
// we NEVER want to run step 0 more than once (send same
// state more than once)
mStepsLeft --;
if( connection != NULL ) {
AppLog::info( "Sending game state\n" );
mEditor->enableSend( false );
// first, clear speech that player is done reading
mEditor->clearNonPlayerSpeech();
int numBytes;
unsigned char *message =
mEditor->mGameStateToEdit->getStateAsMessage(
&numBytes );
connection->sendMessage( message, numBytes );
delete [] message;
ToolTipManager::setTip(
(char*)TranslationManager::translate( "tip_waiting" ) );
ToolTipManager::freeze( true );
}
// wait for controler's response
mEditor->setMovesDisabled( true );
}
else if( mStepsLeft < 0 ) {
// try to receive message from controller
if( connection != NULL ) {
int numBytes;
unsigned char *message = connection->receiveMessage( &numBytes );
if( message != NULL ) {
AppLog::info( "Got message\n" );
GameState *state = new GameState( message, numBytes );
delete [] message;
if( ! state->hasSong() ) {
// copy received note toggles into music player
// try to recreat v13 timbre bands
for( int y=0; y<N-1; y++ ) {
for( int x=0; x<N; x++ ) {
noteToggles[y/5][0][y%5][x] =
GameState::sNoteToggleBuffer[y][x];
}
}
// top row goes in last timbre, too
for( int x=0; x<N; x++ ) {
noteToggles[2][0][5][x] =
GameState::sNoteToggleBuffer[N-1][x];
}
partLengths[0] = 1;
partLengths[1] = 1;
partLengths[2] = 1;
}
else {
// probably coming from v14 Controller
// apply these changes to playing music
Song::setInPlayer( mLiveSong, state->mLiveSongReceived );
mLiveSong = state->mLiveSongReceived;
}
// do this *AFTER* snapshot is taken to avoid blank
// action arrows in snapshot (if player position frozen)
//mEditor->setMovesDisabled( false );
mEditor->setGameStateToEdit( state );
// stale now, because controller done editing it
// thus, it will be cleared right as player sends move
// (before taking flipbook screenshot, to avoid redundant
// speech in screenshots)
mEditor->mGameStateToEdit->markNonPlayerSpeechStale();
// player gets to move again
mStepsLeft = mStepsPerSecond * mSecondsPerMove;
mLastStepTime = Time::getCurrentTime();
// clear waiting tip
ToolTipManager::freeze( false );
ToolTipManager::setTip( NULL );
mEditor->enableSend( true );
}
}
}
}
void PlayerGame::drawScene() {
// right after we receive their move
// OR
// right after we sent our move
if( mStepsLeft == mStepsPerSecond * mSecondsPerMove
||
mStepsLeft == -1 ) {
mEditor->saveFlipBookImage();
if( mStepsLeft == mStepsPerSecond * mSecondsPerMove ) {
// just received a move, and took a snapshot of it...
// now enable player to move
mEditor->setMovesDisabled( false );
}
}
}
void PlayerGame::actionPerformed( GUIComponent *inTarget ) {
if( inTarget == mEditor ) {
// event from this means "send"
AppLog::info( "Send button pressed\n" );
if( mStepsLeft > 0 ) {
// will send on very next step
mStepsLeft = 1;
mainTimerDisplay->setTime( 0 );
}
else {
AppLog::error(
"ERROR: Send button pressed when mStepsLeft not positive" );
}
}
}
+33
View File
@@ -0,0 +1,33 @@
#include "GameHalf.h"
#include "PlayerMoveEditor.h"
#include "minorGems/ui/event/ActionListener.h"
class PlayerGame : public GameHalf, public ActionListener {
public:
PlayerGame( ScreenGL *inScreen );
~PlayerGame();
virtual void resetTimer();
virtual void step();
virtual void drawScene();
// implements ActionListener
virtual void actionPerformed( GUIComponent *inTarget );
protected:
PlayerMoveEditor *mEditor;
char mConnected;
// last live song received from Controller
Song mLiveSong;
};
File diff suppressed because it is too large Load Diff
+79
View File
@@ -0,0 +1,79 @@
#ifndef PLAYER_MOVE_EDITOR_INCLUDED
#define PLAYER_MOVE_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "GameState.h"
#include "GameStateDisplay.h"
#include "MoveToolSet.h"
#include "FixedTipDisplay.h"
class PlayerMoveEditor : public Editor {
public:
PlayerMoveEditor( ScreenGL *inScreen );
~PlayerMoveEditor();
virtual void actionPerformed( GUIComponent *inTarget );
void setGameStateToEdit( GameState *inGameState );
GameState *mGameStateToEdit;
void setMovesDisabled( char inDisabled );
// clears all but player speech
void clearNonPlayerSpeech();
void enableSend( char inEnabled );
// has no effect if flip book turned off
void saveFlipBookImage();
protected:
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
GameStateDisplay *mStateDisplay;
MoveToolSet *mToolSet;
DeleteButtonGL *mSpeechDeleteButton;
DeleteButtonGL *mActionDeleteButton;
SendButtonGL *mSendButton;
SpriteButtonGL *mPracticeStopButton;
FixedTipDisplay *mTipDisplay;
SelectableButtonGL *mFlipBookButton;
char mMovesDisabled;
// updated once per play session
int mFlipBookFolderNumber;
// starts at 1 for each play session
int mFlipBookImageNumber;
};
#endif
+22
View File
@@ -0,0 +1,22 @@
#include "PressActionGUIPanelGL.h"
PressActionGUIPanelGL::PressActionGUIPanelGL(
double inAnchorX, double inAnchorY, double inWidth,
double inHeight, Color *inColor )
: GUIPanelGL( inAnchorX, inAnchorY, inWidth,
inHeight, inColor ) {
}
void PressActionGUIPanelGL::mouseReleased( double inX, double inY ) {
GUIPanelGL::mouseReleased( inX, inY );
if( isInside( inX, inY ) ) {
fireActionPerformed( this );
}
}
+25
View File
@@ -0,0 +1,25 @@
#include "minorGems/graphics/openGL/gui/GUIPanelGL.h"
#include "minorGems/ui/event/ActionListenerList.h"
// panel that fires an action when mouse pressed on it
// can be used to hold focus when mouse pressed outside a component of interest
// (but hits containing panel)
class PressActionGUIPanelGL : public GUIPanelGL, public ActionListenerList {
public:
PressActionGUIPanelGL(
double inAnchorX, double inAnchorY, double inWidth,
double inHeight, Color *inColor );
virtual ~PressActionGUIPanelGL() {
}
// override to fire action
virtual void mouseReleased( double inX, double inY );
};
+804
View File
@@ -0,0 +1,804 @@
#ifndef RESOURCE_PICKER_IMPLEMENTATION_INCLUDED
#define RESOURCE_PICKER_IMPLEMENTATION_INCLUDED
#include "ResourcePicker.h"
#include "resourceDatabase.h"
#include "resourceManager.h"
#include "usageDatabase.h"
#include "speechHints.h"
#include "Room.h"
#include "imageCache.h"
#include "minorGems/util/stringUtils.h"
#include "minorGems/util/log/AppLog.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
extern TextGL *largeTextFixed;
template <class ResourceType>
ResourcePicker<ResourceType>::ResourcePicker(
double inAnchorX, double inAnchorY,
SimpleVector<uniqueID> *inStack )
: BorderPanel( inAnchorX, inAnchorY, 70, 93,
new Color( 0, 0, 0, 1 ),
new Color( 0.35, 0.35, 0.35, 1 ),
1 ),
mNumResourcesOnScreen( 0 ),
mSearchResultOffset( 0 ),
mNumSearchResults( 0 ),
mStackMode( false ),
mStack( inStack ) {
double tileSpace = P;
// room for button borders
double buttonSpace = tileSpace + 4;
// use same separation for both x and y
double buttonSeparation =
( mWidth - buttonSpace * mGridW ) / (mGridW + 1);
// put left and right at very bottom
mLeftButton = new LeftButtonGL( mAnchorX + buttonSeparation,
mAnchorY + buttonSeparation,
8, 8 );
add( mLeftButton );
mLeftButton->setEnabled( false );
mLeftButton->addActionListener( this );
mRightButton = new RightButtonGL( mAnchorX + mWidth - buttonSeparation - 8,
mAnchorY + buttonSeparation,
8, 8 );
add( mRightButton );
mRightButton->setEnabled( false );
mRightButton->addActionListener( this );
// centered above picker
mStackSearchButton = new StackSearchButtonGL( mAnchorX + (mWidth-8)/3 - 1,
mAnchorY + 1,
8, 8 );
add( mStackSearchButton );
mStackSearchButton->addActionListener( this );
double baseY = mAnchorY + buttonSeparation + 8;
double topButtonY = baseY + buttonSeparation +
mGridH * ( buttonSpace + buttonSeparation ) - buttonSpace;
for( int y=0; y<mGridH; y++ ) {
for( int x=0; x<mGridW; x++ ) {
mButtonGrid[y][x] = new DraggableTwoSpriteButtonGL(
NULL, NULL,
1,
mAnchorX + buttonSeparation +
(buttonSpace + buttonSeparation) * x,
topButtonY - buttonSeparation -
(buttonSpace + buttonSeparation) * y,
buttonSpace,
buttonSpace );
add( mButtonGrid[y][x] );
mButtonGrid[y][x]->setEnabled( false );
mButtonGrid[y][x]->addActionListener( this );
}
}
mLastActionFromPress = false;
int fieldHeight = 8;
int fieldWidth = 8 * 8;
const char *defaultSearch = "";
double fieldAnchorY = topButtonY + buttonSpace + 1;
mSearchField = new TextFieldGL( mAnchorX + 2,
fieldAnchorY,
fieldWidth,
fieldHeight,
1,
defaultSearch,
largeTextFixed,
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.15, 0.15, 0.15 ),
new Color( 0.75, 0.75, 0.75 ),
8,
false );
add( mSearchField );
mSearchField->setFocus( false );
//mSearchField->lockFocus( true );
mSearchField->setCursorPosition( strlen( defaultSearch ) );
mSearchField->addActionListener( this );
mSelectedResourceButton = new DraggableTwoSpriteButtonGL(
NULL,
NULL,
1,
mAnchorX + mWidth - buttonSpace - 2,
mAnchorY + mHeight - buttonSpace - 2,
buttonSpace,
buttonSpace );
add( mSelectedResourceButton );
mSelectedResourceButton->setEnabled( true );
mSelectedResourceButton->addActionListener( this );
refreshSelectedResourceSprite();
mDeleteButton = new DeleteButtonGL(
mSelectedResourceButton->getAnchorX() - 8,
mAnchorY + mHeight - 10,
8, 8 );
add( mDeleteButton );
mDeleteButton->addActionListener( this );
mDeleteButton->setEnabled( false );
// concatonate
char *tipKey = autoSprintf( "tip_delete_%s",
ResourceType::getResourceType() );
mDeleteButton->setToolTip( tipKey );
delete [] tipKey;
EightPixelLabel *tileLabel =
new EightPixelLabel( mAnchorX + 2,
mAnchorY + mHeight - 8 - 2,
(char*)( ResourceType::getResourceType() ) );
add( tileLabel );
mSearchLabel = new EightPixelLabel( mAnchorX + 2,
fieldAnchorY +
fieldHeight + 1,
"search" );
add( mSearchLabel );
mStackLabel = new EightPixelLabel( mAnchorX + 2,
fieldAnchorY +
fieldHeight + 1,
"stack" );
}
template <class ResourceType>
ResourcePicker<ResourceType>::~ResourcePicker() {
if( !contains( mSearchLabel ) ) {
delete mSearchLabel;
}
if( !contains( mSearchField ) ) {
delete mSearchField;
}
if( !contains( mStackLabel ) ) {
delete mStackLabel;
}
}
template <class ResourceType>
ResourceType ResourcePicker<ResourceType>::getSelectedResource() {
return mSelectedResource;
}
template <class ResourceType>
uniqueID ResourcePicker<ResourceType>::getSelectedResourceID() {
return mSelectedResource.getUniqueID();
}
template <class ResourceType>
void ResourcePicker<ResourceType>::refreshSelectedResourceSprite() {
Sprite *backgroundSprite = getButtonGridBackground();
char useTrans = false;
if( backgroundSprite != NULL ) {
useTrans = true;
}
mSelectedResourceButton->setFrontSprite(
mSelectedResource.getSprite( useTrans ) );
mSelectedResourceButton->setSprite( backgroundSprite );
char *tip = mSelectedResource.getName();
mSelectedResourceButton->setToolTip( tip, false );
delete [] tip;
}
template <class ResourceType>
void ResourcePicker<ResourceType>::recheckDeletable() {
if( equal( mSelectedResource.getUniqueID(),
ResourceType::getDefaultResource().getUniqueID() ) ) {
// can't delete blank
mDeleteButton->setEnabled( false );
}
else if( isUsed( mSelectedResource.getUniqueID() ) ) {
uniqueID user = getUser( mSelectedResource.getUniqueID() );
char *userName = getResourceName( user );
char *tip;
char *resourceName = mSelectedResource.getName();
if( userName != NULL ) {
tip = autoSprintf( "%s (used in %s)",
resourceName,
userName );
}
else {
tip = autoSprintf( "%s (used in current edit)",
resourceName );
}
delete [] resourceName;
mSelectedResourceButton->setToolTip( tip, false );
delete [] tip;
// can't delete a resource used elsewhere
mDeleteButton->setEnabled( false );
}
else {
// not default, and not used anywhere. Deletable!
mDeleteButton->setEnabled( true );
}
}
template <class ResourceType>
void ResourcePicker<ResourceType>::setSelectedResource(
ResourceType inResource, char inForceResultRefresh, char inFireEvent ) {
if( !equal( mSelectedResource.getUniqueID(), inResource.getUniqueID() ) ) {
// a change
mSelectedResource = inResource;
refreshSelectedResourceSprite();
// remove from lower in stack
uniqueID id = mSelectedResource.getUniqueID();
char found = false;
int stackSize = mStack->size();
for( int i=0; i<stackSize && !found; i++ ) {
if( equal( id, *( mStack->getElement( i ) ) ) ) {
mStack->deleteElement( i );
found = true;
}
}
mStack->push_back( id );
if( mStack->size() > 256 ) {
// size limit reached
// delete oldest
mStack->deleteElement( 0 );
}
recheckDeletable();
if( mStackMode ) {
// back to top whenever top changes
mSearchResultOffset = 0;
}
if( inForceResultRefresh ) {
newSearch( true );
}
else if( mStackMode ) {
if( found ) {
// just stack order changed
getFreshResults();
}
else {
// something new added to stack... maybe a size change
newSearch( false );
}
}
if( inFireEvent ) {
mLastActionFromPress = false;
fireActionPerformed( this );
}
}
}
template <class ResourceType>
void ResourcePicker<ResourceType>::forceNewSearch() {
newSearch( false );
}
template <class ResourceType>
void ResourcePicker<ResourceType>::newSearch( char inPreservePageOffset,
char inMoveBackward ) {
char *search = stringDuplicate( mSearchField->getText() );
int numResults;
if( !mStackMode ) {
numResults = countSearchResults( ResourceType::getResourceType(),
search );
}
else {
numResults = countStackResults();
}
//printf( "New resource search, %d results\n", numResults );
delete [] search;
int oldOffset = mSearchResultOffset;
mNumSearchResults = numResults;
mSearchResultOffset = 0;
if( inPreservePageOffset ) {
if( oldOffset < numResults ) {
// still in range
mSearchResultOffset = oldOffset;
}
}
if( numResults > mNumResourcesDisplayedMax ) {
// scroll
numResults = mNumResourcesDisplayedMax;
mLeftButton->setEnabled( true );
mRightButton->setEnabled( true );
}
else {
// only one page
mLeftButton->setEnabled( false );
mRightButton->setEnabled( false );
}
getFreshResults( inMoveBackward );
}
template <class ResourceType>
Sprite *ResourcePicker<ResourceType>::getButtonGridBackground() {
return NULL;
}
template <class ResourceType>
void ResourcePicker<ResourceType>::getFreshResults( char inMoveBackward ) {
uniqueID ids[mNumResourcesDisplayedMax];
// clear old sprites
int i;
for( i=0; i<mNumResourcesDisplayedMax; i++ ) {
int y = i / mGridW;
int x = i % mGridW;
mButtonGrid[y][x]->setEnabled( false );
}
char *search = stringDuplicate( mSearchField->getText() );
//printf( "Fresh results with offset %d from %d results\n",
// mSearchResultOffset, mNumSearchResults );
if( !mStackMode ) {
mNumResourcesOnScreen = getSearchResults(
ResourceType::getResourceType(),
search,
mSearchResultOffset,
mNumResourcesDisplayedMax,
ids );
}
else {
mNumResourcesOnScreen = getStackResults( mSearchResultOffset,
mNumResourcesDisplayedMax,
ids );
}
delete [] search;
Sprite *backgroundSprite = getButtonGridBackground();
char useTrans = false;
if( backgroundSprite != NULL ) {
useTrans = true;
// delete this test sprite, because we need to add a copy
// to every button below
delete backgroundSprite;
}
char badResultCount = 0;
for( i=0; i<mNumResourcesOnScreen; i++ ) {
ResourceType r( ids[i] );
mResourcesDisplayed[i] = r;
int y = i / mGridW;
int x = i % mGridW;
mButtonGrid[y][x]->setEnabled( true );
mButtonGrid[y][x]->setFrontSprite( r.getSprite( useTrans ) );
mButtonGrid[y][x]->setSprite( getButtonGridBackground() );
char *tip = r.getName();
mButtonGrid[y][x]->setToolTip( tip, false );
delete [] tip;
if( ! equal( ids[i], r.getUniqueID() ) ) {
// this resource failed to load (bogus search result in index,
// and it will be removed by resourceManager)
// it will show up as a "default" resource with blank sprite,
// even if the current search does not match the word "default",
// which is confusing.
badResultCount++;
// abandon this search and start a brand new one, attempting
// to maintain the page offset
// however, finish loading rest of resources in this batch
// in case it contains multiple bad ones (they are only removed
// by resourceManager when they are loaded), instead of removing
// them one-by-one with several newSearches (slow)
}
}
if( badResultCount > 0 ) {
if( inMoveBackward ) {
// when moving backward through results,
// the removal of bad results can cause the left button
// to appear to have no effect
// account for this
mNumSearchResults -= badResultCount;
mSearchResultOffset -= badResultCount;
lowerBoundSearchOffset();
}
newSearch( true, inMoveBackward );
}
}
template <class ResourceType>
void ResourcePicker<ResourceType>::lowerBoundSearchOffset() {
if( mSearchResultOffset < 0 ) {
// roll back to last page
int numOnLastPage = mNumSearchResults % mNumResourcesDisplayedMax;
mSearchResultOffset = mNumSearchResults - numOnLastPage;
if( numOnLastPage == 0 ) {
// an even number of full pages
// show last page
mSearchResultOffset =
mSearchResultOffset - mNumResourcesDisplayedMax;
}
}
}
template <class ResourceType>
void ResourcePicker<ResourceType>::actionPerformed( GUIComponent *inTarget ) {
if( inTarget == mSearchField ) {
// search changed
newSearch();
}
else if( inTarget == mDeleteButton ) {
// confirmed delete!
uniqueID id = mSelectedResource.getUniqueID();
deleteResource( ResourceType::getResourceType(), id );
removeUsages( id );
// delete from stack, too
char found = false;
int stackSize = mStack->size();
// walk backward... most likely at top of stack (right?)
for( int i=stackSize-1; i>=0 && !found; i-- ) {
if( equal( id, *( mStack->getElement( i ) ) ) ) {
AppLog::detail( "Found element to delete in stack\n" );
mStack->deleteElement( i );
found = true;
}
}
// delete from image cache, too
clearCachedImages( id );
// also hint no longer needed
clearSpeechHint( id );
// refresh results (in case they show the deleted tile)
newSearch( true );
if( !mStackMode || mStack->size() == 0 ) {
// set to default
setSelectedResource( ResourceType::getDefaultResource() );
}
else {
// take next top of stack
ResourceType r( *( mStack->getElement( mStack->size() - 1 ) ) );
setSelectedResource( r );
}
}
else if( inTarget == mLeftButton ) {
mSearchResultOffset -= mNumResourcesDisplayedMax;
lowerBoundSearchOffset();
getFreshResults( true );
}
else if( inTarget == mRightButton ) {
mSearchResultOffset += mNumResourcesDisplayedMax;
if( mSearchResultOffset > mNumSearchResults - 1 ) {
// wrap back around
mSearchResultOffset = 0;
}
getFreshResults();
}
else if( inTarget == mStackSearchButton ) {
mStackMode = mStackSearchButton->getState();
if( mStackMode ) {
remove( mSearchField );
remove( mSearchLabel );
add( mStackLabel );
}
else {
remove( mStackLabel );
add( mSearchLabel );
add( mSearchField );
}
newSearch();
}
else if( inTarget == mSelectedResourceButton ) {
if( mSelectedResourceButton->mLastActionFromPress ) {
// event from a press.... start drag and drop?
mLastActionFromPress = true;
mCurrentDraggedResource = mSelectedResource;
fireActionPerformed( this );
}
}
else {
// consider a button press
char found = false;
for( int i=0; i<mNumResourcesOnScreen && !found; i++ ) {
int y = i / mGridW;
int x = i % mGridW;
if( inTarget == mButtonGrid[y][x] ) {
found = true;
if( ! mButtonGrid[y][x]->mLastActionFromPress ) {
// don't let it fire an event...
// (because it only fires on item change)
setSelectedResource( mResourcesDisplayed[ i ], false,
false );
// ...we fire our own, even if there's no change
mLastActionFromPress = false;
fireActionPerformed( this );
/*
if( mStackMode ) {
// show top of stack again
mSearchResultOffset = 0;
getFreshResults();
}
*/
}
else {
// event from a press.... start drag and drop?
mLastActionFromPress = true;
mCurrentDraggedResource = mResourcesDisplayed[ i ];
fireActionPerformed( this );
}
}
}
}
}
template <class ResourceType>
void ResourcePicker<ResourceType>::setState( ResourceType inSelectedResource,
char *inSearchString,
int inResultOffset,
char inStackMode ) {
mSearchField->setText( inSearchString );
mSearchResultOffset = inResultOffset;
char stackModeChange = ( mStackMode != inStackMode );
mStackMode = inStackMode;
mStackSearchButton->setState( mStackMode );
if( stackModeChange ) {
if( mStackMode ) {
remove( mSearchField );
remove( mSearchLabel );
add( mStackLabel );
}
else {
remove( mStackLabel );
add( mSearchLabel );
add( mSearchField );
}
}
setSelectedResource( inSelectedResource );
// always do new search after clone (in case selected resource doesn't
// change, so no new search would be triggered)
newSearch( true );
}
template <class ResourceType>
void ResourcePicker<ResourceType>::cloneState(
ResourcePicker *inOtherPicker ) {
inOtherPicker->setState( mSelectedResource,
mSearchField->getText(),
mSearchResultOffset,
mStackMode );
}
template <class ResourceType>
int ResourcePicker<ResourceType>::countStackResults() {
return mStack->size();
}
template <class ResourceType>
int ResourcePicker<ResourceType>::getStackResults( int inNumToSkip,
int inNumToGet,
uniqueID *outIDs ) {
int numGotten = 0;
int stackSize = mStack->size();
int end = inNumToSkip + inNumToGet;
if( end > stackSize ) {
end = stackSize;
}
for( int i=inNumToSkip; i<end; i++ ) {
// reverse order (end of vector is top of stack
outIDs[ numGotten ] = *( mStack->getElement( stackSize - i - 1 ) );
numGotten++;
}
return numGotten;
}
template <class ResourceType>
char ResourcePicker<ResourceType>::wasLastActionFromPress() {
return mLastActionFromPress;
}
template <class ResourceType>
ResourceType ResourcePicker<ResourceType>::getDraggedResource() {
return mCurrentDraggedResource;
}
template <class ResourceType>
char ResourcePicker<ResourceType>::isSearchFieldFocused() {
return mSearchField->isFocused();
}
#endif
+172
View File
@@ -0,0 +1,172 @@
#ifndef RESOURCE_PICKER_INCLUDED
#define RESOURCE_PICKER_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIContainerGL.h"
#include "minorGems/ui/event/ActionListenerList.h"
#include "minorGems/graphics/openGL/gui/TextFieldGL.h"
#include "BorderPanel.h"
#include "Sprite.h"
#include "buttons.h"
#include "uniqueID.h"
#include "labels.h"
template <class ResourceType>
class ResourcePicker : public BorderPanel, public ActionListener,
public ActionListenerList {
public:
/**
* Constructs a picker.
*
* @param inAnchorX the x position of the upper left corner
* of this component.
* @param inAnchorY the y position of the upper left corner
* of this component.
* @param inStack pointer to the stack shared by this picker.
* Must be destroyed externally.
*
* Sets its own width and height automatically.
*/
ResourcePicker( double inAnchorX, double inAnchorY,
SimpleVector<uniqueID> *inStack );
virtual ~ResourcePicker();
ResourceType getSelectedResource();
uniqueID getSelectedResourceID();
void setSelectedResource( ResourceType inResource,
char inForceResultRefresh = false,
char inFireEvent = true );
void forceNewSearch();
// used to check if DragAndDrop should be started by an action
char wasLastActionFromPress();
ResourceType getDraggedResource();
char isSearchFieldFocused();
// Forces this picker to recheck if selected resource is deleteable
// (should be called after a usage database change)
void recheckDeletable();
// action listener
virtual void actionPerformed( GUIComponent *inTarget );
// copy the state from this picker onto another picker
virtual void cloneState( ResourcePicker *inOtherPicker );
protected:
// force the state of this picker
void setState( ResourceType inSelectedResource,
char *inSearchString,
int inResultOffset,
char inStackMode );
static const int mNumResourcesDisplayedMax = 6;
ResourceType mResourcesDisplayed[mNumResourcesDisplayedMax];
//Sprite *mDisplaySprites[mNumResourcesDisplayedMax];
static const int mGridW = 3;
static const int mGridH = 2;
DraggableTwoSpriteButtonGL *mButtonGrid[mGridH][mGridW];
char mLastActionFromPress;
ResourceType mCurrentDraggedResource;
// defaults to NULL
// can be overridden by subclasses to provide a background
// behind transparent resources
// if non-NULL, will be destroyed by caller
virtual Sprite *getButtonGridBackground();
ResourceType mSelectedResource;
DraggableTwoSpriteButtonGL *mSelectedResourceButton;
DeleteButtonGL *mDeleteButton;
LeftButtonGL *mLeftButton;
RightButtonGL *mRightButton;
StackSearchButtonGL *mStackSearchButton;
int mNumResourcesOnScreen;
int mSearchResultOffset;
int mNumSearchResults;
TextFieldGL *mSearchField;
EightPixelLabel *mSearchLabel;
EightPixelLabel *mStackLabel;
void newSearch( char inPreservePageOffset=false,
char inMoveBackward=false );
void getFreshResults( char inMoveBackward=false );
void refreshSelectedResourceSprite();
void lowerBoundSearchOffset();
char mStackMode;
// pointer passed in from outside (so that two pickers can share
// the same stack)
SimpleVector<uniqueID> *mStack;
int countStackResults();
// caller allocates spaces for inNumToGet and passes pointer as outIDs
// returns the number returned
int getStackResults( int inNumToSkip,
int inNumToGet,
uniqueID *outIDs );
};
#endif
+400
View File
@@ -0,0 +1,400 @@
#include "Room.h"
#include "resourceManager.h"
#include "usageDatabase.h"
#include "uniqueID.h"
#include "imageCache.h"
#include "packSaver.h"
#include "minorGems/util/stringUtils.h"
#include "minorGems/util/log/AppLog.h"
// static inits
Tile *Room::sBlankTile = NULL;
Room *Room::sBlankRoom = NULL;
void Room::staticInit() {
sBlankTile = new Tile();
// ensure that blank tile is saved at least once
sBlankTile->finishEdit();
sBlankRoom = new Room();
// save once
sBlankRoom->finishEdit();
}
void Room::staticFree() {
delete sBlankTile;
delete sBlankRoom;
}
void Room::setupDefault() {
const char *defaultName = "default";
memcpy( mName, defaultName, strlen( defaultName ) + 1 );
uniqueID blankID = sBlankTile->getUniqueID();
int x;
int y;
for( y=0; y<G; y++ ) {
for( x=0; x<G; x++ ) {
mTiles[y][x] = blankID;
mWallFlags[y][x] = false;
}
}
makeUniqueID();
}
Room::Room() {
setupDefault();
}
// room loaded from file
Room::Room( uniqueID inID )
: mID( inID ) {
int length;
char fromNetwork;
unsigned char *data = loadResourceData( "room", mID, &length,
&fromNetwork );
if( data != NULL ) {
readFromBytes( data, length );
delete [] data;
if( fromNetwork ) {
// tiles might not be present locally, either
// more efficient to load them in a chunk instead of with separate
// messages
uniqueID ids[G*G];
int i=0;
for( int ty=0; ty<G; ty++ ) {
for( int tx=0; tx<G; tx++ ) {
ids[i] = mTiles[ty][tx];
i++;
}
}
int chunkLengths[G*G];
char fromNetworkFlags[G*G];
unsigned char **chunks = loadResourceData( "tile",
G*G,
ids,
chunkLengths,
fromNetworkFlags );
for( int i=0; i<G*G; i++ ) {
if( chunks[i] != NULL ) {
if( fromNetworkFlags[i] ) {
// make a tile
Tile t( ids[i], chunks[i], chunkLengths[i] );
// force into disk cache, don't remake ID
t.finishEdit( false );
}
delete [] chunks[i];
}
}
delete [] chunks;
// save room to disk using the ID that we fetched it with
finishEdit( false );
}
}
else {
setupDefault();
}
}
void Room::editRoomTile( int inX, int inY, uniqueID inTileID ) {
mTiles[inY][inX] = inTileID;
}
void Room::editRoomWall( int inX, int inY, char inIsWall ){
mWallFlags[inY][inX] = inIsWall;
}
void Room::editRoomName( const char *inName ) {
int newLength = strlen( inName );
if( newLength > 10 ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: Room name %s is too long (10 max)\n", inName );
}
else {
memcpy( mName, inName, newLength + 1 );
}
}
uniqueID Room::getTile( int inX, int inY ) {
return mTiles[inY][inX];
}
char Room::getWall( int inX, int inY ) {
return mWallFlags[inY][inX];
}
char *Room::getRoomName() {
return stringDuplicate( mName );
}
// finishes the edit, generates a new unique ID, saves result
void Room::finishEdit( char inGenerateNewID ) {
uniqueID oldID = mID;
if( inGenerateNewID ) {
makeUniqueID();
}
if( !equal( oldID, mID ) ||
! resourceExists( "room", mID ) ) {
// change
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
saveResourceData( "room", mID, mName, bytes, numBytes );
delete [] bytes;
// track usages
for( int y=0; y<G; y++ ) {
for( int x=0; x<G; x++ ) {
addUsage( mID, mTiles[y][x] );
}
}
}
}
void Room::saveToPack() {
for( int ty=0; ty<G; ty++ ) {
for( int tx=0; tx<G; tx++ ) {
Tile t( mTiles[ty][tx] );
t.saveToPack();
}
}
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
addToPack( "room", mID, mName, bytes, numBytes );
delete [] bytes;
}
uniqueID Room::getUniqueID() {
return mID;
}
Image *Room::getImage() {
// compose all tile images into a sprite with 1 pixel per tile
// zero to prepare for average
Image *fullImage = new Image( P, P, 3, true );
double *fullC[3] =
{ fullImage->getChannel( 0 ),
fullImage->getChannel( 1 ),
fullImage->getChannel( 2 ) };
for( int ty=0; ty<G; ty++ ) {
for( int tx=0; tx<G; tx++ ) {
int fullP = (ty + 1) * P + (tx + 1);
Tile t( mTiles[ty][tx] );
Image *tImage = t.getImage();
double *tC[3] =
{ tImage->getChannel( 0 ),
tImage->getChannel( 1 ),
tImage->getChannel( 2 ) };
// compute average of all tile pixels
for( int c=0; c<3; c++ ) {
// sum first
for( int i=0; i<P*P; i++ ) {
fullC[c][fullP] += tC[c][i];
}
fullC[c][fullP] /= P*P;
}
delete tImage;
}
}
return fullImage;
}
Sprite *Room::getSprite( char inUseTrans, char inCacheOK ) {
// ignore inUseTrans
Image *cachedImage = NULL;
if( inCacheOK ) {
cachedImage = getCachedImage( mID, false );
}
if( cachedImage == NULL ) {
cachedImage = getImage();
if( inCacheOK ) {
addCachedImage( mID, false, cachedImage );
}
}
Sprite *sprite = new Sprite( cachedImage );
if( !inCacheOK ) {
// image not in cache... we must destroy it
delete cachedImage;
}
return sprite;
}
const char *Room::getResourceType() {
return "room";
}
Room Room::getDefaultResource() {
return *sBlankRoom;
}
#include "minorGems/util/SimpleVector.h"
unsigned char *Room::makeBytes( int *outLength ) {
SimpleVector<unsigned char> buffer;
int y, x;
for( y=0; y<G; y++ ) {
for( x=0; x<G; x++ ) {
buffer.push_back( mTiles[y][x].bytes, U );
}
}
for( y=0; y<G; y++ ) {
for( x=0; x<G; x++ ) {
buffer.push_back( mWallFlags[y][x] );
}
}
buffer.push_back( (unsigned char *)mName, strlen( mName ) + 1 );
*outLength = buffer.size();
return buffer.getElementArray();
}
void Room::readFromBytes( unsigned char *inBytes, int inLength ) {
int numBytesUsed;
char success = readUniqueIDs( (uniqueID *)mTiles, G*G,
inBytes, inLength,
&numBytesUsed );
if( success ) {
inBytes = &( inBytes[ numBytesUsed ] );
inLength -= numBytesUsed;
}
if( inLength >= G*G ) {
memcpy( (void *)mWallFlags, inBytes, G*G );
inBytes = &( inBytes[ G*G ] );
inLength -= G*G;
}
// remainder is name
if( inLength <= 11 ) {
memcpy( mName, inBytes, inLength );
}
}
void Room::makeUniqueID() {
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
mID = ::makeUniqueID( bytes, numBytes );
delete [] bytes;
}
+102
View File
@@ -0,0 +1,102 @@
#ifndef ROOM_INCLUDED
#define ROOM_INCLUDED
#include "color.h"
#include "common.h"
#include "uniqueID.h"
#include "Sprite.h"
#include "Tile.h"
class Room {
public:
// room filled with blank tiles and default name
Room();
// room loaded from file
Room( uniqueID inID );
// changes the tile in a given grid location
void editRoomTile( int inX, int inY, uniqueID inTileID );
void editRoomWall( int inX, int inY, char inIsWall );
// name has at most 10 chars
void editRoomName( const char *inName );
// gets ID of tile on grid
uniqueID getTile( int inX, int inY );
char getWall( int inX, int inY );
char *getRoomName();
// finishes the edit, generates a new unique ID, saves result
void finishEdit( char inGenerateNewID=true );
// recursively saves to current resource pack
void saveToPack();
// image version of getSprite, never fetched from cache
Image *getImage();
// implements ResourceType functions as needed by ResourcePicker
uniqueID getUniqueID();
Sprite *getSprite( char inUseTrans=false, char inCacheOK=true );
static const char *getResourceType();
static Room getDefaultResource();
char *getName() {
return getRoomName();
}
// blank tile used as "edge" in a room
static Tile *sBlankTile;
// blank room
static Room *sBlankRoom;
static void staticInit();
static void staticFree();
protected:
void setupDefault();
// result destroyed by caller
unsigned char *makeBytes( int *outLength );
void readFromBytes( unsigned char *inBytes, int inLength );
void makeUniqueID();
// all tiles are exactly one grid square in size
uniqueID mTiles[G][G];
char mWallFlags[G][G];
char mName[11];
uniqueID mID;
};
#endif
+775
View File
@@ -0,0 +1,775 @@
#include "RoomEditor.h"
#include "RoomPicker.h"
#include "TilePicker.h"
#include "BorderPanel.h"
#include "GridOverlay.h"
#include "labels.h"
#include "minorGems/util/log/AppLog.h"
#include <stdio.h>
extern TilePicker *mainTilePicker;
extern RoomPicker *mainRoomPicker;
extern int gameWidth, gameHeight;
extern TextGL *largeTextFixed;
template <>
void SizeLimitedVector<Room>::deleteElementOfType(
Room inElement ) {
// no delete necessary
}
RoomEditor::RoomEditor( ScreenGL *inScreen )
: Editor( inScreen ),
mStateDisplay( NULL ),
mUndoStack( MAX_UNDOS, false ),
// gen a fake ID
mCurrentWorkingStateID( makeUniqueID( (unsigned char*)"currRoom",
strlen( "currRoom" ) ) ) {
AppLog::info( "Constructing room editor\n" );
mCloseButton->setToolTip( "tip_closeEdit_room" );
/*
LabelGL *titleLabel = new LabelGL( 0.75, 0.5, 0.25,
0.1, "Room Editor", largeText );
mSidePanel->add( titleLabel );
*/
mSidePanel->add( mainTilePicker );
mainTilePicker->addActionListener( this );
mSidePanel->add( mainRoomPicker );
mainRoomPicker->addActionListener( this );
mEditTileButton =
new EditButtonGL(
mainTilePicker->getAnchorX() - 1,
mainTilePicker->getAnchorY() + mainTilePicker->getHeight() + 1,
8,
8 );
mSidePanel->add( mEditTileButton );
mEditTileButton->addActionListener( this );
mEditTileButton->setToolTip( "tip_edit_tile" );
/*
// 1-pixel wide white border
// inner panel to provide a 1-pixel wide black gap
BorderPanel *workingColorBorderPanel =
new BorderPanel( 256, 206, 48, 14,
new Color( 0, 0, 0, 1 ),
new Color( 1, 1, 1, 1 ),
1 );
// smaller to leave black gap
GUIPanelGL *workingColorPanel = new GUIPanelGL( 258, 208, 44, 10,
mWorkingColor );
mSidePanel->add( workingColorBorderPanel );
workingColorBorderPanel->add( workingColorPanel );
mAddButton = new AddButtonGL( 272, 184, 16, 16 );
mSidePanel->add( mAddButton );
mAddButton->addActionListener( this );
*/
double offset = P;
double buttonSize = P;
for( int y=0; y<G; y++ ) {
for( int x=0; x<G; x++ ) {
mButtonGrid[y][x] = new SpriteCellButtonGL(
NULL, 1.0,
8 + x * buttonSize,
gameWidth - ( 48 + offset + y * buttonSize ),
buttonSize,
buttonSize );
mMainPanel->add( mButtonGrid[y][x] );
mButtonGrid[y][x]->addActionListener( this );
}
}
// no stamp tool here
mToolSet = new DrawToolSet( 2, 42, "tip_pickTile", false );
mMainPanel->add( mToolSet );
EightPixelLabel *fieldLabel = new EightPixelLabel( 150, 54,
"roomName" );
mMainPanel->add( fieldLabel );
int fieldHeight = 8;
int fieldWidth = 8 * 10;
const char *defaultText = "default";
mSetNameField = new TextFieldGL( 150,
43,
fieldWidth,
fieldHeight,
1,
defaultText,
largeTextFixed,
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.15, 0.15, 0.15 ),
new Color( 0.75, 0.75, 0.75 ),
10,
false );
mMainPanel->add( mSetNameField );
mSetNameField->setFocus( true );
//mSetNameField->lockFocus( true );
mSetNameField->setCursorPosition( strlen( defaultText ) );
mSetNameField->addActionListener( this );
// center add button
double gridEdge = 8 + G * buttonSize;
double extra = gameHeight - gridEdge;
// center it vertically on room picker
double addY = mainRoomPicker->getAnchorY() +
mainRoomPicker->getHeight() - 15;
double sideButtonsX = gridEdge + (extra - 16) / 2;
mAddButton = new AddButtonGL( sideButtonsX,
addY,
16, 16 );
mMainPanel->add( mAddButton );
mAddButton->addActionListener( this );
mAddButton->setToolTip( "tip_addRoom" );
mAddAction = false;
double miniButtonSize = P + 4;
mMiniViewButton = new SpriteButtonGL(
NULL, 1,
gridEdge + ( extra - miniButtonSize ) / 2,
gameWidth / 2,
miniButtonSize,
miniButtonSize );
mMainPanel->add( mMiniViewButton );
mWallsButton = new SelectableButtonGL(
new Sprite( "walls.tga", true ),
1,
sideButtonsX - 2, mMiniViewButton->getAnchorY() + 30,
20, 20 );
mMainPanel->add( mWallsButton );
mWallsButton->setSelected( false );
mWallsButton->addActionListener( this );
mWallsButton->setToolTip( "tip_walls" );
mHintsButton = new SelectableButtonGL(
new Sprite( "hints.tga", true ),
1,
sideButtonsX - 2, mMiniViewButton->getAnchorY() + 60,
20, 20 );
mMainPanel->add( mHintsButton );
mHintsButton->setSelected( false );
mHintsButton->addActionListener( this );
mHintsButton->setToolTip( "tip_hints" );
double undoButtonY = gameWidth - ( 48 + offset + (G - 1) * buttonSize );
mUndoButton = new UndoButtonGL( sideButtonsX, undoButtonY, 16, 16 );
mMainPanel->add( mUndoButton );
mUndoButton->addActionListener( this );
mUndoButton->setEnabled( false );
mRedoButton = new RedoButtonGL( sideButtonsX, undoButtonY + 19, 16, 16 );
mMainPanel->add( mRedoButton );
mRedoButton->addActionListener( this );
mRedoButton->setEnabled( false );
mClearButton = new ClearButtonGL( sideButtonsX, addY - 20, 16, 16 );
mMainPanel->add( mClearButton );
mClearButton->addActionListener( this );
setRoomToEdit( mainRoomPicker->getSelectedResource() );
mPenDown = false;
GridOverlay *overlay = new GridOverlay(
8,
gameWidth - ( 48 + offset + (G - 1) * buttonSize ),
G * buttonSize, G * buttonSize,
G );
mMainPanel->add( overlay );
// do this later, after state display added
//postConstruction();
}
RoomEditor::~RoomEditor() {
mSidePanel->remove( mainTilePicker );
mSidePanel->remove( mainRoomPicker );
if( mStateDisplay != NULL ) {
mMainPanel->remove( mStateDisplay );
}
}
void RoomEditor::setGameStateDisplay( GameStateDisplay *inDisplay ) {
mStateDisplay = inDisplay;
mMainPanel->add( mStateDisplay );
// now ready to add top-layer GUI components
postConstruction();
}
void RoomEditor::takeOverGameStateDisplay() {
char showHints = mHintsButton->getSelected();
if( mStateDisplay != NULL ) {
mStateDisplay->setEnabled( showHints );
}
}
void RoomEditor::setRoomToEdit( Room inRoom ) {
// clear old working usages
removeUsages( mCurrentWorkingStateID );
mRoomToEdit = inRoom;
for( int y=0; y<G; y++ ) {
for( int x=0; x<G; x++ ) {
uniqueID tileID = mRoomToEdit.getTile( x, y );
Tile t( tileID );
mButtonGrid[y][x]->setSprite( t.getSprite() );
char *tileName = t.getName();
mButtonGrid[y][x]->setToolTip( tileName, false );
delete [] tileName;
if( mWallsButton->getSelected() ) {
mButtonGrid[y][x]->setHighlight( mRoomToEdit.getWall( x, y ) );
}
// add working usages
addUsage( mCurrentWorkingStateID, tileID );
}
}
mainTilePicker->recheckDeletable();
refreshMiniView();
char *name = mRoomToEdit.getRoomName();
mSetNameField->setText( name );
mSetNameField->setCursorPosition( strlen( name ) );
delete [] name;
}
void RoomEditor::refreshMiniView() {
// don't use cached version
mMiniViewButton->setSprite( mRoomToEdit.getSprite( false, false ) );
}
char RoomEditor::recursiveFill( int inX, int inY,
uniqueID inOldTile, uniqueID inNewTile,
drawTool inTool ) {
if( equal( mRoomToEdit.getTile( inX, inY ), inOldTile )
&&
!equal( mRoomToEdit.getTile( inX, inY ), inNewTile ) ) {
Tile t( inNewTile );
mButtonGrid[inY][inX]->setSprite( t.getSprite() );
char *tileName = t.getName();
mButtonGrid[inY][inX]->setToolTip( tileName, false );
delete [] tileName;
mRoomToEdit.editRoomTile( inX, inY, inNewTile );
// call on neighbors
if( inTool == fill || inTool == horLine ) {
if( inX > 0 ) {
recursiveFill( inX - 1, inY, inOldTile, inNewTile, inTool );
}
if( inX < G - 1 ) {
recursiveFill( inX + 1, inY, inOldTile, inNewTile, inTool );
}
}
if( inTool == fill || inTool == verLine ) {
if( inY > 0 ) {
recursiveFill( inX, inY - 1, inOldTile, inNewTile, inTool );
}
if( inY < G - 1 ) {
recursiveFill( inX, inY + 1, inOldTile, inNewTile, inTool );
}
}
return true;
}
return false;
}
char RoomEditor::recursiveFill( int inX, int inY,
char inOldWall, char inNewWall,
drawTool inTool ) {
if( mRoomToEdit.getWall( inX, inY ) == inOldWall
&&
mRoomToEdit.getWall( inX, inY ) != inNewWall ) {
mButtonGrid[inY][inX]->setHighlight( inNewWall );
mRoomToEdit.editRoomWall( inX, inY, inNewWall );
// call on neighbors
if( inTool == fill || inTool == horLine ) {
if( inX > 0 ) {
recursiveFill( inX - 1, inY, inOldWall, inNewWall, inTool );
}
if( inX < G - 1 ) {
recursiveFill( inX + 1, inY, inOldWall, inNewWall, inTool );
}
}
if( inTool == fill || inTool == verLine ) {
if( inY > 0 ) {
recursiveFill( inX, inY - 1, inOldWall, inNewWall, inTool );
}
if( inY < G - 1 ) {
recursiveFill( inX, inY + 1, inOldWall, inNewWall, inTool );
}
}
return true;
}
return false;
}
void RoomEditor::actionPerformed( GUIComponent *inTarget ) {
// superclass
Editor::actionPerformed( inTarget );
if( inTarget == mainTilePicker ) {
// new tile picked
}
else if( inTarget == mainRoomPicker ) {
// ignore if caused by our own Add action
if( ! mAddAction &&
! mainRoomPicker->wasLastActionFromPress() ) {
// will change room
mUndoStack.push_back( mRoomToEdit );
mUndoButton->setEnabled( true );
setRoomToEdit( mainRoomPicker->getSelectedResource() );
mainTilePicker->recheckDeletable();
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
}
}
else if( inTarget == mSetNameField ) {
mUndoStack.push_back( mRoomToEdit );
mUndoButton->setEnabled( true );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
mRoomToEdit.editRoomName( mSetNameField->getText() );
}
else if( inTarget == mAddButton ) {
addRoom();
}
else if( inTarget == mEditTileButton ) {
showTileEditor();
}
else if( inTarget == mUndoButton ) {
int lastIndex = mUndoStack.size() - 1;
Room last = *( mUndoStack.getElement( lastIndex ) );
mUndoStack.deleteElement( lastIndex );
if( mUndoStack.size() == 0 ) {
mUndoButton->setEnabled( false );
}
mRedoStack.push_back( mRoomToEdit );
mRedoButton->setEnabled( true );
setRoomToEdit( last );
}
else if( inTarget == mRedoButton ) {
int nextIndex = mRedoStack.size() - 1;
Room next = *( mRedoStack.getElement( nextIndex ) );
mRedoStack.deleteElement( nextIndex );
if( mRedoStack.size() == 0 ) {
mRedoButton->setEnabled( false );
}
mUndoStack.push_back( mRoomToEdit );
mUndoButton->setEnabled( true );
setRoomToEdit( next );
}
else if( inTarget == mWallsButton ) {
// toggle
mWallsButton->setSelected( ! mWallsButton->getSelected() );
char showWalls = mWallsButton->getSelected();
for( int y=0; y<G; y++ ) {
for( int x=0; x<G; x++ ) {
mButtonGrid[y][x]->setHighlight(
mRoomToEdit.getWall( x, y ) && showWalls );
}
}
}
else if( inTarget == mHintsButton ) {
// toggle
mHintsButton->setSelected( ! mHintsButton->getSelected() );
char showHints = mHintsButton->getSelected();
if( mStateDisplay != NULL ) {
mStateDisplay->setEnabled( showHints );
}
}
else if( inTarget == mClearButton ) {
mUndoStack.push_back( mRoomToEdit );
mUndoButton->setEnabled( true );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
// stick default everywhere
Tile t = Tile::getDefaultResource();
uniqueID id = t.getUniqueID();
for( int y=0; y<G; y++ ) {
for( int x=0; x<G; x++ ) {
// all walls off
mRoomToEdit.editRoomWall( x, y, false );
// okay even if wall visibility turned off
mButtonGrid[y][x]->setHighlight( false );
mButtonGrid[y][x]->setSprite( t.getSprite() );
char *tileName = t.getName();
mButtonGrid[y][x]->setToolTip( tileName, false );
delete [] tileName;
mRoomToEdit.editRoomTile( x, y, id );
}
}
refreshMiniView();
}
else {
// check grid
char found = false;
for( int y=0; y<G && !found; y++ ) {
for( int x=0; x<G && !found; x++ ) {
if( inTarget == mButtonGrid[y][x] ) {
found = true;
Room oldRoomState = mRoomToEdit;
char changed = false;
char release = false;
switch( mToolSet->getSelected() ) {
case pen: {
if( mWallsButton->getSelected() ) {
// edit walls with pen
if( !mPenDown ) {
// set ink until released
mWallInk = ! mRoomToEdit.getWall( x, y );
mPenDown = true;
changed = true;
}
// use ink already set
mRoomToEdit.editRoomWall( x, y, mWallInk );
mButtonGrid[y][x]->setHighlight(
mWallInk );
}
else {
// place tiles
Tile t =
mainTilePicker->getSelectedResource();
uniqueID id = t.getUniqueID();
if( ! equal( id,
mRoomToEdit.getTile( x, y ) ) ) {
mButtonGrid[y][x]->setSprite(
t.getSprite() );
char *tileName = t.getName();
mButtonGrid[y][x]->setToolTip( tileName,
false );
delete [] tileName;
mRoomToEdit.editRoomTile( x, y, id );
refreshMiniView();
// don't count single pen dots as undoable
// until pen is released
// save undo state only on on initial
// presses
if( !mPenDown ) {
mPenDown = true;
changed = true;
}
}
}
if( !mButtonGrid[y][x]->isPressed() ) {
// a release
mPenDown = false;
release = true;
}
}
break;
case horLine:
case verLine:
case fill:
if( ! mWallsButton->getSelected() ) {
changed = recursiveFill(
x, y,
mRoomToEdit.getTile( x, y ),
mainTilePicker->getSelectedResourceID(),
mToolSet->getSelected() );
refreshMiniView();
}
else {
if( !mPenDown ) {
mWallInk = ! mRoomToEdit.getWall( x, y );
}
changed = recursiveFill(
x, y,
mRoomToEdit.getWall( x, y ),
mWallInk,
mToolSet->getSelected() );
refreshMiniView();
}
if( !mPenDown ) {
mPenDown = true;
}
if( !mButtonGrid[y][x]->isPressed() ) {
// a release
mPenDown = false;
release = true;
}
break;
case pickColor: {
Tile t( mRoomToEdit.getTile( x, y ) );
mainTilePicker->setSelectedResource( t );
}
break;
case stamp:
AppLog::error(
"Error: unsupported stamp tool used in"
"RoomEditor\n" );
break;
}
if( changed ) {
// initial press, or step of fill
// we're certainly using the selected tile
addUsage( mCurrentWorkingStateID,
mainTilePicker->getSelectedResourceID() );
mainTilePicker->recheckDeletable();
mUndoStack.push_back( oldRoomState );
mUndoButton->setEnabled( true );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
}
if( release ) {
// will this be too slow? How else to track all
// usages in a consistent way?
// only doing this on release, so maybe okay?
removeUsages( mCurrentWorkingStateID );
for( int ty=0; ty<G; ty++ ) {
for( int tx=0; tx<G; tx++ ) {
addUsage( mCurrentWorkingStateID,
mRoomToEdit.getTile( tx, ty ) );
}
}
mainTilePicker->recheckDeletable();
}
}
}
}
}
}
void RoomEditor::editorClosing() {
mStateDisplay->setHintMode( false );
mStateDisplay->setEnabled( true );
addRoom();
}
void RoomEditor::addRoom() {
mAddAction = true;
mRoomToEdit.finishEdit();
mainRoomPicker->setSelectedResource( mRoomToEdit, true );
mainTilePicker->recheckDeletable();
mAddAction = false;
}
+114
View File
@@ -0,0 +1,114 @@
#ifndef ROOM_EDITOR_INCLUDED
#define ROOM_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "Room.h"
#include "GameStateDisplay.h"
#include "DrawToolSet.h"
#include "SizeLimitedVector.h"
#include "minorGems/graphics/openGL/gui/TextFieldGL.h"
class RoomEditor : public Editor {
public:
RoomEditor( ScreenGL *inScreen );
~RoomEditor();
// a bit messy, but this allows GameStateEditor and RoomEditor
// to share the same state display (so room editor can show
// object hints)
//
// Room Editor construction not complete until this has been called.
//
// We don't destroy this
void setGameStateDisplay( GameStateDisplay *inDisplay );
// called by GameStateEditor before it opens the RoomEditor
// tells us that the shared GameStateDisplay is now ours
void takeOverGameStateDisplay();
virtual void actionPerformed( GUIComponent *inTarget );
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
protected:
GameStateDisplay *mStateDisplay;
// triggered by add button or close
void addRoom();
AddButtonGL *mAddButton;
char mAddAction;
void setRoomToEdit( Room inRoom );
void refreshMiniView();
SpriteCellButtonGL *mButtonGrid[G][G];
SpriteButtonGL *mMiniViewButton;
Room mRoomToEdit;
EditButtonGL *mEditTileButton;
UndoButtonGL *mUndoButton;
RedoButtonGL *mRedoButton;
ClearButtonGL *mClearButton;
DrawToolSet *mToolSet;
SelectableButtonGL *mWallsButton;
SelectableButtonGL *mHintsButton;
// returns true if anything changed
char recursiveFill( int inX, int inY,
uniqueID inOldTile, uniqueID inNewTile,
drawTool inTool );
// version for wall toggles
char recursiveFill( int inX, int inY,
char inOldWall, char inNewWall,
drawTool inTool );
SizeLimitedVector<Room> mUndoStack;
SimpleVector<Room> mRedoStack;
TextFieldGL *mSetNameField;
char mPenDown;
char mWallInk;
// for usage tracking of objects in current state
uniqueID mCurrentWorkingStateID;
};
#endif
+17
View File
@@ -0,0 +1,17 @@
#include "RoomPicker.h"
// all share one stack
SimpleVector<uniqueID> globalRoomStack;
RoomPicker::RoomPicker(
double inAnchorX, double inAnchorY )
: ResourcePicker<Room>( inAnchorX, inAnchorY, &globalRoomStack ) {
}
RoomPicker::~RoomPicker() {
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef ROOM_PICKER_INCLUDED
#define ROOM_PICKER_INCLUDED
#include "Room.h"
#include "ResourcePicker.h"
// must include this to avoid linker errors
#include "ResourcePicker.cpp"
class RoomPicker : public ResourcePicker<Room> {
public:
/**
* Constructs a picker.
*
* @param inAnchorX the x position of the upper left corner
* of this component.
* @param inAnchorY the y position of the upper left corner
* of this component.
*
* Sets its own width and height automatically.
*/
RoomPicker( double inAnchorX, double inAnchorY );
virtual ~RoomPicker();
};
#endif
+356
View File
@@ -0,0 +1,356 @@
#include "Scale.h"
#include "resourceManager.h"
#include "imageCache.h"
#include "packSaver.h"
#include "minorGems/util/log/AppLog.h"
Color toneLowColor( 0, 85.0/255, 128.0/255 );
Color toneHighColor( 0, 170.0/255, 1.0 );
// static inits
Scale *Scale::sBlankScale = NULL;
void Scale::staticInit() {
sBlankScale = new Scale();
// ensure that blank timbre is saved at least once
sBlankScale->finishEdit();
}
void Scale::staticFree() {
delete sBlankScale;
}
void Scale::setupDefault() {
memset( (void *)mHalftonesOn, 0, HT );
// default to minor pentatonic
mHalftonesOn[0] = 1;
mHalftonesOn[3] = 1;
mHalftonesOn[5] = 1;
mHalftonesOn[7] = 1;
mHalftonesOn[10] = 1;
const char *defaultName = "default";
memcpy( mName, defaultName, strlen( defaultName ) + 1 );
makeUniqueID();
}
Scale::Scale() {
setupDefault();
}
char Scale::initFromData( unsigned char *inData, int inLength ) {
if( inLength >= mDataLength ) {
memcpy( (void *)mHalftonesOn, inData, HT );
inLength -= HT;
// remainder is name
if( inLength <= 11 ) {
memcpy( mName, &inData[mDataLength], inLength );
return true;
}
}
return false;
}
Scale::Scale( unsigned char *inData, int inLength )
: mID( defaultID ) {
if( !initFromData( inData, inLength ) ) {
// fail
setupDefault();
}
}
Scale::Scale( uniqueID inID )
: mID( inID ) {
int length;
char fromNetwork;
unsigned char *data = loadResourceData( "scale", mID, &length,
&fromNetwork );
if( data != NULL ) {
if( ! initFromData( data, length ) ) {
// failure
setupDefault();
}
delete [] data;
data = NULL;
if( fromNetwork ) {
// save to disk using the ID that we fetched it with
finishEdit( false );
}
}
else {
// scale failed to load
setupDefault();
}
if( data != NULL ) {
delete [] data;
}
}
void Scale::editScale( int inHalftone, char inToneOn ) {
mHalftonesOn[inHalftone] = (unsigned char)inToneOn;
}
char Scale::getToneOn( int inHalftone ) {
return mHalftonesOn[inHalftone];
}
int Scale::getNumOn() {
int sum = 0;
for( int y=0; y<HT; y++ ) {
if( mHalftonesOn[y] ) {
sum++;
}
}
return sum;
}
void Scale::editScaleName( const char *inName ) {
int newLength = strlen( inName );
if( newLength > 10 ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: Scale name %s is too long (10 max)\n", inName );
}
else {
memcpy( mName, inName, newLength + 1 );
}
}
#include "minorGems/util/stringUtils.h"
char *Scale::getScaleName() {
return stringDuplicate( mName );
}
#include "minorGems/util/SimpleVector.h"
unsigned char *Scale::makeBytes( int *outLength ) {
SimpleVector<unsigned char> dataAccum;
dataAccum.push_back( mHalftonesOn, HT );
dataAccum.push_back( (unsigned char *)mName,
strlen( mName ) + 1 );
*outLength = dataAccum.size();
return dataAccum.getElementArray();
}
void Scale::finishEdit( char inGenerateNewID ) {
uniqueID oldID = mID;
if( inGenerateNewID ) {
makeUniqueID();
}
if( ! equal( oldID, mID ) ||
! resourceExists( "scale", mID ) ) {
// change
int dataSize;
unsigned char *data = makeBytes( &dataSize );
saveResourceData( "scale", mID,
mName,
data, dataSize );
delete [] data;
}
}
void Scale::saveToPack() {
int dataSize;
unsigned char *data = makeBytes( &dataSize );
addToPack( "scale", mID,
mName,
data, dataSize );
delete [] data;
}
uniqueID Scale::getUniqueID() {
return mID;
}
const char *Scale::getResourceType() {
return "scale";
}
Scale Scale::getDefaultResource() {
return *( Scale::sBlankScale );
}
void Scale::makeUniqueID() {
partialUniqueID p = startUniqueID();
p = addToUniqueID( p, mHalftonesOn, HT );
p = addToUniqueID( p, (unsigned char*)mName, strlen( mName ) + 1 );
mID = ::makeUniqueID( p );
}
Image *Scale::getImage() {
Image *image = new Image( P, P, 3, true );
double *channels[3];
int i;
for( i=0; i<3; i++ ) {
channels[i] = image->getChannel( i );
}
Color *drawColorA = &toneLowColor;
Color *drawColorB = &toneHighColor;
for( int y=0; y<HT; y++ ) {
int pixY = HT - y - 1;
pixY += 2;
// diagonal
int x = y;
x += 2;
int pixel = pixY * P + x;
if( mHalftonesOn[y] ) {
Color *levelColor = Color::linearSum( drawColorB, drawColorA,
y / (float)HT );
for( i=0; i<3; i++ ) {
channels[i][pixel] = (*levelColor)[i];
}
delete levelColor;
}
}
return image;
}
Sprite *Scale::getSprite( char inUseTrans, char inCacheOK ) {
// ignore inUseTrans
if( inCacheOK ) {
Image *cachedImage = getCachedImage( mID, false );
if( cachedImage == NULL ) {
cachedImage = getImage();
addCachedImage( mID, false, cachedImage );
}
return new Sprite( cachedImage );
}
else {
// ignore cache
Image *image = getImage();
Sprite *sprite = new Sprite( image );
delete image;
return sprite;
}
}
void Scale::print() {
char *idString = getHumanReadableString( mID );
printf( "Scale %s:\n", idString );
delete [] idString;
printf( " name: %s\n", mName );
printf( " " );
for( int h=0; h<HT; h++ ) {
printf( "%c ", mHalftonesOn[h] );
}
printf( "\n" );
}
void Scale::setInPlayer( ) {
setScale( (char*)mHalftonesOn );
}
+114
View File
@@ -0,0 +1,114 @@
#ifndef SCALE_INCLUDED
#define SCALE_INCLUDED
#include "color.h"
#include "common.h"
#include "uniqueID.h"
#include "Sprite.h"
#include "musicPlayer.h"
// number of halftones
#define HT 12
class Scale {
public:
// blank music
Scale();
// music loaded from resource manager
Scale( uniqueID inID );
// music loaded from data string
Scale( unsigned char *inData, int inLength );
// changes halftone on/off
// halftone from 0..11
void editScale( int inHalftone, char inToneOn );
char getToneOn( int inHalftone );
int getNumOn();
// name has at most 10 chars
void editScaleName( const char *inName );
// destroyed by caller
char *getScaleName();
// finishes the edit, generates a new unique ID, saves result
void finishEdit( char inGenerateNewID=true );
// recursively saves to current resource pack
void saveToPack();
// get an image of this timbre
Image *getImage();
// implements ResourceType functions as needed by ResourcePicker
uniqueID getUniqueID();
Sprite *getSprite( char inUseTrans=false, char inCacheOK=true );
static const char *getResourceType();
static Scale getDefaultResource();
char *getName() {
return getScaleName();
}
// sets this scale into music player
void setInPlayer( );
void print();
// blank timbre (pure sine)
static Scale *sBlankScale;
static void staticInit();
static void staticFree();
protected:
void setupDefault();
char initFromData( unsigned char *inData, int inLength );
// result destroyed by caller
unsigned char *makeBytes( int *outLength );
void makeUniqueID();
unsigned char mHalftonesOn[HT];
const static int mDataLength = HT;
char mName[11];
uniqueID mID;
};
#endif
+360
View File
@@ -0,0 +1,360 @@
#include "ScaleEditor.h"
#include "ScalePicker.h"
#include "SongEditor.h"
#include "BorderPanel.h"
#include "labels.h"
#include "SelectionManager.h"
#include "packSaver.h"
#include "minorGems/util/log/AppLog.h"
extern ScalePicker *mainScalePicker;
extern SongEditor *mainSongEditor;
extern int gameWidth, gameHeight;
extern TextGL *largeTextFixed;
extern Color toneLowColor;
extern Color toneHighColor;
template <>
void SizeLimitedVector<Scale>::deleteElementOfType(
Scale inElement ) {
// no delete necessary
}
ScaleEditor::ScaleEditor( ScreenGL *inScreen )
: Editor( inScreen ),
mUndoStack( MAX_UNDOS, false ) {
mCloseButton->setToolTip( "tip_closeEdit_scale" );
mSidePanel->add( mainScalePicker );
mainScalePicker->addActionListener( this );
double offset = P;
double buttonSize = (gameHeight - 2 * offset - 8) / P;
for( int y=0; y<HT; y++ ) {
mScaleButtons[y] = new ScaleToggleButton(
new Sprite( "scaleSpace.tga", true ),
new Sprite( "note.tga", true ),
1,
8 + y * 16,
( 48 + (y + 1) * 16 ),
16,
16 );
Color *c = Color::linearSum( &toneLowColor, &toneHighColor,
(HT - y - 1) / (double)(HT - 1 ) );
mScaleButtons[y]->setColor( c );
mMainPanel->add( mScaleButtons[y] );
mScaleButtons[y]->addActionListener( this );
}
EightPixelLabel *fieldLabel = new EightPixelLabel( 150, 54,
"scaleName" );
mMainPanel->add( fieldLabel );
int fieldHeight = 8;
int fieldWidth = 8 * 10;
const char *defaultText = "default";
mNameField = new TextFieldGL( 150,
43,
fieldWidth,
fieldHeight,
1,
defaultText,
largeTextFixed,
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.75, 0.75, 0.75 ),
new Color( 0.15, 0.15, 0.15 ),
new Color( 0.75, 0.75, 0.75 ),
10,
false );
mMainPanel->add( mNameField );
mNameField->setFocus( true );
//mNameField->lockFocus( true );
mNameField->setCursorPosition( strlen( defaultText ) );
mNameField->addActionListener( this );
// center add button
double gridEdge = 8 + buttonSize * P;
double extra = gameHeight - gridEdge;
// center it vertically on scale picker
double addY = mainScalePicker->getAnchorY() +
mainScalePicker->getHeight() - 15;
double sideButtonsX = gridEdge + (extra - 16) / 2;
mAddButton = new AddButtonGL( sideButtonsX,
addY,
16, 16 );
mMainPanel->add( mAddButton );
mAddButton->addActionListener( this );
mAddButton->setToolTip( "tip_addScale" );
mAddAction = false;
double miniButtonSize = P + 4;
mMiniViewButton = new SpriteButtonGL(
NULL, 1,
gridEdge + ( extra - miniButtonSize ) / 2,
addY - 24,
miniButtonSize,
miniButtonSize );
mMainPanel->add( mMiniViewButton );
double undoButtonY = gameWidth - ( 48 + P * buttonSize );
mUndoButton = new UndoButtonGL( sideButtonsX, undoButtonY, 16, 16 );
mMainPanel->add( mUndoButton );
mUndoButton->addActionListener( this );
mUndoButton->setEnabled( false );
mRedoButton = new RedoButtonGL( sideButtonsX, undoButtonY + 19, 16, 16 );
mMainPanel->add( mRedoButton );
mRedoButton->addActionListener( this );
mRedoButton->setEnabled( false );
mPartToWatch = 0;
setScaleToEdit( mainScalePicker->getSelectedResource() );
postConstruction();
}
ScaleEditor::~ScaleEditor() {
mSidePanel->remove( mainScalePicker );
}
extern Color harmonicLowColor;
extern Color harmonicHighColor;
#include "musicPlayer.h"
void ScaleEditor::setPartToWatch( int inPartNumber ) {
mPartToWatch = inPartNumber;
int toneNumber = 0;
int numTones = mScaleToEdit.getNumOn();
for( int y=0; y<HT; y++ ) {
char toneOn = mScaleToEdit.getToneOn( y );
mScaleButtons[y]->setState( toneOn );
if( toneOn ) {
mScaleButtons[y]->setMusicInfo( toneNumber, numTones,
mPartToWatch );
toneNumber++;
}
else {
mScaleButtons[y]->setMusicInfo( -1, numTones, mPartToWatch );
}
}
}
void ScaleEditor::setScaleToEdit( Scale inScale ) {
mScaleToEdit = inScale;
// redo button note monitors
setPartToWatch( mPartToWatch );
refreshMiniView();
char *name = mScaleToEdit.getScaleName();
mNameField->setText( name );
mNameField->setCursorPosition( strlen( name ) );
delete [] name;
mScaleToEdit.setInPlayer();
if( mainSongEditor != NULL ) {
mainSongEditor->scaleChanged();
}
}
void ScaleEditor::refreshMiniView() {
// don't use cached version
mMiniViewButton->setSprite( mScaleToEdit.getSprite( false, false ) );
}
void ScaleEditor::saveUndoPoint() {
mUndoStack.push_back( mScaleToEdit );
mUndoButton->setEnabled( true );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
}
void ScaleEditor::actionPerformed( GUIComponent *inTarget ) {
// superclass
Editor::actionPerformed( inTarget );
if( inTarget == mainScalePicker ) {
Scale scalePicked = mainScalePicker->getSelectedResource();
if( ! mAddAction &&
! mainScalePicker->wasLastActionFromPress() ) {
// will change scale
mUndoStack.push_back( mScaleToEdit );
mUndoButton->setEnabled( true );
setScaleToEdit( scalePicked );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
}
}
else if( inTarget == mNameField ) {
mUndoStack.push_back( mScaleToEdit );
mUndoButton->setEnabled( true );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
mScaleToEdit.editScaleName( mNameField->getText() );
}
else if( inTarget == mAddButton ) {
addScale();
}
else if( inTarget == mUndoButton ) {
int lastIndex = mUndoStack.size() - 1;
Scale last = *( mUndoStack.getElement( lastIndex ) );
mUndoStack.deleteElement( lastIndex );
if( mUndoStack.size() == 0 ) {
mUndoButton->setEnabled( false );
}
mRedoStack.push_back( mScaleToEdit );
mRedoButton->setEnabled( true );
setScaleToEdit( last );
}
else if( inTarget == mRedoButton ) {
int nextIndex = mRedoStack.size() - 1;
Scale next = *( mRedoStack.getElement( nextIndex ) );
mRedoStack.deleteElement( nextIndex );
if( mRedoStack.size() == 0 ) {
mRedoButton->setEnabled( false );
}
mUndoStack.push_back( mScaleToEdit );
mUndoButton->setEnabled( true );
setScaleToEdit( next );
}
else {
// check scale buttons
char found = false;
for( int y=0; y<HT && !found; y++ ) {
if( inTarget == mScaleButtons[y] ) {
mUndoStack.push_back( mScaleToEdit );
mUndoButton->setEnabled( true );
// new branch... "redo" future now impossible
mRedoStack.deleteAll();
mRedoButton->setEnabled( false );
found = true;
int oldToneOn =
mScaleToEdit.getToneOn( y );
mScaleToEdit.editScale( y, !oldToneOn );
setScaleToEdit( mScaleToEdit );
}
}
}
}
void ScaleEditor::editorClosing() {
addScale();
}
void ScaleEditor::addScale() {
mAddAction = true;
mScaleToEdit.finishEdit();
mainScalePicker->setSelectedResource( mScaleToEdit, true );
mAddAction = false;
}
+75
View File
@@ -0,0 +1,75 @@
#ifndef SCALE_EDITOR_INCLUDED
#define SCALE_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "Scale.h"
#include "SizeLimitedVector.h"
#include "minorGems/graphics/openGL/gui/TextFieldGL.h"
class ScaleEditor : public Editor {
public:
ScaleEditor( ScreenGL *inScreen );
~ScaleEditor();
// for realtime highlights
void setPartToWatch( int inPart );
virtual void actionPerformed( GUIComponent *inTarget );
protected:
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
// triggered by add button or close
void addScale();
AddButtonGL *mAddButton;
char mAddAction;
void setScaleToEdit( Scale inScale );
void refreshMiniView();
void saveUndoPoint();
ScaleToggleButton *mScaleButtons[HT];
SpriteButtonGL *mMiniViewButton;
Scale mScaleToEdit;
UndoButtonGL *mUndoButton;
RedoButtonGL *mRedoButton;
SizeLimitedVector<Scale> mUndoStack;
SimpleVector<Scale> mRedoStack;
TextFieldGL *mNameField;
int mPartToWatch;
};
#endif
+18
View File
@@ -0,0 +1,18 @@
#include "ScalePicker.h"
// all share one stack
SimpleVector<uniqueID> globalScaleStack;
ScalePicker::ScalePicker(
double inAnchorX, double inAnchorY )
: ResourcePicker<Scale>( inAnchorX, inAnchorY,
&globalScaleStack ) {
}
ScalePicker::~ScalePicker() {
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef SCALE_PICKER_INCLUDED
#define SCALE_PICKER_INCLUDED
#include "Scale.h"
#include "ResourcePicker.h"
// must include this to avoid linker errors
#include "ResourcePicker.cpp"
class ScalePicker : public ResourcePicker<Scale> {
public:
/**
* Constructs a picker.
*
* @param inAnchorX the x position of the upper left corner
* of this component.
* @param inAnchorY the y position of the upper left corner
* of this component.
*
* Sets its own width and height automatically.
*/
ScalePicker( double inAnchorX, double inAnchorY );
virtual ~ScalePicker();
};
#endif
+876
View File
@@ -0,0 +1,876 @@
#include "Scene.h"
#include "resourceManager.h"
#include "usageDatabase.h"
#include "Room.h"
#include "StateObject.h"
#include "SpriteResource.h"
#include "imageCache.h"
#include "packSaver.h"
#include "minorGems/util/stringUtils.h"
#include "minorGems/util/log/AppLog.h"
static unsigned char sceneVersionNumber = 2;
// static inits
Scene *Scene::sBlankScene = NULL;
void Scene::staticInit() {
sBlankScene = new Scene();
// save once
sBlankScene->finishEdit();
}
void Scene::staticFree() {
delete sBlankScene;
}
void Scene::setupDefault() {
const char *defaultName = "default";
memcpy( mName, defaultName, strlen( defaultName ) + 1 );
mRoom = Room::sBlankRoom->getUniqueID();
mRoomTrans = 255;
mObjectZeroFrozen = false;
// okay that there are ZERO objects in the default,
// because when loaded against a blank game state, that game state
// will contain the default object position for Obj-Zero anyway,
// and we simply won't change it (because we have zero objects!)
// No need to duplicate default object position and other attributes here.
makeUniqueID();
}
Scene::Scene() {
setupDefault();
}
// room loaded from file
Scene::Scene( uniqueID inID )
: mID( inID ) {
int length;
char fromNetwork;
unsigned char *data = loadResourceData( "scene", mID, &length,
&fromNetwork );
if( data != NULL ) {
readFromBytes( data, length );
delete [] data;
if( fromNetwork ) {
// room might not be present locally
// force it to load
Room loadedRoom( mRoom );
// objects might not be present locally, either
// more efficient to load them in a chunk instead of with separate
// messages
int numObjects = mObjects.size();
if( numObjects > 0 ) {
uniqueID *ids = mObjects.getElementArray();
int *chunkLengths = new int[ numObjects ];
char *fromNetworkFlags = new char[ numObjects ];
unsigned char **chunks = loadResourceData( "object",
numObjects,
ids,
chunkLengths,
fromNetworkFlags );
for( int i=0; i<numObjects; i++ ) {
if( chunks[i] != NULL ) {
if( fromNetworkFlags[i] ) {
// make an object
StateObject o( ids[i],
chunks[i], chunkLengths[i], true );
// saves itself to disk, since fromNetwork flag
// passed to constructor
}
delete [] chunks[i];
}
}
delete [] ids;
delete [] fromNetworkFlags;
delete [] chunkLengths;
delete [] chunks;
}
// save Scene to disk using the ID that we fetched it with
finishEdit( false );
}
}
else {
setupDefault();
}
}
void Scene::saveToPack() {
Room room( mRoom );
room.saveToPack();
for( int i=0; i<mObjects.size(); i++ ) {
StateObject o( *( mObjects.getElement(i) ) );
o.saveToPack();
}
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
addToPack( "scene", mID, mName, bytes, numBytes );
delete [] bytes;
}
void Scene::editSceneName( const char *inName ) {
int newLength = strlen( inName );
if( newLength > 10 ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: Scene name %s is too long (10 max)\n", inName );
}
else {
memcpy( mName, inName, newLength + 1 );
}
}
char *Scene::getSceneName() {
return stringDuplicate( mName );
}
// finishes the edit, generates a new unique ID, saves result
void Scene::finishEdit( char inGenerateNewID ) {
uniqueID oldID = mID;
if( inGenerateNewID ) {
makeUniqueID();
}
if( !equal( oldID, mID ) ||
! resourceExists( "scene", mID ) ) {
// change
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
saveResourceData( "scene", mID, mName, bytes, numBytes );
delete [] bytes;
// track usages
addUsage( mID, mRoom );
int numObjects = mObjects.size();
for( int j=0; j<numObjects; j++ ) {
addUsage( mID, *( mObjects.getElementFast( j ) ) );
}
}
}
uniqueID Scene::getUniqueID() {
return mID;
}
#include "minorGems/graphics/converters/PNGImageConverter.h"
#include "minorGems/io/file/File.h"
#include "minorGems/io/file/FileOutputStream.h"
Sprite *Scene::getSprite( char inUseTrans, char inCacheOK ) {
Image *cachedImage = NULL;
if( inCacheOK ) {
cachedImage = getCachedImage( mID, inUseTrans );
}
if( cachedImage == NULL ) {
int imageW = P*G;
// zero image
Image fullImage( imageW, imageW, 4, true );
//int imageCenter = (P*G)/2;
// track largest rectangle area that is not transparent
int lowX = imageW;
int hiX = 0;
int lowY = imageW;
int hiY = 0;
// first, lay room in
Room room( mRoom );
float roomTrans = mRoomTrans / 255.0f;
for( int ty=0; ty<G; ty++ ) {
for( int tx=0; tx<G; tx++ ) {
uniqueID tID = room.getTile( tx, ty );
// skip blanks
if( !equal( tID, Room::sBlankTile->getUniqueID() ) ) {
Tile t( tID );
Image *tImage = t.getImage();
for( int py=0; py<P; py++ ) {
int fullY = py + ty * P;
for( int px=0; px<P; px++ ) {
int fullX = px + tx * P;
Color c = tImage->getColor( py * P + px );
// darken based on trans
c.r *= roomTrans;
c.g *= roomTrans;
c.b *= roomTrans;
fullImage.setColor( fullY * imageW + fullX,
c );
if( fullX < lowX ) {
lowX = fullX;
}
if( fullX > hiX ) {
hiX = fullX;
}
if( fullY < lowY ) {
lowY = fullY;
}
if( fullY > hiY ) {
hiY = fullY;
}
}
}
delete tImage;
}
}
}
/*
// FIXME lay tile sprites in... actually, should be okay to lay in
// big 16x16 blocks of color (taken from room thumbnail sprite)
// because that is what would happen with area mapping reduction anyway
Image *roomImage = room.getImage();
double *roomChannels[3] =
{ roomImage->getChannel( 0 ),
roomImage->getChannel( 1 ),
roomImage->getChannel( 2 ) };
double *fullChannels[4] =
{ fullImage.getChannel( 0 ),
fullImage.getChannel( 1 ),
fullImage.getChannel( 2 ),
fullImage.getChannel( 3 ) };
double roomTrans = mRoomTrans / 255.0f;
for( int y=0; y<imageW; y++ ) {
int gy = y / P;
for( int x=0; x<imageW; x++ ) {
int gx = x / P;
// image is PxP, even though room is only GxG
// padded with black
int gi = gy * P + gx;
int fullI = y * imageW + x;
// ignore black areas
if( roomChannels[0][gi] > 0 ||
roomChannels[1][gi] > 0 ||
roomChannels[2][gi] > 0 ) {
// track areas hit with color
if( x < lowX ) {
lowX = x;
}
if( x > hiX ) {
hiX = x;
}
if( y < lowY ) {
lowY = y;
}
if( y > hiY ) {
hiY = y;
}
for( int c=0; c<3; c++ ) {
// darken based on trans
fullChannels[c][fullI] =
roomTrans * roomChannels[c][gi];
}
fullChannels[3][fullI] = 1.0f;
}
}
}
delete roomImage;
*/
int numObjects = mObjects.size();
for( int j=0; j<numObjects; j++ ) {
StateObject obj( *( mObjects.getElement( j ) ) );
intPair objOffset = *( mObjectOffsets.getElement( j ) );
float objTrans = *( mObjectTrans.getElement( j ) ) / 255.0f;
int numLayers = obj.getNumLayers();
for( int i=0; i<numLayers; i++ ) {
SpriteResource r( obj.getLayerSprite( i ) );
intPair layerOffset = obj.getLayerOffset( i );
float layerTrans = obj.getLayerTrans( i ) / 255.0f;
layerTrans *= objTrans;
// force trans for sub-sprites, or else weird bg color overlap
// results
Image *spriteImage = r.getImage( true );
int spriteCenter = P/2;
for( int y=0; y<P; y++ ) {
for( int x=0; x<P; x++ ) {
int fullY = y - spriteCenter - layerOffset.y -
objOffset.y;
fullY += imageW - spriteCenter;
//fullY += imageCenter;
int fullX = x - spriteCenter + layerOffset.x +
objOffset.x;
fullX += spriteCenter;
//fullX += imageCenter;
if( fullY < imageW && fullY >= 0
&&
fullX < imageW && fullX >= 0 ) {
int index = y * P + x;
Color c = spriteImage->getColor( index );
float colorTrans = c.a * layerTrans;
if( colorTrans > 0 ) {
int fullIndex = fullY * imageW + fullX;
Color oldColor =
fullImage.getColor( fullIndex );
Color *blend;
if( oldColor.a > 0 ) {
blend = Color::linearSum(
&c, &( oldColor ),
colorTrans );
}
else {
blend = c.copy();
blend->a = colorTrans;
}
// overwrite with blend of ink from higher
// layer
fullImage.setColor( fullIndex, *blend );
delete blend;
if( fullX < lowX ) {
lowX = fullX;
}
if( fullX > hiX ) {
hiX = fullX;
}
if( fullY < lowY ) {
lowY = fullY;
}
if( fullY > hiY ) {
hiY = fullY;
}
}
}
}
}
delete spriteImage;
}
}
/*
PNGImageConverter png;
File pngFile( NULL, "fullTest.png" );
FileOutputStream out( &pngFile );
png.formatImage( &fullImage, &out );
*/
int subW = hiX - lowX + 1;
int subH = hiY - lowY + 1;
Image *subImage = fullImage.getSubImage( lowX, lowY,
subW, subH );
//printf( "subimage = x,y=(%d,%d), w,h=(%d,%d)\n",
// lowX, lowY, subW, subH );
// build up sums here
Image *shrunkImage = new Image( P, P, 4, true );
double *subChannels[4];
double *shrunkChannels[4];
int hitCount[P*P];
memset( hitCount, 0, P*P*sizeof(int) );
for( int c=0; c<4; c++ ) {
subChannels[c] = subImage->getChannel( c );
shrunkChannels[c] = shrunkImage->getChannel( c );
}
double scaleFactor;
if( subW > subH ) {
scaleFactor = P / (double)subW;
}
else {
scaleFactor = P / (double)subH;
}
if( scaleFactor > 1 ) {
// P big enough to contain whole scene
scaleFactor = 1;
}
// center in shrunk image
int xExtra = (int)( P - ( scaleFactor * subW ) );
int yExtra = (int)( P - ( scaleFactor * subH ) );
int xOffset = xExtra / 2;
int yOffset = yExtra / 2;
for( int y=0; y<subH; y++ ) {
int shrunkY = (int)( y * scaleFactor ) + yOffset;
for( int x=0; x<subW; x++ ) {
int shrunkX = (int)( x * scaleFactor ) + xOffset;
int shrunkIndex = shrunkY * P + shrunkX;
int subIndex = y * subW + x;
// skip transparent pixels
if( subChannels[3][subIndex] > 0 ) {
hitCount[shrunkIndex] ++;
for( int c=0; c<3; c++ ) {
shrunkChannels[c][shrunkIndex] +=
subChannels[c][subIndex];
}
// anything hit by non-trans color is opaque
shrunkChannels[3][shrunkIndex] = 1;
}
}
}
for( int c=0; c<3; c++ ) {
for( int i=0; i<P*P; i++ ) {
// transparent pixels hit 0 times
if( hitCount[i] > 0 ) {
shrunkChannels[c][i] /= hitCount[i];
}
}
}
// sharp transparency already computed above
// (skipped trans pixels in sums)
delete subImage;
cachedImage = shrunkImage;
if( inCacheOK ) {
addCachedImage( mID, inUseTrans, cachedImage );
}
}
Sprite *sprite = new Sprite( cachedImage );
if( !inCacheOK ) {
// image not in cache... we must destroy it
delete cachedImage;
}
return sprite;
}
const char *Scene::getResourceType() {
return "scene";
}
Scene Scene::getDefaultResource() {
return *sBlankScene;
}
#include "minorGems/util/SimpleVector.h"
unsigned char *Scene::makeBytes( int *outLength ) {
SimpleVector<unsigned char> buffer;
buffer.push_back( (unsigned char *)SID_MAGIC_CODE,
strlen( SID_MAGIC_CODE ) );
buffer.push_back( sceneVersionNumber );
buffer.push_back( mRoom.bytes, U );
buffer.push_back( mRoomTrans );
buffer.push_back( (unsigned char)mObjectZeroFrozen );
unsigned char numObjects = (unsigned char)mObjects.size();
// one byte for num objects (max 255 objects)
buffer.push_back( numObjects );
for( int i=0; i<numObjects; i++ ) {
buffer.push_back( mObjects.getElement(i)->bytes, U );
intPair *p = mObjectOffsets.getElement( i );
// full precision
buffer.push_back( getChars( *p ), 8 );
p = mSpeechOffsets.getElement( i );
buffer.push_back( getChars( *p ), 8 );
buffer.push_back( *( mSpeechFlipFlags.getElement( i ) ) );
buffer.push_back( *( mSpeechBoxFlags.getElement( i ) ) );
buffer.push_back( *( mLockedFlags.getElement( i ) ) );
buffer.push_back( *( mObjectTrans.getElement( i ) ) );
}
buffer.push_back( (unsigned char *)mName, strlen( mName ) + 1 );
*outLength = buffer.size();
return buffer.getElementArray();
}
void Scene::readFromBytes( unsigned char *inBytes, int inLength ) {
unsigned char version = 1;
// check for magic code before version number
const char *code = SID_MAGIC_CODE;
int codeLength = strlen( code );
if( inLength >= codeLength ) {
char codeFound = true;
for( int i=0; i<codeLength && codeFound; i++ ) {
if( inBytes[i] != code[i] ) {
codeFound = false;
}
}
if( codeFound ) {
// next byte is file version number
version = inBytes[codeLength];
// skip them
inBytes = &( inBytes[ codeLength + 1 ] );
inLength -= ( codeLength + 1 );
}
// else no version number, data starts right at beginning
}
int numUsed;
mRoom = readUniqueID( inBytes, inLength, &numUsed );
if( numUsed < 0 ) {
AppLog::error( "Error: reading Scene's room uniqueID failed.\n" );
return;
}
inBytes = &( inBytes[ numUsed ] );
inLength -= numUsed;
if( inLength < 0 ) {
return;
}
mRoomTrans = inBytes[0];
inBytes = &( inBytes[ 1 ] );
inLength --;
if( inLength < 0 ) {
return;
}
mObjectZeroFrozen = inBytes[0];
inBytes = &( inBytes[ 1 ] );
inLength --;
if( inLength < 0 ) {
return;
}
int numObjects = inBytes[0];
inBytes = &( inBytes[ 1 ] );
inLength --;
int objDataLength = U+8+8+1+1+1+1;
if( version < 2 ) {
objDataLength = U+8+8+1+1;
}
if( inLength < objDataLength * numObjects ) {
AppLog::error( "Error: Data stream too short to contain scene.\n" );
return;
}
for( int i=0; i<numObjects; i++ ) {
uniqueID id = readUniqueID( inBytes, inLength, &numUsed );
if( numUsed < 0 ) {
AppLog::error( "Error: reading Scene's object uniqueID failed.\n" );
return;
}
mObjects.push_back( id );
inBytes = &( inBytes[ numUsed ] );
inLength -= numUsed;
// full precision
intPair p = readIntPair( inBytes, inLength,
&numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse Scene from message\n" );
return;
}
inLength -= numUsed;
inBytes = &( inBytes[ numUsed ] );
mObjectOffsets.push_back( p );
// full precision
p = readIntPair( inBytes, inLength,
&numUsed );
if( numUsed == -1 ) {
AppLog::error( "Failed to parse Scene from message\n" );
return;
}
inLength -= numUsed;
inBytes = &( inBytes[ numUsed ] );
mSpeechOffsets.push_back( p );
mSpeechFlipFlags.push_back( inBytes[0] );
inBytes = &( inBytes[ 1 ] );
inLength -= 1;
if( version >= 2 ) {
mSpeechBoxFlags.push_back( inBytes[0] );
inBytes = &( inBytes[ 1 ] );
inLength -= 1;
mLockedFlags.push_back( inBytes[0] );
inBytes = &( inBytes[ 1 ] );
inLength -= 1;
}
else {
// fill in with dummy data
mSpeechBoxFlags.push_back( 0 );
mLockedFlags.push_back( 0 );
}
mObjectTrans.push_back( inBytes[0] );
inBytes = &( inBytes[ 1 ] );
inLength -= 1;
}
// remainder is name
if( inLength <= 11 ) {
memcpy( mName, inBytes, inLength );
}
}
void Scene::makeUniqueID() {
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
mID = ::makeUniqueID( bytes, numBytes );
delete [] bytes;
}
void Scene::print() {
char *idString = getHumanReadableString( mID );
printf( "Scene %s:\n", idString );
delete [] idString;
for( int i=0; i<mObjects.size(); i++ ) {
idString = getHumanReadableString( *( mObjects.getElement(i) ) );
intPair *p = mObjectOffsets.getElement( i );
printf( " Object: %s, (%d,%d)\n",
idString, p->x, p->y );
delete [] idString;
}
printf( "\n" );
}
+106
View File
@@ -0,0 +1,106 @@
#ifndef SCENE_INCLUDED
#define SCENE_INCLUDED
#include "color.h"
#include "common.h"
#include "uniqueID.h"
#include "Sprite.h"
#include "minorGems/util/SimpleVector.h"
class Scene {
public:
// room filled with blank tiles and default name
Scene();
// room loaded from file
Scene( uniqueID inID );
// name has at most 10 chars
void editSceneName( const char *inName );
char *getSceneName();
// finishes the edit, generates a new unique ID, saves result
void finishEdit( char inGenerateNewID=true );
// recursively saves to current resource pack
void saveToPack();
// implements ResourceType functions as needed by ResourcePicker
uniqueID getUniqueID();
Sprite *getSprite( char inUseTrans=false, char inCacheOK=true );
static const char *getResourceType();
static Scene getDefaultResource();
char *getName() {
return getSceneName();
}
// blank object
static Scene *sBlankScene;
static void staticInit();
static void staticFree();
void print();
// not part of state that is saved to disk
// used only by editor
int mSelectedLayer;
// these are public to avoid needed to write a bunch of set/get
// functions
uniqueID mRoom;
unsigned char mRoomTrans;
char mObjectZeroFrozen;
SimpleVector<uniqueID> mObjects;
SimpleVector<intPair> mObjectOffsets;
SimpleVector<intPair> mSpeechOffsets;
SimpleVector<char> mSpeechFlipFlags;
SimpleVector<char> mSpeechBoxFlags;
SimpleVector<char> mLockedFlags;
SimpleVector<unsigned char> mObjectTrans;
protected:
void setupDefault();
// result destroyed by caller
unsigned char *makeBytes( int *outLength );
void readFromBytes( unsigned char *inBytes, int inLength );
void makeUniqueID();
char mName[11];
uniqueID mID;
};
#endif
+17
View File
@@ -0,0 +1,17 @@
#include "ScenePicker.h"
// all share one stack
SimpleVector<uniqueID> globalSceneStack;
ScenePicker::ScenePicker(
double inAnchorX, double inAnchorY )
: ResourcePicker<Scene>( inAnchorX, inAnchorY, &globalSceneStack ) {
}
ScenePicker::~ScenePicker() {
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef SCENE_PICKER_INCLUDED
#define SCENE_PICKER_INCLUDED
#include "Scene.h"
#include "ResourcePicker.h"
// must include this to avoid linker errors
#include "ResourcePicker.cpp"
class ScenePicker : public ResourcePicker<Scene> {
public:
/**
* Constructs a picker.
*
* @param inAnchorX the x position of the upper left corner
* of this component.
* @param inAnchorY the y position of the upper left corner
* of this component.
*
* Sets its own width and height automatically.
*/
ScenePicker( double inAnchorX, double inAnchorY );
virtual ~ScenePicker();
};
#endif
+96
View File
@@ -0,0 +1,96 @@
#include "SelectionManager.h"
char SelectionManager::sSelection[P][P];
rgbaColor SelectionManager::sColor[P][P];
char SelectionManager::sTrans[P][P];
static rgbaColor black = { {0,0,0,255} };
void SelectionManager::clearSelection() {
for( int y=0; y<P; y++ ) {
for( int x=0; x<P; x++ ) {
sSelection[y][x] = false;
sColor[y][x] = black;
sTrans[y][x] = false;
}
}
}
char SelectionManager::isInSelection( int inX, int inY ) {
return sSelection[inY][inX];
}
rgbaColor SelectionManager::getColor( int inX, int inY ) {
return sColor[inY][inX];
}
char SelectionManager::getTrans( int inX, int inY ) {
return sTrans[inY][inX];
}
void SelectionManager::toggleSelection( int inX, int inY, char inSelected ) {
sSelection[inY][inX] = inSelected;
}
void SelectionManager::setColor( int inX, int inY, rgbaColor inColor ) {
sColor[inY][inX] = inColor;
}
void SelectionManager::setTrans( int inX, int inY, char inTrans ) {
sTrans[inY][inX] = inTrans;
}
intPair SelectionManager::getSelectionCenter( char inIgnoreTrans ) {
int minX = P;
int maxX = 0;
int minY = P;
int maxY = 0;
for( int y=0; y<P; y++ ) {
for( int x=0; x<P; x++ ) {
if( sSelection[y][x]
&&
( !inIgnoreTrans || !sTrans[y][x] ) ) {
if( x < minX ) {
minX = x;
}
if( y < minY ) {
minY = y;
}
if( x > maxX ) {
maxX = x;
}
if( y > maxY ) {
maxY = y;
}
}
}
}
intPair result = { ( minX + maxX ) / 2,
( minY + maxY ) / 2 };
return result;
}
+43
View File
@@ -0,0 +1,43 @@
#include "common.h"
#include "color.h"
// maintains a global selection on a PxP grid
class SelectionManager {
public:
static void clearSelection();
static char isInSelection( int inX, int inY );
static rgbaColor getColor( int inX, int inY );
static char getTrans( int inX, int inY );
static void toggleSelection( int inX, int inY, char inSelected );
static void setColor( int inX, int inY, rgbaColor inColor );
static void setTrans( int inX, int inY, char inTrans );
// if trans ignored, center computed based on colored pixels only
static intPair getSelectionCenter( char inIgnoreTrans = false );
protected:
static char sSelection[P][P];
static rgbaColor sColor[P][P];
static char sTrans[P][P];
};
+103
View File
@@ -0,0 +1,103 @@
#ifndef SIZE_LIMITED_VECTOR_INCLUDED
#define SIZE_LIMITED_VECTOR_INCLUDED
#include "minorGems/util/SimpleVector.h"
// vector that trims earlier elements if size passes double of maxSize
template <class Type>
class SizeLimitedVector : public SimpleVector<Type> {
public:
// inDeleteWhenCulling true to perform a "delete" operation
// on culled elements before discarding them (should be set
// when vector contains pointers)
SizeLimitedVector( int inLimit, char inDeleteWhenCulling );
// override
void push_back( Type x );
protected:
int mLimit;
char mDeleteWhenCulling;
// must be implemented specially for each instantiation types
// used to destroy elements when culling excess in vector
//
// can do nothing if no element destruction necessary
// Only called if inDeleteWhenCulling is set in constructor
void deleteElementOfType( Type inElement );
};
template <class Type>
inline SizeLimitedVector<Type>::SizeLimitedVector( int inLimit,
char inDeleteWhenCulling )
: mLimit( inLimit ),
mDeleteWhenCulling( inDeleteWhenCulling ) {
}
template <class Type>
inline void SizeLimitedVector<Type>::push_back( Type x ) {
SimpleVector<Type>::push_back( x );
int oldNum = SimpleVector<Type>::size();
if( oldNum >= mLimit * 2 ) {
printf( "Passed limit of %d with %d elements...\n",
mLimit, oldNum );
// cull back down to mLimit elements by removing excess
int numToRemove = oldNum - mLimit;
if( mDeleteWhenCulling ) {
for( int i=0; i<numToRemove; i++ ) {
// delete values being removed
//delete SimpleVector<Type>::elements[i];
deleteElementOfType( SimpleVector<Type>::elements[i] );
}
}
for( int i=numToRemove; i<oldNum; i++ ) {
// note that this will invoke assignment operators on elements
// which is what we need
SimpleVector<Type>::elements[i - numToRemove] =
SimpleVector<Type>::elements[i];
}
SimpleVector<Type>::numFilledElements -= numToRemove;
printf( "...After culling, %d left\n", SimpleVector<Type>::size() );
}
}
#endif
+686
View File
@@ -0,0 +1,686 @@
#include "Song.h"
#include "Music.h"
#include "TimbreResource.h"
#include "Scale.h"
#include "resourceManager.h"
#include "usageDatabase.h"
#include "uniqueID.h"
#include "imageCache.h"
#include "packSaver.h"
#include "minorGems/util/stringUtils.h"
#include "minorGems/util/log/AppLog.h"
// static inits
Song *Song::sBlankSong = NULL;
void Song::staticInit() {
sBlankSong = new Song();
// save once
sBlankSong->finishEdit();
}
void Song::staticFree() {
delete sBlankSong;
}
void Song::setupDefault() {
const char *defaultName = "default";
memcpy( mName, defaultName, strlen( defaultName ) + 1 );
uniqueID blankID = Music::sBlankMusic->getUniqueID();
int x;
int y;
for( y=0; y<SI; y++ ) {
mRowLengths[y] = 0;
mTimbres[y] = TimbreResource::sBlankTimbre->getUniqueID();
for( x=0; x<S; x++ ) {
mPhrases[y][x] = blankID;
}
mRowLoudness[y] = 255;
mRowStereo[y] = 128;
}
mScale = Scale::sBlankScale->getUniqueID();
// normal, default
mSpeed = 1;
makeUniqueID();
}
Song::Song() {
setupDefault();
}
// song loaded from file
Song::Song( uniqueID inID )
: mID( inID ) {
int length;
char fromNetwork;
unsigned char *data = loadResourceData( "song", mID, &length,
&fromNetwork );
if( data != NULL ) {
// save this one
initFromData( data, length, fromNetwork, true );
delete [] data;
data = NULL;
}
else {
setupDefault();
}
}
Song::Song( uniqueID inID, unsigned char *inData, int inLength,
char inFromNetwork, char inSaveWholeSong )
: mID( inID ) {
initFromData( inData, inLength, inFromNetwork, inSaveWholeSong );
}
void Song::initFromData( unsigned char *inData, int inLength,
char inFromNetwork, char inSaveWholeSong ) {
readFromBytes( inData, inLength );
if( inFromNetwork ) {
// phrases might not be present locally, either
// more efficient to load them in a chunk instead of with separate
// messages
uniqueID ids[SI*S];
int i=0;
for( int ty=0; ty<SI; ty++ ) {
for( int tx=0; tx<S; tx++ ) {
ids[i] = mPhrases[ty][tx];
i++;
}
}
int chunkLengths[SI*S];
char fromNetworkFlags[SI*S];
unsigned char **chunks = loadResourceData( "music",
SI*S,
ids,
chunkLengths,
fromNetworkFlags );
for( int i=0; i<SI*S; i++ ) {
if( chunks[i] != NULL ) {
if( fromNetworkFlags[i] ) {
// make a phrase
Music m( ids[i], chunks[i], chunkLengths[i] );
// force into disk cache, don't remake ID
m.finishEdit( false );
}
delete [] chunks[i];
}
}
delete [] chunks;
i=0;
for( int ty=0; ty<SI; ty++ ) {
ids[i] = mTimbres[ty];
i++;
}
chunks = loadResourceData( "timbre",
SI,
ids,
chunkLengths,
fromNetworkFlags );
for( int i=0; i<SI; i++ ) {
if( chunks[i] != NULL ) {
if( fromNetworkFlags[i] ) {
// make a timbre
TimbreResource t( ids[i], chunks[i], chunkLengths[i] );
// force into disk cache, don't remake ID
t.finishEdit( false );
}
delete [] chunks[i];
}
}
delete [] chunks;
// force scale to be saved
Scale s( mScale );
if( inSaveWholeSong ) {
// save song to disk using the ID that we fetched it with
finishEdit( false );
}
}
}
void Song::editSongPhrase( int inX, int inY, uniqueID inPhraseID ) {
mPhrases[inY][inX] = inPhraseID;
}
void Song::editRowLength( int inY, int inLength ){
mRowLengths[inY] = (unsigned char)inLength;
}
void Song::editTimbre( int inY, uniqueID inTimbreID ) {
mTimbres[inY] = inTimbreID;
}
void Song::editRowLoudness( int inY, int inLoudness ){
mRowLoudness[inY] = (unsigned char)inLoudness;
}
void Song::editRowStereo( int inY, int inStereo ){
mRowStereo[inY] = (unsigned char)inStereo;
}
void Song::editScale( uniqueID inScale ) {
mScale = inScale;
}
void Song::editSpeed( int inSpeed ) {
mSpeed = (unsigned char)inSpeed;
}
void Song::editSongName( const char *inName ) {
int newLength = strlen( inName );
if( newLength > 10 ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: Song name %s is too long (10 max)\n", inName );
}
else {
memcpy( mName, inName, newLength + 1 );
}
}
uniqueID Song::getPhrase( int inX, int inY ) {
return mPhrases[inY][inX];
}
int Song::getRowLength( int inY ) {
return mRowLengths[inY];
}
int Song::getRowLoudness( int inY ) {
return mRowLoudness[inY];
}
int Song::getRowStereo( int inY ) {
return mRowStereo[inY];
}
uniqueID Song::getTimbre( int inY ) {
return mTimbres[inY];
}
uniqueID Song::getScale() {
return mScale;
}
int Song::getSpeed() {
return mSpeed;
}
char *Song::getSongName() {
return stringDuplicate( mName );
}
// finishes the edit, generates a new unique ID, saves result
void Song::finishEdit( char inGenerateNewID ) {
uniqueID oldID = mID;
if( inGenerateNewID ) {
makeUniqueID();
}
if( !equal( oldID, mID ) ||
! resourceExists( "song", mID ) ) {
// change
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
saveResourceData( "song", mID, mName, bytes, numBytes );
delete [] bytes;
// track usages
addUsage( mID, mScale );
for( int y=0; y<SI; y++ ) {
addUsage( mID, mTimbres[y] );
for( int x=0; x<S; x++ ) {
addUsage( mID, mPhrases[y][x] );
}
}
}
}
void Song::saveToPack() {
for( int py=0; py<SI; py++ ) {
for( int px=0; px<S; px++ ) {
Music m( mPhrases[py][px] );
m.saveToPack();
}
TimbreResource t( mTimbres[py] );
t.saveToPack();
}
Scale s( mScale );
s.saveToPack();
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
addToPack( "song", mID, mName, bytes, numBytes );
delete [] bytes;
}
uniqueID Song::getUniqueID() {
return mID;
}
Image *Song::getImage() {
// compose all phrase images into a sprite with 1 pixel per phrase
// zero to prepare for average
Image *fullImage = new Image( P, P, 3, true );
double *fullC[3] =
{ fullImage->getChannel( 0 ),
fullImage->getChannel( 1 ),
fullImage->getChannel( 2 ) };
for( int py=0; py<SI; py++ ) {
// color by timbre
TimbreResource timbre( getTimbre( py ) );
Color timbreColor = toColor( timbre.getTimbreColor() );
for( int px=0; px<S; px++ ) {
int fullP = (py + 1) * P + (px + 1);
Music m( mPhrases[py][px] );
Image *tImage = m.getImage();
double *tC[4] =
{ tImage->getChannel( 0 ),
tImage->getChannel( 1 ),
tImage->getChannel( 2 ),
tImage->getChannel( 3 ) };
// compute average of all non-trans phrase pixels
for( int c=0; c<3; c++ ) {
// sum first
int numNonTrans = 0;
for( int i=0; i<P*P; i++ ) {
if( tC[3][i] > 0 ) {
fullC[c][fullP] += tC[c][i];
numNonTrans ++;
}
}
if( numNonTrans > 0 ) {
fullC[c][fullP] /= numNonTrans;
fullC[c][fullP] *= (timbreColor)[c];
}
else {
fullC[c][fullP] = 0;
}
}
delete tImage;
}
}
return fullImage;
}
Sprite *Song::getSprite( char inUseTrans, char inCacheOK ) {
// ignore inUseTrans
Image *cachedImage = NULL;
if( inCacheOK ) {
cachedImage = getCachedImage( mID, false );
}
if( cachedImage == NULL ) {
cachedImage = getImage();
if( inCacheOK ) {
addCachedImage( mID, false, cachedImage );
}
}
Sprite *sprite = new Sprite( cachedImage );
if( !inCacheOK ) {
// image not in cache... we must destroy it
delete cachedImage;
}
return sprite;
}
const char *Song::getResourceType() {
return "song";
}
Song Song::getDefaultResource() {
return *sBlankSong;
}
#include "minorGems/util/SimpleVector.h"
unsigned char *Song::makeBytes( int *outLength ) {
SimpleVector<unsigned char> buffer;
int y, x;
for( y=0; y<SI; y++ ) {
for( x=0; x<S; x++ ) {
buffer.push_back( mPhrases[y][x].bytes, U );
}
}
for( y=0; y<SI; y++ ) {
buffer.push_back( mRowLengths[y] );
}
for( y=0; y<SI; y++ ) {
buffer.push_back( mTimbres[y].bytes, U );
}
for( y=0; y<SI; y++ ) {
buffer.push_back( mRowLoudness[y] );
}
for( y=0; y<SI; y++ ) {
buffer.push_back( mRowStereo[y] );
}
buffer.push_back( mScale.bytes, U );
buffer.push_back( mSpeed );
buffer.push_back( (unsigned char *)mName, strlen( mName ) + 1 );
*outLength = buffer.size();
return buffer.getElementArray();
}
void Song::readFromBytes( unsigned char *inBytes, int inLength ) {
int numBytesUsed;
char success = readUniqueIDs( (uniqueID *)mPhrases, SI*S,
inBytes, inLength,
&numBytesUsed );
if( success ) {
inBytes = &( inBytes[ numBytesUsed ] );
inLength -= numBytesUsed;
}
if( inLength >= SI ) {
memcpy( (void *)mRowLengths, inBytes, SI );
inBytes = &( inBytes[ SI ] );
inLength -= SI;
}
success = readUniqueIDs( (uniqueID *)mTimbres, SI,
inBytes, inLength,
&numBytesUsed );
if( success ) {
inBytes = &( inBytes[ numBytesUsed ] );
inLength -= numBytesUsed;
}
if( inLength >= SI ) {
memcpy( (void *)mRowLoudness, inBytes, SI );
inBytes = &( inBytes[ SI ] );
inLength -= SI;
}
if( inLength >= SI ) {
memcpy( (void *)mRowStereo, inBytes, SI );
inBytes = &( inBytes[ SI ] );
inLength -= SI;
}
mScale = readUniqueID( inBytes, inLength,
&numBytesUsed );
if( numBytesUsed == U ) {
inBytes = &( inBytes[ numBytesUsed ] );
inLength -= numBytesUsed;
}
if( inLength > 0 ) {
mSpeed = inBytes[0];
inBytes = &( inBytes[ 1 ] );
inLength -= 1;
}
// remainder is name
if( inLength <= 11 ) {
memcpy( mName, inBytes, inLength );
}
}
void Song::makeUniqueID() {
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
mID = ::makeUniqueID( bytes, numBytes );
delete [] bytes;
}
#include "musicPlayer.h"
extern char noteToggles[PARTS][S][N][N];
extern int partLengths[PARTS];
extern double partLoudness[PARTS];
extern double partStereo[PARTS];
void Song::setInPlayer( Song inOldSong, Song inNewSong, char inForceUpdate ) {
// timbre updates are slow... only do the ones we need
uniqueID oldTimbres[SI];
for( int y=0; y<SI; y++ ) {
oldTimbres[y] = inOldSong.getTimbre( y );
}
// update ALL on a scale change or speed change
uniqueID oldScale = inOldSong.getScale();
uniqueID newScale = inNewSong.getScale();
char scaleChange = ( !equal( oldScale, newScale ) ) || inForceUpdate;
if( scaleChange ) {
Scale s( newScale );
s.setInPlayer();
}
char speedChange = ( inOldSong.getSpeed() != inNewSong.getSpeed() )
|| inForceUpdate;
if( speedChange ) {
setSpeed( inNewSong.getSpeed() );
}
// speed change forces everything to update
for( int y=0; y<SI; y++ ) {
partLengths[y] = inNewSong.getRowLength( y );
TimbreResource t( inNewSong.getTimbre( y ) );
for( int x=0; x<S; x++ ) {
Music m( inNewSong.getPhrase( x, y ) );
if( inNewSong.getRowLength( y ) > x ) {
for( int py=0; py<N; py++ ) {
for( int px=0; px<N; px++ ) {
noteToggles[y][x][py][px] =
m.getNoteOn( px, py );
}
}
}
else {
// turn rest off
for( int py=0; py<N; py++ ) {
for( int px=0; px<N; px++ ) {
noteToggles[y][x][py][px] = false;
}
}
}
}
if( !equal( t.getUniqueID(), oldTimbres[y] ) ) {
// a true timbre change, set both timbre and envelope
t.setInPlayer( y, true, true );
}
else if( speedChange ) {
// just a speed change, keep timbres
t.setInPlayer( y, false, true );
}
else if( scaleChange ) {
// just a scale change, keep envelopes
t.setInPlayer( y, true, false );
}
partLoudness[y] = inNewSong.getRowLoudness( y ) / 255.0;
partStereo[y] = inNewSong.getRowStereo( y ) / 255.0;
}
}
+162
View File
@@ -0,0 +1,162 @@
#ifndef SONG_INCLUDED
#define SONG_INCLUDED
#include "color.h"
#include "common.h"
#include "uniqueID.h"
#include "Sprite.h"
// length of a song
#define S 9
// number of instruments (rows) in a song
#define SI 8
class Song {
public:
// empty song with default name
Song();
// song loaded from file
Song( uniqueID inID );
// song loaded from data string
Song( uniqueID inID, unsigned char *inData, int inLength,
char inFromNetwork, char inSaveWholeSong );
// changes the tile in a given grid location
void editSongPhrase( int inX, int inY, uniqueID inMusicID );
void editRowLength( int inY, int inLength );
void editTimbre( int inY, uniqueID inTimbreID );
// 0-255
void editRowLoudness( int inY, int inLoudness );
// 0 = left, 127=center, 255=right
void editRowStereo( int inY, int inStereo );
void editScale( uniqueID inScale );
// 0 slow, 1 normal, 2 fast
void editSpeed( int inSpeed );
// name has at most 10 chars
void editSongName( const char *inName );
// gets ID of tile on grid
uniqueID getPhrase( int inX, int inY );
int getRowLength( int inY );
uniqueID getTimbre( int inY );
int getRowLoudness( int inY );
int getRowStereo( int inY );
uniqueID getScale();
int getSpeed();
char *getSongName();
// finishes the edit, generates a new unique ID, saves result
void finishEdit( char inGenerateNewID=true );
// recursively saves to current resource pack
void saveToPack();
// image version of getSprite, never fetched from cache
Image *getImage();
// implements ResourceType functions as needed by ResourcePicker
uniqueID getUniqueID();
Sprite *getSprite( char inUseTrans=false, char inCacheOK=true );
static const char *getResourceType();
static Song getDefaultResource();
char *getName() {
return getSongName();
}
// blank song
static Song *sBlankSong;
static void staticInit();
static void staticFree();
// result destroyed by caller
unsigned char *makeBytes( int *outLength );
// sets a new song into music player, only updating parts that have
// changed (for efficiency)
static void setInPlayer( Song inOldSong, Song inNewSong,
char inForceUpdate = false );
protected:
void setupDefault();
void initFromData( unsigned char *inData, int inLength,
char inFromNetwork, char inSaveWholeSong );
void readFromBytes( unsigned char *inBytes, int inLength );
void makeUniqueID();
// all phrases are exactly one grid square in size
uniqueID mPhrases[SI][S];
unsigned char mRowLengths[SI];
uniqueID mTimbres[SI];
unsigned char mRowLoudness[SI];
unsigned char mRowStereo[SI];
uniqueID mScale;
unsigned char mSpeed;
char mName[11];
uniqueID mID;
};
#endif
File diff suppressed because it is too large Load Diff
+129
View File
@@ -0,0 +1,129 @@
#ifndef SONG_EDITOR_INCLUDED
#define SONG_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "Song.h"
#include "DrawToolSet.h"
#include "SizeLimitedVector.h"
#include "ToolTipSliderGL.h"
#include "minorGems/graphics/openGL/gui/TextFieldGL.h"
class SongEditor : public Editor {
public:
SongEditor( ScreenGL *inScreen );
~SongEditor();
// may not be saved into picker yet
Song getLiveSong();
// tell editor to re-generate all timbres
void scaleChanged();
virtual void actionPerformed( GUIComponent *inTarget );
protected:
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
// triggered by add button or close
void addSong();
void saveUndoPoint();
AddButtonGL *mAddButton;
char mAddAction;
void setSongToEdit( Song inSong );
void refreshMiniView();
MusicCellButtonGL *mButtonGrid[SI][S];
Sprite *mEmptyPhraseSprite;
SpriteButtonGL *mTimbreButtons[SI];
EditButtonGL *mEditTimbreButtons[SI];
ToolTipSliderGL *mLoudnessSliders[SI];
ToolTipSliderGL *mStereoSliders[SI];
SpriteButtonGL *mMiniViewButton;
Song mSongToEdit;
EditButtonGL *mEditMusicButton;
SpriteButtonGL *mEditScaleButton;
SelectableButtonGL *mSpeedButtons[3];
UndoButtonGL *mUndoButton;
RedoButtonGL *mRedoButton;
ClearButtonGL *mClearButton;
DrawToolSet *mToolSet;
SelectableButtonGL *mWallsButton;
SelectableButtonGL *mEraseButton;
// returns true if anything changed
char recursiveFill( int inX, int inY,
uniqueID inOldMusic, uniqueID inNewMusic,
drawTool inTool );
// version for erasing
char recursiveErase( int inX, int inY,
uniqueID inOldMusic,
drawTool inTool );
SizeLimitedVector<Song> mUndoStack;
SimpleVector<Song> mRedoStack;
TextFieldGL *mSetNameField;
ToggleSpriteButtonGL *mAddToPackButton;
SpriteButtonGL *mSavePackButton;
char mPenDown;
int mLastRowTouched;
char mIgnoreSliders;
char mFirstSongSet;
// for usage tracking of phrases in current state
uniqueID mCurrentWorkingPhrasesID;
// for SEPARATE usage tracking of timbres in current state
uniqueID mCurrentWorkingTimbresID;
};
#endif
+17
View File
@@ -0,0 +1,17 @@
#include "SongPicker.h"
// all share one stack
SimpleVector<uniqueID> globalSongStack;
SongPicker::SongPicker(
double inAnchorX, double inAnchorY )
: ResourcePicker<Song>( inAnchorX, inAnchorY, &globalSongStack ) {
}
SongPicker::~SongPicker() {
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef SONG_PICKER_INCLUDED
#define SONG_PICKER_INCLUDED
#include "Song.h"
#include "ResourcePicker.h"
// must include this to avoid linker errors
#include "ResourcePicker.cpp"
class SongPicker : public ResourcePicker<Song> {
public:
/**
* Constructs a picker.
*
* @param inAnchorX the x position of the upper left corner
* of this component.
* @param inAnchorY the y position of the upper left corner
* of this component.
*
* Sets its own width and height automatically.
*/
SongPicker( double inAnchorX, double inAnchorY );
virtual ~SongPicker();
};
#endif
+249
View File
@@ -0,0 +1,249 @@
#include "Sprite.h"
#include "common.h"
#include "minorGems/math/geometry/Angle3D.h"
#include "minorGems/util/log/AppLog.h"
Sprite::Sprite( const char *inImageFileName,
char inTransparentLowerLeftCorner,
int inNumFrames,
int inNumPages ) {
Image *spriteImage = readTGA( inImageFileName );
if( spriteImage == NULL ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Failed to read image file: %s\n", inImageFileName );
// soft fail: default image
spriteImage = new Image( 16, 16, 3, true );
}
initTexture( spriteImage, inTransparentLowerLeftCorner, inNumFrames,
inNumPages );
delete spriteImage;
}
Sprite::Sprite( Image *inImage,
char inTransparentLowerLeftCorner,
int inNumFrames,
int inNumPages ) {
initTexture( inImage, inTransparentLowerLeftCorner, inNumFrames,
inNumPages );
}
void Sprite::initTexture( Image *inImage,
char inTransparentLowerLeftCorner,
int inNumFrames,
int inNumPages ) {
mNumFrames = inNumFrames;
mNumPages = inNumPages;
Image *spriteImage = inImage;
Image *imageToDelete = NULL;
if( spriteImage->getNumChannels() == 3 && inTransparentLowerLeftCorner ) {
// use lower-left corner color as transparent color for alpha
Image *fourChannelImage = new Image( spriteImage->getWidth(),
spriteImage->getHeight(),
4, false );
// copy first three
for( int c=0; c<3; c++ ) {
fourChannelImage->pasteChannel( spriteImage->getChannel( c ),
c );
}
double *r = fourChannelImage->getChannel( 0 );
double *g = fourChannelImage->getChannel( 1 );
double *b = fourChannelImage->getChannel( 2 );
// index of transparency
int tI =
fourChannelImage->getWidth() *
( fourChannelImage->getHeight() - 1 );
// color of transparency
double tR = r[tI];
double tG = g[tI];
double tB = b[tI];
double *alpha = fourChannelImage->getChannel( 3 );
int numPixels =
fourChannelImage->getWidth() *
fourChannelImage->getHeight();
for( int i=0; i<numPixels; i++ ) {
if( r[i] == tR &&
g[i] == tG &&
b[i] == tB ) {
alpha[i] = 0;
}
else {
alpha[i] = 1;
}
}
spriteImage = fourChannelImage;
imageToDelete = fourChannelImage;
}
mTexture = new SingleTextureGL( spriteImage,
// no wrap
false );
mBaseScaleX = spriteImage->getWidth() / mNumPages;
mBaseScaleY = spriteImage->getHeight() / mNumFrames;
if( imageToDelete != NULL ) {
delete imageToDelete;
}
mFlipHorizontal = false;
mHorizontalOffset = 0;
mCurrentPage = 0;
}
Sprite::~Sprite() {
delete mTexture;
}
// opt (found with profiler)
// only construct these once, not every draw call
Vector3D corners[4];
void Sprite::draw( int inFrame,
double inRotation, Vector3D *inPosition,
double inScale,
double inFadeFactor,
Color *inColor ) {
/*
printf( "Drawing sprite %d, r%f, (%f,%f), s%f, f%f\n",
(int)(this), inRotation, inPosition->mX, inPosition->mY, inScale,
inFadeFactor );
*/
// profiler opt:
// this is expensive, and the game never needs it
// (all frame references are specific and never auto-cycling)
// inFrame = inFrame % mNumFrames;
double xRadius = inScale * mBaseScaleX / 2;
double yRadius = inScale * mBaseScaleY / 2;
double xOffset = mHorizontalOffset * inScale;
if( mFlipHorizontal ) {
xRadius = -xRadius;
}
// first, set up corners relative to 0,0
// loop is unrolled here, with all offsets added in
// also, mZ ignored now, since rotation no longer done
double posX = inPosition->mX + xOffset;
double posY = inPosition->mY;
corners[0].mX = posX - xRadius;
corners[0].mY = posY - yRadius;
//corners[0].mZ = 0;
corners[1].mX = posX + xRadius;
corners[1].mY = posY - yRadius;
//corners[1].mZ = 0;
corners[2].mX = posX + xRadius;
corners[2].mY = posY + yRadius;
//corners[2].mZ = 0;
corners[3].mX = posX - xRadius;
corners[3].mY = posY + yRadius;
//corners[3].mZ = 0;
// int i;
// now rotate around center
// then add inPosition so that center is at inPosition
// Found with profiler: ignore rotation, since game doesn't use it anyway.
// Angle3D rot( 0, 0, inRotation );
// found with profiler:
// unroll this loop
/*
for( i=0; i<4; i++ ) {
corners[i].mX += xOffset;
// corners[i].rotate( &rot );
corners[i].add( inPosition );
}
*/
if( inColor != NULL ) {
glColor4f( inColor->r, inColor->g, inColor->b, inFadeFactor );
}
else {
glColor4f( 1, 1, 1, inFadeFactor );
}
mTexture->enable();
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
double textXA = (1.0 / mNumPages) * mCurrentPage;
double textXB = textXA + (1.0 / mNumPages );
double textYB = (1.0 / mNumFrames) * inFrame;
double textYA = textYB + (1.0 / mNumFrames );
glBegin( GL_QUADS ); {
glTexCoord2f( textXA, textYA );
glVertex2d( corners[0].mX, corners[0].mY );
glTexCoord2f( textXB, textYA );
glVertex2d( corners[1].mX, corners[1].mY );
glTexCoord2f( textXB, textYB );
glVertex2d( corners[2].mX, corners[2].mY );
glTexCoord2f( textXA, textYB );
glVertex2d( corners[3].mX, corners[3].mY );
}
glEnd();
mTexture->disable();
}
+105
View File
@@ -0,0 +1,105 @@
#ifndef SPRITE_INCLUDED
#define SPRITE_INCLUDED
#include "minorGems/graphics/openGL/SingleTextureGL.h"
#include "minorGems/graphics/Color.h"
#include "minorGems/math/geometry/Vector3D.h"
#include <stdlib.h>
class Sprite{
public:
// transparent color for RGB images can be taken from lower-left
// corner pixel
// lower-left corner ignored for RGBA images
// image split vertically into inNumFrames of equal size
Sprite( const char *inImageFileName,
char inTransparentLowerLeftCorner = false,
int inNumFrames = 1,
int inNumPages = 1 );
Sprite( Image *inImage,
char inTransparentLowerLeftCorner = false,
int inNumFrames = 1,
int inNumPages = 1 );
~Sprite();
void draw( int inFrame,
double inRotation, Vector3D *inPosition,
double inScale = 1,
double inFadeFactor = 1,
Color *inColor = NULL );
int getNumFrames() {
return mNumFrames;
}
void setFlipHorizontal( char inFlip ) {
mFlipHorizontal = inFlip;
}
// used to adjust horizontal center point, in pixels
void setHorizontalOffset( double inOffset ) {
mHorizontalOffset = inOffset;
}
void setPage( int inPage ) {
mCurrentPage = inPage;
}
int getPage() {
return mCurrentPage;
}
double getBaseScaleX() {
return mBaseScaleX;
}
double getBaseScaleY() {
return mBaseScaleY;
}
protected:
SingleTextureGL *mTexture;
int mNumFrames;
int mNumPages;
double mBaseScaleX;
double mBaseScaleY;
char mFlipHorizontal;
double mHorizontalOffset;
int mCurrentPage;
void initTexture( Image *inImage,
char inTransparentLowerLeftCorner = false,
int inNumFrames = 1,
int inNumPages = 1 );
};
#endif
File diff suppressed because it is too large Load Diff
+121
View File
@@ -0,0 +1,121 @@
#ifndef SPRITE_EDITOR_INCLUDED
#define SPRITE_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "SpriteResource.h"
#include "DrawToolSet.h"
#include "TransformToolSet.h"
#include "SizeLimitedVector.h"
#include "minorGems/graphics/openGL/gui/TextFieldGL.h"
class SpriteEditor : public Editor {
public:
SpriteEditor( ScreenGL *inScreen );
~SpriteEditor();
virtual void actionPerformed( GUIComponent *inTarget );
protected:
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
// triggered by add button or close
void addSprite();
// override from Editor
virtual void colorEditorClosed();
AddButtonGL *mAddButton;
char mAddAction;
void setSpriteToEdit( SpriteResource inSprite );
void refreshMiniView();
HighlightColorButtonGL *mButtonGrid[P][P];
TwoSpriteButtonGL *mMiniViewButton;
SpriteResource mSpriteToEdit;
EditButtonGL *mEditColorButton;
EditButtonGL *mEditPaletteButton;
UndoButtonGL *mUndoButton;
RedoButtonGL *mRedoButton;
TransformToolSet *mTransformToolSet;
DrawToolSet *mToolSet;
SelectableButtonGL *mEraseButton;
SelectableButtonGL *mSelectionButton;
SelectableButtonGL *mPickerTransButton;
// returns true if anything changed
char recursiveFill( int inX, int inY, rgbaColor inOldColor,
char inOldTrans,
rgbaColor inNewColor,
drawTool inTool );
// version for transparency
char recursiveFill( int inX, int inY,
rgbaColor inOldColor,
char inOldTrans, char inNewTrans,
drawTool inTool );
// version for selection
char recursiveSelectionFill( int inX, int inY,
rgbaColor inOldColor,
char inOldTrans,
char inOldSelection,
char inNewSelection,
drawTool inTool );
void toggleErase();
void toggleSelection();
void clearStampOverlay();
SizeLimitedVector<SpriteResource> mUndoStack;
SimpleVector<SpriteResource> mRedoStack;
TextFieldGL *mNameField;
char mPenDown;
char mTransInk;
char mSelectionInk;
};
#endif
+111
View File
@@ -0,0 +1,111 @@
#include "SpritePicker.h"
#include "TilePicker.h"
extern TilePicker *mainTilePicker;
// all share one stack
SimpleVector<uniqueID> globalSpriteStack;
SpritePicker::SpritePicker(
double inAnchorX, double inAnchorY )
: ResourcePicker<SpriteResource>( inAnchorX, inAnchorY,
&globalSpriteStack ) {
mainTilePicker->addActionListener( this );
mTransButton = new SelectableButtonGL(
new Sprite( "transSmall.tga", true ),
1,
mAnchorX + 2 * (mWidth - 12) / 3 + 2,
mAnchorY,
12, 12 );
add( mTransButton );
mTransButton->setSelected( true );
mTransButton->addActionListener( this );
mTransButton->setToolTip( "tip_trans" );
}
SpritePicker::~SpritePicker() {
}
Sprite *SpritePicker::getButtonGridBackground() {
if( mTransButton->getSelected() ) {
return mainTilePicker->getBackgroundTile().getSprite();
}
else {
// no trans
return NULL;
}
}
void SpritePicker::actionPerformed( GUIComponent *inTarget ) {
if( inTarget == mainTilePicker ) {
// tile changed, new background sprite in result display
// generate new result sprites
getFreshResults();
refreshSelectedResourceSprite();
}
else if( inTarget == mTransButton ) {
// toggle
mTransButton->setSelected( ! mTransButton->getSelected() );
getFreshResults();
refreshSelectedResourceSprite();
}
else {
ResourcePicker<SpriteResource>::actionPerformed( inTarget );
}
}
void SpritePicker::cloneState(
ResourcePicker<SpriteResource> *inOtherPicker ) {
SpritePicker *other = (SpritePicker *)inOtherPicker;
other->mTransButton->setSelected( mTransButton->getSelected() );
other->getFreshResults();
other->refreshSelectedResourceSprite();
ResourcePicker<SpriteResource>::cloneState( inOtherPicker );
}
char SpritePicker::isTransOn() {
return mTransButton->getSelected();
}
SelectableButtonGL *SpritePicker::getTransButton() {
return mTransButton;
}
+71
View File
@@ -0,0 +1,71 @@
#ifndef SPRITE_PICKER_INCLUDED
#define SPRITE_PICKER_INCLUDED
#include "SpriteResource.h"
#include "ResourcePicker.h"
// must include this to avoid linker errors
#include "ResourcePicker.cpp"
#include "buttons.h"
class SpritePicker : public ResourcePicker<SpriteResource> {
public:
/**
* Constructs a picker.
*
* @param inAnchorX the x position of the upper left corner
* of this component.
* @param inAnchorY the y position of the upper left corner
* of this component.
*
* Sets its own width and height automatically.
*/
SpritePicker( double inAnchorX, double inAnchorY );
virtual ~SpritePicker();
// override this so we can listen to tile-picker too
virtual void actionPerformed( GUIComponent *inTarget );
// override to copy trans button state
virtual void cloneState(
ResourcePicker<SpriteResource> *inOtherPicker );
// gets state of trans button
char isTransOn();
// so that others can add action listeners to it
SelectableButtonGL *getTransButton();
protected:
// override, returning current tile as background to show
// behind transparent sprites
Sprite *getButtonGridBackground();
// to toggle sprite trans
SelectableButtonGL *mTransButton;
};
#endif
+531
View File
@@ -0,0 +1,531 @@
#include "SpriteResource.h"
#include "resourceManager.h"
#include "imageCache.h"
#include "packSaver.h"
#include "minorGems/util/log/AppLog.h"
// static inits
SpriteResource *SpriteResource::sBlankSprite = NULL;
void SpriteResource::staticInit() {
sBlankSprite = new SpriteResource();
// ensure that blank sprite is saved at least once
sBlankSprite->finishEdit();
/*
printf( "Generating random sprites...\n" );
// code to generate lots of random sprites
for( int i=0; i<50000; i++ ) {
if( i%1000 == 0 ) {
printf( "...%d...\n", i );
}
SpriteResource r;
r.editSpriteName( "manymany" );
// use uniqueID as a random num generator
uniqueID randID = ::makeUniqueID( (unsigned char*)(&i),
sizeof( int ) );
rgbaColor c;
c.comp.r = randID.bytes[0];
c.comp.g = randID.bytes[1];
c.comp.b = randID.bytes[2];
c.comp.a = 255;
r.editSprite( randID.bytes[3] % P,
randID.bytes[4] % P,
c );
r.finishEdit();
}
printf( "...done\n" );
*/
}
void SpriteResource::staticFree() {
delete sBlankSprite;
}
void SpriteResource::setupDefault() {
memset( (void *)mPixelColors, 0, mPixelDataLength );
// default sprite is fully trans
memset( (void *)mTransFlags, true, mTransDataLength );
/*
mPixelColors[0][0].comp.r = 255;
mPixelColors[0][0].comp.a = 255;
mPixelColors[0][1].comp.g = 255;
mPixelColors[0][1].comp.a = 255;
*/
const char *defaultName = "default";
memcpy( mSetName, defaultName, strlen( defaultName ) + 1 );
makeUniqueID();
}
SpriteResource::SpriteResource() {
setupDefault();
}
char SpriteResource::initFromData( unsigned char *inData, int inLength ) {
if( inLength >= mPixelDataLength ) {
memcpy( (void *)mPixelColors, inData, mPixelDataLength );
inLength -= mPixelDataLength;
inData = &( inData[ mPixelDataLength ] );
if( inLength >= mTransDataLength ) {
memcpy( (void*)mTransFlags, inData, mTransDataLength );
inLength -= mTransDataLength;
inData = &( inData[mTransDataLength] );
// remainder is name
if( inLength <= 11 ) {
memcpy( mSetName, inData, inLength );
return true;
}
}
}
return false;
}
SpriteResource::SpriteResource( uniqueID inID,
unsigned char *inData, int inLength )
: mID( inID ) {
if( !initFromData( inData, inLength ) ) {
// fail
setupDefault();
}
}
SpriteResource::SpriteResource( uniqueID inID )
: mID( inID ) {
int length;
char fromNetwork;
unsigned char *data = loadResourceData( "sprite", mID, &length,
&fromNetwork );
if( data != NULL ) {
if( !initFromData( data, length ) ) {
// fail
setupDefault();
}
delete [] data;
data = NULL;
if( fromNetwork ) {
// save to disk using the ID that we fetched it with
finishEdit( false );
}
}
else {
// load failed
setupDefault();
}
if( data != NULL ) {
delete [] data;
}
}
void SpriteResource::editSprite( int inX, int inY, rgbaColor inNewColor ) {
mPixelColors[inY][inX] = inNewColor;
}
rgbaColor SpriteResource::getColor( int inX, int inY ) {
return mPixelColors[inY][inX];
}
void SpriteResource::editSpriteName( const char *inName ) {
int newLength = strlen( inName );
if( newLength > 10 ) {
AppLog::getLog()->logPrintf(
Log::ERROR_LEVEL,
"Error: Sprite name %s is too long (10 max)\n", inName );
}
else {
memcpy( mSetName, inName, newLength + 1 );
}
}
void SpriteResource::editTrans( int inX, int inY, char inIsTrans ){
mTransFlags[inY][inX] = inIsTrans;
}
char SpriteResource::getTrans( int inX, int inY ) {
return mTransFlags[inY][inX];
}
#include "minorGems/util/stringUtils.h"
char *SpriteResource::getSpriteName() {
return stringDuplicate( mSetName );
}
#include "minorGems/util/SimpleVector.h"
void SpriteResource::finishEdit( char inGenerateNewID ) {
uniqueID oldID = mID;
if( inGenerateNewID ) {
makeUniqueID();
}
if( ! equal( oldID, mID ) ||
! resourceExists( "sprite", mID ) ) {
// change
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
saveResourceData( "sprite", mID,
mSetName,
bytes, numBytes );
delete [] bytes;
}
}
unsigned char *SpriteResource::makeBytes( int *outLength ) {
SimpleVector<unsigned char> dataAccum;
dataAccum.push_back( (unsigned char *)mPixelColors,
mPixelDataLength );
dataAccum.push_back( (unsigned char *)mTransFlags,
mTransDataLength );
dataAccum.push_back( (unsigned char *)mSetName,
strlen( mSetName ) + 1 );
*outLength = dataAccum.size();
return dataAccum.getElementArray();
}
void SpriteResource::saveToPack() {
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
addToPack( "sprite", mID,
mSetName,
bytes, numBytes );
delete [] bytes;
}
uniqueID SpriteResource::getUniqueID() {
return mID;
}
const char *SpriteResource::getResourceType() {
return "sprite";
}
SpriteResource SpriteResource::getDefaultResource() {
return *( SpriteResource::sBlankSprite );
}
void SpriteResource::makeUniqueID() {
partialUniqueID p = startUniqueID();
p = addToUniqueID( p,
(unsigned char*)mPixelColors, mPixelDataLength );
p = addToUniqueID( p,
(unsigned char*)mTransFlags, mTransDataLength );
p = addToUniqueID( p, (unsigned char*)mSetName, strlen( mSetName ) + 1 );
mID = ::makeUniqueID( p );
}
Image *SpriteResource::getImage( char inUseTrans ) {
Image *image = new Image( P, P, 4, false );
double *channels[4];
int i;
for( i=0; i<4; i++ ) {
channels[i] = image->getChannel( i );
}
for( int y=0; y<P; y++ ) {
for( int x=0; x<P; x++ ) {
int pixel = y * P + x;
rgbaColor c = mPixelColors[y][x];
for( i=0; i<3; i++ ) {
channels[i][pixel] = c.bytes[i] / 255.0;
}
if( mTransFlags[y][x] && inUseTrans ) {
channels[3][pixel] = 0;
}
else {
channels[3][pixel] = 1;
}
}
}
return image;
}
Sprite *SpriteResource::getSprite( char inUseTrans, char inCacheOK ) {
if( inCacheOK ) {
Image *cachedImage = getCachedImage( mID, inUseTrans );
if( cachedImage == NULL ) {
cachedImage = getImage();
addCachedImage( mID, inUseTrans, cachedImage );
}
return new Sprite( cachedImage );
}
else {
// ignore cache
Image *image = getImage();
Sprite *sprite = new Sprite( image );
delete image;
return sprite;
}
}
Sprite *SpriteResource::getHighlightSprite() {
Image *cachedImage = getCachedImage( mID, true );
if( cachedImage == NULL ) {
cachedImage = getImage();
addCachedImage( mID, true, cachedImage );
}
Image *highlight = cachedImage->copy();
double *channels[4];
int i;
int numPixels = P * P;
int opaqueCount = 0;
double aveComponent = 0;
channels[3] = highlight->getChannel( 3 );
for( i=0; i<3; i++ ) {
channels[i] = highlight->getChannel( i );
for( int p=0; p<numPixels; p++ ) {
if( channels[3][p] > 0 ) {
double val = channels[i][p];
aveComponent += val;
opaqueCount += 1;
}
}
}
aveComponent /= opaqueCount;
double opposite = 0;
if( aveComponent < 0.5 ) {
opposite = 1;
}
double same = 1 - opposite;
/*
// leave alpha alone
for( i=0; i<3; i++ ) {
channels[i] = highlight->getChannel( i );
for( int p=0; p<numPixels; p++ ) {
double val = channels[i][p];
if( val > 0.5 ) {
val = 0;
}
else {
val = 1.0;
}
channels[i][p] = val;
}
}
*/
// make an outline based on alpha
char outline[P*P];
char edge[P*P];
for( int y=0; y<P; y++ ) {
for( int x=0; x<P; x++ ) {
int index = y * P + x;
outline[index] = 0;
edge[index] = 0;
if( channels[3][ index ] == 0 ) {
// trans spot
// does it have non-trans neighbor?
if( y > 0 && channels[3][ index - P ] != 0 ) {
outline[index] = 1;
}
else if( y < P-1 && channels[3][ index + P ] != 0 ) {
outline[index] = 1;
}
else if( x > 0 && channels[3][ index - 1 ] != 0 ) {
outline[index] = 1;
}
else if( x < P-1 && channels[3][ index + 1 ] != 0 ) {
outline[index] = 1;
}
}
else {
// opaque
// does it touch edge?
if( y == 0 || y == P-1 ||
x == 0 || x == P-1 ) {
edge[index] = 1;
}
}
}
}
// fill in outline with same color, edges with opposite
for( i=0; i<3; i++ ) {
channels[i] = highlight->getChannel( i );
for( int p=0; p<numPixels; p++ ) {
if( outline[p] ) {
channels[i][p] = same;
channels[3][p] = 1;
}
else if( edge[p] ) {
channels[i][p] = opposite;
channels[3][p] = 1;
}
else {
channels[3][p] = 0;
}
}
}
Sprite *s = new Sprite( highlight );
delete highlight;
return s;
}
void SpriteResource::print() {
char *idString = getHumanReadableString( mID );
printf( "SpriteResource %s:\n", idString );
delete [] idString;
printf( " set name: %s\n", mSetName );
for( int y=0; y<P; y++ ) {
printf( " " );
for( int x=0; x<P; x++ ) {
::print( mPixelColors[y][x] );
printf( " " );
}
printf( "\n" );
}
}
+111
View File
@@ -0,0 +1,111 @@
#ifndef SPRITE_RESOURCE_INCLUDED
#define SPRITE_RESOURCE_INCLUDED
#include "color.h"
#include "common.h"
#include "uniqueID.h"
#include "Sprite.h"
class SpriteResource {
public:
// blank sprite
SpriteResource();
// sprite loaded from file
SpriteResource( uniqueID inID );
// sprite loaded from data string
SpriteResource( uniqueID inID, unsigned char *inData, int inLength );
// changes the color of a sprite pixel
void editSprite( int inX, int inY, rgbaColor inNewColor );
// changes the transparency of a pixel
void editTrans( int inX, int inY, char inIsTrans );
// name has at most 10 chars
void editSpriteName( const char *inName );
rgbaColor getColor( int inX, int inY );
// destroyed by caller
char *getSpriteName();
char getTrans( int inX, int inY );
// finishes the edit, generates a new unique ID, saves result
void finishEdit( char inGenerateNewID=true );
// recursively saves to current resource pack
void saveToPack();
// get an image of this sprite
Image *getImage( char inUseTrans=true );
// implements ResourceType functions as needed by ResourcePicker
uniqueID getUniqueID();
Sprite *getSprite( char inUseTrans=true, char inCacheOK=true );
static const char *getResourceType();
static SpriteResource getDefaultResource();
char *getName() {
return getSpriteName();
}
// a compliment of getSprite, for a blinking overlay
Sprite *getHighlightSprite();
void print();
// blank sprite
static SpriteResource *sBlankSprite;
static void staticInit();
static void staticFree();
protected:
void setupDefault();
char initFromData( unsigned char *inData, int inLength );
// result destroyed by caller
unsigned char *makeBytes( int *outLength );
void makeUniqueID();
// all sprites are exactly one grid square in size
rgbaColor mPixelColors[P][P];
char mTransFlags[P][P];
const static int mPixelDataLength = P * P * 4;
const static int mTransDataLength = P * P;
char mSetName[11];
uniqueID mID;
};
#endif
+814
View File
@@ -0,0 +1,814 @@
#include "StateObject.h"
#include "resourceManager.h"
#include "usageDatabase.h"
#include "uniqueID.h"
#include "SpriteResource.h"
#include "imageCache.h"
#include "packSaver.h"
#include "minorGems/util/stringUtils.h"
#include "minorGems/util/log/AppLog.h"
static unsigned char objectVersionNumber = 3;
// static inits
StateObject *StateObject::sBlankObject = NULL;
void StateObject::staticInit() {
sBlankObject = new StateObject();
// save once
sBlankObject->finishEdit();
}
void StateObject::staticFree() {
delete sBlankObject;
}
void StateObject::setupDefault() {
const char *defaultName = "default";
memcpy( mName, defaultName, strlen( defaultName ) + 1 );
makeUniqueID();
}
StateObject::StateObject() {
setupDefault();
}
void StateObject::initFromData( unsigned char *inData, int inLength,
char inFromNetwork ) {
readFromBytes( inData, inLength );
if( inFromNetwork ) {
// sprites might not be present locally, either
// more efficient to load them in a chunk instead of with separate
// messages
int numSprites = mSpriteLayers.size();
if( numSprites > 0 ) {
uniqueID *ids = mSpriteLayers.getElementArray();
int *chunkLengths = new int[ numSprites ];
char *fromNetworkFlags = new char[ numSprites ];
unsigned char **chunks = loadResourceData( "sprite",
numSprites,
ids,
chunkLengths,
fromNetworkFlags );
for( int i=0; i<numSprites; i++ ) {
if( chunks[i] != NULL ) {
if( fromNetworkFlags[i] ) {
// make a sprite
SpriteResource r( ids[i], chunks[i], chunkLengths[i] );
// force into disk cache, don't update ID
r.finishEdit( false );
}
delete [] chunks[i];
}
}
delete [] ids;
delete [] fromNetworkFlags;
delete [] chunkLengths;
delete [] chunks;
}
// save Object to disk using the ID that we fetched it with
finishEdit( false );
}
}
StateObject::StateObject( uniqueID inID, unsigned char *inData, int inLength,
char inFromNetwork )
: mID( inID ) {
initFromData( inData, inLength, inFromNetwork );
}
// room loaded from file
StateObject::StateObject( uniqueID inID )
: mID( inID ) {
int length;
char fromNetwork;
unsigned char *data = loadResourceData( "object", mID, &length,
&fromNetwork );
if( data != NULL ) {
initFromData( data, length, fromNetwork );
delete [] data;
}
else {
setupDefault();
}
}
int StateObject::getNumLayers() {
return mSpriteLayers.size();
}
char StateObject::canAdd( int inNumToAdd ) {
if( mSpriteLayers.size() + inNumToAdd <= 255 ) {
return true;
}
return false;
}
char StateObject::addLayer( uniqueID inSpriteID, int inLayerBelow ) {
if( mSpriteLayers.size() < 255 ) {
int thisIndex = inLayerBelow + 1;
SimpleVector<uniqueID> displacedIDs;
SimpleVector<intPair> displacedOffsets;
SimpleVector<unsigned char> displacedTrans;
SimpleVector<unsigned char> displacedGlow;
int oldSize = mSpriteLayers.size();
for( int i=thisIndex; i<oldSize; i++ ) {
displacedIDs.push_back( *( mSpriteLayers.getElement( i ) ) );
displacedOffsets.push_back( *( mLayerOffsets.getElement( i ) ) );
displacedTrans.push_back( *( mLayerTrans.getElement( i ) ) );
displacedGlow.push_back( *( mLayerGlow.getElement( i ) ) );
}
for( int i=oldSize-1; i>=thisIndex; i--) {
mSpriteLayers.deleteElement( i );
mLayerOffsets.deleteElement( i );
mLayerTrans.deleteElement( i );
mLayerGlow.deleteElement( i );
}
// displaced layers removed
// add new layer to end
mSpriteLayers.push_back( inSpriteID );
intPair p = { 0, 0 };
mLayerOffsets.push_back( p );
mLayerTrans.push_back( 255 );
mLayerGlow.push_back( 0 );
// add displaced after that.
int numDisplaced = displacedIDs.size();
for( int i=0; i<numDisplaced; i++ ) {
mSpriteLayers.push_back( *( displacedIDs.getElement( i ) ) );
mLayerOffsets.push_back( *( displacedOffsets.getElement( i ) ) );
mLayerTrans.push_back( *( displacedTrans.getElement( i ) ) );
mLayerGlow.push_back( *( displacedGlow.getElement( i ) ) );
}
return true;
}
else {
AppLog::error( "Error: 256th layer added to full StateObject\n" );
return false;
}
}
void StateObject::deleteLayer( int inLayer ) {
mSpriteLayers.deleteElement( inLayer );
mLayerOffsets.deleteElement( inLayer );
mLayerTrans.deleteElement( inLayer );
mLayerGlow.deleteElement( inLayer );
}
void StateObject::editLayerSprite( int inLayer, uniqueID inSpriteID ) {
*( mSpriteLayers.getElement( inLayer ) ) = inSpriteID;
}
void StateObject::editLayerOffset( int inLayer, intPair inOffset ) {
*( mLayerOffsets.getElement( inLayer ) ) = inOffset;
}
void StateObject::editLayerTrans( int inLayer, unsigned char inTrans ) {
*( mLayerTrans.getElement( inLayer ) ) = inTrans;
}
void StateObject::editLayerGlow( int inLayer, char inGlow ) {
*( mLayerGlow.getElement( inLayer ) ) = (unsigned char)inGlow;
}
uniqueID StateObject::getLayerSprite( int inLayer ) {
return *( mSpriteLayers.getElement( inLayer ) );
}
intPair StateObject::getLayerOffset( int inLayer ) {
return *( mLayerOffsets.getElement( inLayer ) );
}
unsigned char StateObject::getLayerTrans( int inLayer ) {
return *( mLayerTrans.getElement( inLayer ) );
}
char StateObject::getLayerGlow( int inLayer ) {
return (char)( *( mLayerGlow.getElement( inLayer ) ) );
}
void StateObject::moveLayerUp( int inLayer ) {
uniqueID oldID = getLayerSprite( inLayer );
intPair oldOffset = getLayerOffset( inLayer );
unsigned char oldTrans = getLayerTrans( inLayer );
char oldGlow = getLayerGlow( inLayer );
deleteLayer( inLayer );
addLayer( oldID, inLayer );
editLayerOffset( inLayer + 1, oldOffset );
editLayerTrans( inLayer + 1, oldTrans );
editLayerGlow( inLayer + 1, oldGlow );
}
void StateObject::moveLayerDown( int inLayer ) {
uniqueID oldID = getLayerSprite( inLayer );
intPair oldOffset = getLayerOffset( inLayer );
unsigned char oldTrans = getLayerTrans( inLayer );
char oldGlow = getLayerGlow( inLayer );
deleteLayer( inLayer );
addLayer( oldID, inLayer - 2 );
editLayerOffset( inLayer - 1, oldOffset );
editLayerTrans( inLayer - 1, oldTrans );
editLayerGlow( inLayer - 1, oldGlow );
}
void StateObject::editStateObjectName( const char *inName ) {
int newLength = strlen( inName );
if( newLength > 10 ) {
printf( "Error: StateObject name %s is too long (10 max)\n", inName );
}
else {
memcpy( mName, inName, newLength + 1 );
}
}
char *StateObject::getStateObjectName() {
return stringDuplicate( mName );
}
// finishes the edit, generates a new unique ID, saves result
void StateObject::finishEdit( char inGenerateNewID ) {
uniqueID oldID = mID;
if( inGenerateNewID ) {
makeUniqueID();
}
if( !equal( oldID, mID ) ||
! resourceExists( "object", mID ) ) {
// change
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
saveResourceData( "object", mID, mName, bytes, numBytes );
delete [] bytes;
// track usages
int numSprites = mSpriteLayers.size();
for( int j=0; j<numSprites; j++ ) {
addUsage( mID, *( mSpriteLayers.getElementFast( j ) ) );
}
}
}
void StateObject::saveToPack() {
for( int i=0; i<mSpriteLayers.size(); i++ ) {
SpriteResource s( *( mSpriteLayers.getElement(i) ) );
s.saveToPack();
}
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
addToPack( "object", mID, mName, bytes, numBytes );
delete [] bytes;
}
uniqueID StateObject::getUniqueID() {
return mID;
}
Sprite *StateObject::getSprite( char inUseTrans, char inCacheOK ) {
Image *cachedImage = NULL;
if( inCacheOK ) {
cachedImage = getCachedImage( mID, inUseTrans );
}
if( cachedImage == NULL ) {
int imageW = P*G;
// zero image
Image fullImage( imageW, imageW, 4, true );
int imageCenter = (P*G)/2;
// track largest rectangle area that is not transparent
int lowX = imageW;
int hiX = 0;
int lowY = imageW;
int hiY = 0;
int numLayers = mSpriteLayers.size();
for( int i=0; i<numLayers; i++ ) {
SpriteResource r( *( mSpriteLayers.getElement(i) ) );
intPair layerOffset = *( mLayerOffsets.getElement(i) );
float layerTrans = *( mLayerTrans.getElement(i) ) / 255.0f;
char layerGlow = *( mLayerGlow.getElement(i) );
// force trans for sub-sprites, or else weird bg color overlap
// results
Image *spriteImage = r.getImage( true );
int spriteCenter = P/2;
for( int y=0; y<P; y++ ) {
for( int x=0; x<P; x++ ) {
int fullY = y - spriteCenter - layerOffset.y;
fullY += imageCenter;
int fullX = x - spriteCenter + layerOffset.x;
fullX += imageCenter;
if( fullY < imageW && fullY >= 0
&&
fullX < imageW && fullX >= 0 ) {
int index = y * P + x;
Color c = spriteImage->getColor( index );
float colorTrans = c.a * layerTrans;
if( colorTrans > 0 ) {
int fullIndex = fullY * imageW + fullX;
Color oldColor = fullImage.getColor( fullIndex );
Color *blend;
if( oldColor.a > 0 ) {
if( ! layerGlow ) {
blend = Color::linearSum(
&c, &( oldColor ),
colorTrans );
}
else {
// additive
blend = oldColor.copy();
blend->r += c.r * colorTrans;
blend->g += c.g * colorTrans;
blend->b += c.b * colorTrans;
// cap
if( blend->r > 1 ) {
blend->r = 1;
}
if( blend->g > 1 ) {
blend->g = 1;
}
if( blend->b > 1 ) {
blend->b = 1;
}
}
}
else {
blend = c.copy();
blend->a = colorTrans;
}
// overwrite with blend of ink from higher layer
fullImage.setColor( fullIndex, *blend );
delete blend;
if( fullX < lowX ) {
lowX = fullX;
}
if( fullX > hiX ) {
hiX = fullX;
}
if( fullY < lowY ) {
lowY = fullY;
}
if( fullY > hiY ) {
hiY = fullY;
}
}
}
}
}
delete spriteImage;
}
int subW = hiX - lowX + 1;
int subH = hiY - lowY + 1;
Image *subImage = fullImage.getSubImage( lowX, lowY,
subW, subH );
//printf( "subimage = x,y=(%d,%d), w,h=(%d,%d)\n",
// lowX, lowY, subW, subH );
// build up sums here
Image *shrunkImage = new Image( P, P, 4, true );
double *subChannels[4];
double *shrunkChannels[4];
int hitCount[P*P];
memset( hitCount, 0, P*P*sizeof(int) );
for( int c=0; c<4; c++ ) {
subChannels[c] = subImage->getChannel( c );
shrunkChannels[c] = shrunkImage->getChannel( c );
}
double scaleFactor;
if( subW > subH ) {
scaleFactor = P / (double)subW;
}
else {
scaleFactor = P / (double)subH;
}
if( scaleFactor > 1 ) {
// P big enough to contain whole object
scaleFactor = 1;
}
// center in shrunk image
int xExtra = (int)( P - ( scaleFactor * subW ) );
int yExtra = (int)( P - ( scaleFactor * subH ) );
int xOffset = xExtra / 2;
int yOffset = yExtra / 2;
for( int y=0; y<subH; y++ ) {
int shrunkY = (int)( y * scaleFactor ) + yOffset;
for( int x=0; x<subW; x++ ) {
int shrunkX = (int)( x * scaleFactor ) + xOffset;
int shrunkIndex = shrunkY * P + shrunkX;
int subIndex = y * subW + x;
// skip transparent pixels
if( subChannels[3][subIndex] > 0 ) {
hitCount[shrunkIndex] ++;
for( int c=0; c<4; c++ ) {
shrunkChannels[c][shrunkIndex] +=
subChannels[c][subIndex];
}
// anything hit by non-trans color is opaque
//shrunkChannels[3][shrunkIndex] = 1;
}
}
}
for( int c=0; c<4; c++ ) {
for( int i=0; i<P*P; i++ ) {
// transparent pixels hit 0 times
if( hitCount[i] > 0 ) {
shrunkChannels[c][i] /= hitCount[i];
}
}
}
// sharp transparency already computed above
// (skipped trans pixels in sums)
delete subImage;
cachedImage = shrunkImage;
if( inCacheOK ) {
addCachedImage( mID, inUseTrans, cachedImage );
}
}
Sprite *sprite = new Sprite( cachedImage );
if( !inCacheOK ) {
// image not in cache... we must destroy it
delete cachedImage;
}
return sprite;
}
const char *StateObject::getResourceType() {
return "object";
}
StateObject StateObject::getDefaultResource() {
return *sBlankObject;
}
#include "minorGems/util/SimpleVector.h"
unsigned char *StateObject::makeBytes( int *outLength ) {
SimpleVector<unsigned char> buffer;
buffer.push_back( (unsigned char *)SID_MAGIC_CODE,
strlen( SID_MAGIC_CODE ) );
buffer.push_back( objectVersionNumber );
unsigned char numLayers = (unsigned char)mSpriteLayers.size();
// one byte for num layers (max 255 layers)
buffer.push_back( numLayers );
for( int i=0; i<numLayers; i++ ) {
buffer.push_back( mSpriteLayers.getElement(i)->bytes, U );
intPair *p = mLayerOffsets.getElement( i );
// one byte each coordinate
buffer.push_back( (unsigned char)p->x );
buffer.push_back( (unsigned char)p->y );
buffer.push_back( *( mLayerTrans.getElement( i ) ) );
buffer.push_back( *( mLayerGlow.getElement( i ) ) );
}
buffer.push_back( (unsigned char *)mName, strlen( mName ) + 1 );
*outLength = buffer.size();
return buffer.getElementArray();
}
void StateObject::readFromBytes( unsigned char *inBytes, int inLength ) {
unsigned char version = 1;
// check for magic code before version number
const char *code = SID_MAGIC_CODE;
int codeLength = strlen( code );
if( inLength >= codeLength ) {
char codeFound = true;
for( int i=0; i<codeLength && codeFound; i++ ) {
if( inBytes[i] != code[i] ) {
codeFound = false;
}
}
if( codeFound ) {
// next byte is file version number
version = inBytes[codeLength];
// skip them
inBytes = &( inBytes[ codeLength + 1 ] );
inLength -= ( codeLength + 1 );
}
// else no version number, data starts right at beginning
}
if( inLength < 0 ) {
return;
}
int numLayers = inBytes[0];
inBytes = &( inBytes[ 1 ] );
inLength --;
int layerDataLength = U+2+1;
if( version < 2 ) {
layerDataLength = U+2;
}
if( inLength < layerDataLength * numLayers ) {
AppLog::error(
"Error: Data stream too short to contain state object.\n" );
return;
}
for( int i=0; i<numLayers; i++ ) {
int numUsed;
uniqueID id = readUniqueID( inBytes, inLength, &numUsed );
if( numUsed < 0 ) {
AppLog::error( "Error: reading StateObject's sprite uniqueID failed.\n" );
return;
}
mSpriteLayers.push_back( id );
inBytes = &( inBytes[ numUsed ] );
inLength -= numUsed;
// convert to signed to unpack potentially-negative values
intPair p = { (char)( inBytes[0] ), (char)( inBytes[1] ) };
mLayerOffsets.push_back( p );
inBytes = &( inBytes[ 2 ] );
inLength -= 2;
if( version >= 2 ) {
mLayerTrans.push_back( inBytes[0] );
inBytes = &( inBytes[ 1 ] );
inLength -= 1;
if( version >= 3 ) {
mLayerGlow.push_back( inBytes[0] );
inBytes = &( inBytes[ 1 ] );
inLength -= 1;
}
else {
// fill in with dummy data
mLayerGlow.push_back( 0 );
}
}
else {
// fill in with dummy data
mLayerTrans.push_back( 255 );
}
}
// remainder is name
if( inLength <= 11 ) {
memcpy( mName, inBytes, inLength );
}
}
void StateObject::makeUniqueID() {
int numBytes;
unsigned char *bytes = makeBytes( &numBytes );
mID = ::makeUniqueID( bytes, numBytes );
delete [] bytes;
}
void StateObject::print() {
char *idString = getHumanReadableString( mID );
printf( "StateObject %s:\n", idString );
delete [] idString;
for( int i=0; i<mSpriteLayers.size(); i++ ) {
idString = getHumanReadableString( *( mSpriteLayers.getElement(i) ) );
intPair *p = mLayerOffsets.getElement( i );
printf( " Sprite: %s, (%d,%d) [%d] [Glow=%d]\n",
idString, p->x, p->y, *(mLayerTrans.getElement( i )),
*(mLayerGlow.getElement( i )) );
delete [] idString;
}
printf( "\n" );
}
+137
View File
@@ -0,0 +1,137 @@
#ifndef STATE_OBJECT_INCLUDED
#define STATE_OBJECT_INCLUDED
#include "color.h"
#include "common.h"
#include "uniqueID.h"
#include "Sprite.h"
#include "minorGems/util/SimpleVector.h"
class StateObject {
public:
// empty object with default name
StateObject();
// object loaded from file
StateObject( uniqueID inID );
// object loaded from data string
StateObject( uniqueID inID, unsigned char *inData, int inLength,
char inFromNetwork );
int getNumLayers();
// true if there's room for more layers
char canAdd( int inNumToAdd );
// add a new layer with zero offset above layer number "inLayerBelow"
// returns true on success, or false if full
char addLayer( uniqueID inSpriteID, int inLayerBelow = -1 );
void deleteLayer( int inLayer );
void editLayerSprite( int inLayer, uniqueID inSpriteID );
void editLayerOffset( int inLayer, intPair inOffset );
void editLayerTrans( int inLayer, unsigned char inTrans );
void editLayerGlow( int inLayer, char inGlowMode);
void moveLayerUp( int inLayer );
void moveLayerDown( int inLayer );
// name has at most 10 chars
void editStateObjectName( const char *inName );
uniqueID getLayerSprite( int inLayer );
intPair getLayerOffset( int inLayer );
unsigned char getLayerTrans( int inLayer );
char getLayerGlow( int inLayer );
char *getStateObjectName();
// finishes the edit, generates a new unique ID, saves result
void finishEdit( char inGenerateNewID=true );
// recursively saves to current resource pack
void saveToPack();
// implements ResourceType functions as needed by ResourcePicker
uniqueID getUniqueID();
Sprite *getSprite( char inUseTrans=false, char inCacheOK=true );
static const char *getResourceType();
static StateObject getDefaultResource();
char *getName() {
return getStateObjectName();
}
// blank object
static StateObject *sBlankObject;
static void staticInit();
static void staticFree();
void print();
// not part of state that is saved to disk
// used only by editor
int mSelectedLayer;
protected:
void setupDefault();
void initFromData( unsigned char *inData, int inLength,
char inFromNetwork );
// result destroyed by caller
unsigned char *makeBytes( int *outLength );
void readFromBytes( unsigned char *inBytes, int inLength );
void makeUniqueID();
SimpleVector<uniqueID> mSpriteLayers;
SimpleVector<intPair> mLayerOffsets;
SimpleVector<unsigned char> mLayerTrans;
SimpleVector<unsigned char> mLayerGlow;
char mName[11];
uniqueID mID;
};
#endif
+479
View File
@@ -0,0 +1,479 @@
#include "StateObjectDisplay.h"
#include "common.h"
#include "SpriteResource.h"
#include "TilePicker.h"
#include "minorGems/graphics/openGL/gui/TextGL.h"
#include "minorGems/util/stringUtils.h"
extern TilePicker *mainTilePicker;
static clickMask blankMask;
StateObjectDisplay::StateObjectDisplay( int inAnchorX, int inAnchorY )
: GUIComponentGL( inAnchorX, inAnchorY,
G * P, G * P ),
mLastPixelClickX( 0 ), mLastPixelClickY( 0 ),
mLastGridClickX( 0 ), mLastGridClickY( 0 ),
mLastActionRelease( false ),
mLastActionPress( false ),
mMouseHover( false ),
mLastHoverX( 0 ), mLastHoverY( 0 ),
mShowSelected( true ),
mShowGrid( true ),
mUseTrans( true ),
mLastSelected( -2 ),
mBlinkCycle( 0 ) {
for( int y=0; y<P; y++ ) {
for( int x=0; x<P; x++ ) {
blankMask.opaque[y][x] = false;
}
}
mZoomFactor = 1.0;
}
StateObjectDisplay::~StateObjectDisplay() {
int numSprites = mObjectSprites.size();
for( int s=0; s<numSprites; s++ ) {
delete *( mObjectSprites.getElement( s ) );
delete *( mObjectSpriteHighlights.getElement( s ) );
}
}
void StateObjectDisplay::setStateObject( StateObject inStateObject,
char inUseTrans ) {
mUseTrans = inUseTrans;
mStateObject = inStateObject;
// clear old
int numSprites = mObjectSprites.size();
int i;
for( i=0; i<numSprites; i++ ) {
delete *( mObjectSprites.getElement( i ) );
delete *( mObjectSpriteHighlights.getElement( i ) );
}
mObjectSprites.deleteAll();
mObjectSpriteHighlights.deleteAll();
mObjectSpritePositions.deleteAll();
mObjectSpriteFades.deleteAll();
mObjectSpriteGlows.deleteAll();
mObjectMasks.deleteAll();
int centerScreenPosX = (int)( P * (G/2) / mZoomFactor );
int centerScreenPosY = (int)( P * (G/2 - mZoomFactor) / mZoomFactor );
// add new
numSprites = mStateObject.getNumLayers();
for( int s=0; s<numSprites; s++ ) {
SpriteResource resource( mStateObject.getLayerSprite( s ) );
mObjectSprites.push_back( resource.getSprite( mUseTrans ) );
mObjectSpriteHighlights.push_back( resource.getHighlightSprite() );
intPair p = mStateObject.getLayerOffset( s );
p.x += centerScreenPosX;
p.y += centerScreenPosY;
mObjectSpritePositions.push_back( p );
mObjectSpriteFades.push_back( mStateObject.getLayerTrans( s ) /
255.0f );
mObjectSpriteGlows.push_back( mStateObject.getLayerGlow( s ) );
clickMask mask;
for( int y=0; y<P; y++ ) {
for( int x=0; x<P; x++ ) {
mask.opaque[y][x] = ! resource.getTrans( x, y );
}
}
mObjectMasks.push_back( mask );
}
// last, add anchor sprite for this object, on top
mObjectSprites.push_back( new Sprite( "anchor.tga", true ) );
// no special highlight for anchor
mObjectSpriteHighlights.push_back( new Sprite( "anchor.tga", true ) );
intPair p = {0,0};
p.x += centerScreenPosX;
p.y += centerScreenPosY;
mObjectSpritePositions.push_back( p );
mObjectSpriteFades.push_back( 1.0f );
mObjectSpriteGlows.push_back( false );
mObjectMasks.push_back( blankMask );
}
void StateObjectDisplay::updateSpritePositions( StateObject inStateObject ) {
// don't need to touch masks
mStateObject = inStateObject;
int centerScreenPosX = (int)( P * (G/2) / mZoomFactor );
int centerScreenPosY = (int)( P * (G/2 - mZoomFactor) / mZoomFactor );
int numSprites = mStateObject.getNumLayers();
for( int s=0; s<numSprites; s++ ) {
intPair p = mStateObject.getLayerOffset( s );
p.x += centerScreenPosX;
p.y += centerScreenPosY;
*( mObjectSpritePositions.getElement( s ) ) = p;
*( mObjectSpriteFades.getElement( s ) ) =
mStateObject.getLayerTrans( s ) / 255.0f;
*( mObjectSpriteGlows.getElement( s ) ) =
mStateObject.getLayerGlow( s );
}
// anchor
intPair p = {0,0};
p.x += centerScreenPosX;
p.y += centerScreenPosY;
*( mObjectSpritePositions.getElement( numSprites ) ) = p;
}
void StateObjectDisplay::showGrid( char inShow ) {
mShowGrid = inShow;
}
double StateObjectDisplay::getZoom() {
return mZoomFactor;
}
void StateObjectDisplay::setZoom( double inZoom ) {
mZoomFactor = inZoom;
updateSpritePositions( mStateObject );
}
void StateObjectDisplay::fireRedraw() {
int gridLimit = (int)( G / mZoomFactor );
int selected = mStateObject.mSelectedLayer;
if( mLastSelected != selected || ! mShowGrid ) {
// reset
mBlinkCycle = 0;
mLastSelected = selected;
}
else {
mBlinkCycle ++;
}
if( mUseTrans ) {
// background of selected tile
Tile tile = (Tile)( mainTilePicker->getBackgroundTile() );
Sprite *tileSprite = tile.getSprite();
for( int y=0; y<gridLimit; y++ ) {
for( int x=0; x<gridLimit; x++ ) {
Vector3D pos( mAnchorX + ( x * P + P/2 ) * mZoomFactor,
mAnchorY + (y * P + P/2) * mZoomFactor,
0 );
tileSprite->draw( 0, 0, &pos, mZoomFactor );
}
}
delete tileSprite;
}
// now draw all sprites
int numSprites = mObjectSprites.size();
//printf( "Drawing %d sprites, %d selected\n", numSprites, selected );
for( int s=0; s<numSprites; s++ ) {
Sprite *sprite = *( mObjectSprites.getElement( s ) );
intPair pixelPos = *( mObjectSpritePositions.getElement( s ) );
float fadeFactor = *( mObjectSpriteFades.getElement( s ) );
Vector3D pos( mAnchorX + mZoomFactor * (pixelPos.x + P/2),
mAnchorY + mZoomFactor * (pixelPos.y + P/2), 0 );
char drawIt = true;
Color c( 1, 1, 1, 1 );
char isAnchor = false;
if( s == numSprites - 1 ) {
// an anchor
isAnchor = true;
fadeFactor *= 0.25;
if( !mShowGrid ) {
// hide anchors
drawIt = false;
}
}
/*
if( ! isAnchor &&
mShowGrid &&
s == selected ) {
// make it blink
double blinkFadeFactor = 0.35 * cos( mBlinkCycle / 3.0 ) + 0.65;
fadeFactor *= blinkFadeFactor;
}
*/
// skip sprites that are way out of bounds (due to zooming)
if( pos.mX < mAnchorX || pos.mX > mAnchorX + mWidth
||
pos.mY < mAnchorY || pos.mY > mAnchorY + mHeight ) {
drawIt = false;
}
if( drawIt ) {
char glow = *( mObjectSpriteGlows.getElement( s ) );
if( glow ) {
// brighten only
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
}
sprite->draw( 0, 0, &pos, mZoomFactor, fadeFactor, &c );
if( glow ) {
// back to normal blend
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
}
if( ! isAnchor &&
mShowGrid &&
mShowSelected &&
s == selected ) {
// blinking highlight on top
double blinkFadeFactor =
0.25 * cos( mBlinkCycle / 3.0 ) + 0.25;
Sprite *highlight =
*( mObjectSpriteHighlights.getElement( s ) );
highlight->draw( 0, 0, &pos,
mZoomFactor,
blinkFadeFactor, &c );
}
}
}
if( mShowGrid ) {
//grid lines
glColor4f( 1, 1, 1, 0.25f );
glBegin( GL_LINES ); {
for( int x=0; x<gridLimit+1; x++ ) {
glVertex2d( mAnchorX + x * P * mZoomFactor, mAnchorY );
glVertex2d( mAnchorX + x * P * mZoomFactor,
mAnchorY + mHeight );
}
for( int y=0; y<gridLimit+1; y++ ) {
glVertex2d( mAnchorX, mAnchorY + y * P * mZoomFactor );
glVertex2d( mAnchorX + mWidth,
mAnchorY + y * P * mZoomFactor );
}
}
glEnd();
}
/*
// thin white border, partly transparent
glColor4f( 1, 1, 1, 0.25f );
glBegin( GL_LINE_LOOP ); {
glVertex2d( mAnchorX, mAnchorY );
glVertex2d( mAnchorX + mWidth, mAnchorY );
glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight );
glVertex2d( mAnchorX, mAnchorY + mHeight );
}
glEnd();
*/
}
void StateObjectDisplay::setLastMouse( double inX, double inY ) {
double tempX = ( inX - mAnchorX ) - P * (G/2);
double tempY = ( inY - mAnchorY ) - P * (G/2 - mZoomFactor);
mLastPixelClickX = (int)( floor( tempX / mZoomFactor ) );
mLastPixelClickY = (int)( floor( tempY / mZoomFactor ) );
mLastPixelClickX -= P/2;
mLastPixelClickY -= P/2;
mLastHoverX = mLastPixelClickX;
mLastHoverY = mLastPixelClickY;
mLastGridClickX = (int)( mLastPixelClickX / P );
mLastGridClickY = (int)( mLastPixelClickY / P );
}
void StateObjectDisplay::mouseDragged( double inX, double inY ) {
mLastActionRelease = false;
mLastActionPress = false;
if( isEnabled() && isInside( inX, inY ) ) {
setLastMouse( inX, inY );
fireActionPerformed( this );
}
}
void StateObjectDisplay::mousePressed( double inX, double inY ) {
mLastActionRelease = false;
mLastActionPress = true;
if( isEnabled() && isInside( inX, inY ) ) {
setLastMouse( inX, inY );
fireActionPerformed( this );
}
}
void StateObjectDisplay::mouseReleased( double inX, double inY ) {
mShowSelected = true;
mLastActionRelease = true;
mLastActionPress = false;
if( isEnabled() && isInside( inX, inY ) ) {
setLastMouse( inX, inY );
fireActionPerformed( this );
}
}
void StateObjectDisplay::mouseMoved( double inX, double inY ) {
if( isEnabled() && isInside( inX, inY ) ) {
mMouseHover = true;
setLastMouse( inX, inY );
}
else {
mMouseHover = false;
}
}
int StateObjectDisplay::getClickedLayer( int inPixelClickX,
int inPixelClickY,
int *outClickOffsetX,
int *outClickOffsetY ) {
//printf( "Checking clicked layer for %d, %d\n",
// inPixelClickX, inPixelClickY );
int centerScreenPosX = (int)( P * (G/2) / mZoomFactor ) + P / 2;
int centerScreenPosY =
(int)( P * (G/2 - mZoomFactor) / mZoomFactor ) + P / 2;
int numLayers = mObjectMasks.size();
// skip anchor, start at top, stop when a hit is found
for( int i=numLayers-2; i>=0; i-- ) {
intPair pos = *( mObjectSpritePositions.getElement( i ) );
int posX = pos.x - centerScreenPosX;
int posY = pos.y - centerScreenPosY;
//printf( " Layer %d at (%d,%d)\n", i, posX, posY );
// first, do bounding box
if( inPixelClickX >= posX && inPixelClickX < posX + P
&&
inPixelClickY >= posY && inPixelClickY < posY + P ) {
clickMask *mask = mObjectMasks.getElement( i );
int offsetY = P - ( inPixelClickY - posY ) - 1;
int offsetX = inPixelClickX - posX;
if( mask->opaque[ offsetY ][ offsetX ] ) {
// hit!
*outClickOffsetX = offsetX - P/2;
*outClickOffsetY = offsetY - P/2;
return i;
}
}
}
return -1;
}
+107
View File
@@ -0,0 +1,107 @@
#ifndef STATE_OBJECT_DISPLAY_INCLUDED
#define STATE_OBJECT_DISPLAY_INCLUDED
#include "minorGems/graphics/openGL/gui/GUIComponentGL.h"
#include "minorGems/ui/event/ActionListenerList.h"
#include "StateObject.h"
#include "common.h"
// used to track trans of each displayed sprite
typedef struct clickMask{
char opaque[P][P];
} clickMask;
class StateObjectDisplay : public GUIComponentGL, public ActionListenerList {
public:
// sets its own width and height
StateObjectDisplay( int inAnchorX, int inAnchorY );
virtual ~StateObjectDisplay();
void setStateObject( StateObject inStateObject, char inUseTrans );
// updates sprite positions from data in inState
// inState assumed to be indentical to last setState otherwise
void updateSpritePositions( StateObject inStateObject );
// turns grid/anchor drawing on and off
void showGrid( char inShow );
double getZoom();
void setZoom( double inZoom );
// passes out offset of click insided clicked layer
// offset is from center of sprite layer
int getClickedLayer( int inPixelClickX, int inPixelClickY,
int *outClickOffsetX, int *outClickOffsetY );
// override
virtual void fireRedraw();
virtual void mouseDragged( double inX, double inY );
virtual void mousePressed( double inX, double inY );
virtual void mouseReleased( double inX, double inY );
virtual void mouseMoved( double inX, double inY );
// after this display fires an action, find out
// where the mouse event happened
int mLastPixelClickX, mLastPixelClickY;
int mLastGridClickX, mLastGridClickY;
char mLastActionRelease;
char mLastActionPress;
char mMouseHover;
int mLastHoverX, mLastHoverY;
// used to disable selected highlight during drags
char mShowSelected;
protected:
void setLastMouse( double inX, double inY );
double mZoomFactor;
char mFocused;
char mShowGrid;
char mUseTrans;
int mLastSelected;
int mBlinkCycle;
StateObject mStateObject;
SimpleVector<Sprite*> mObjectSprites;
SimpleVector<Sprite*> mObjectSpriteHighlights;
SimpleVector<intPair> mObjectSpritePositions;
SimpleVector<float> mObjectSpriteFades;
SimpleVector<char> mObjectSpriteGlows;
SimpleVector<clickMask> mObjectMasks;
};
#endif
File diff suppressed because it is too large Load Diff
+155
View File
@@ -0,0 +1,155 @@
#ifndef STATE_OBJECT_EDITOR_INCLUDED
#define STATE_OBJECT_EDITOR_INCLUDED
#include "Editor.h"
#include "buttons.h"
#include "StateObject.h"
#include "StateObjectDisplay.h"
#include "SizeLimitedVector.h"
#include "ToolTipSliderGL.h"
#include "minorGems/graphics/openGL/gui/TextFieldGL.h"
class StateObjectEditor : public Editor {
public:
StateObjectEditor( ScreenGL *inScreen );
~StateObjectEditor();
virtual void actionPerformed( GUIComponent *inTarget );
protected:
// implemented by all subclasses
// called by parent class when editor is being closed
virtual void editorClosing();
// triggered by add button or close
void addStateObject();
void generateDraggingOffsets();
AddButtonGL *mAddButton;
char mAddAction;
int mSelectedLayer;
// where user clicked on layer, move layer center relative to
// mouse movement using this offset
intPair mLayerClickOffset;
// when dragging the whole object around, track offset of each layer
SimpleVector<intPair> mWholeDragOffsets;
void setStateObjectToEdit( StateObject inStateObject );
StateObject mStateObjectToEdit;
StateObjectDisplay *mStateDisplay;
//EditButtonGL *mEditRoomButton;
EditButtonGL *mEditSpriteButton;
UndoButtonGL *mUndoButton;
RedoButtonGL *mRedoButton;
ClearButtonGL *mClearButton;
RotateCWButtonGL *mCWButton;
FlipHButtonGL *mFlipHButton;
ToolTipSliderGL *mLayerTransSlider;
char mIgnoreSliders;
char mUndoOrRedoAction;
void clearRedoStack();
void saveUndoPoint();
SizeLimitedVector<StateObject> mUndoStack;
SimpleVector<StateObject> mRedoStack;
//QuickDeleteButtonGL *mObjectLayerDeleteButton;
//SmallAddButtonGL *mObjectLayerAddButton;
//SpriteButtonGL *mLayerReplaceButton;
SpriteButtonGL *mReplaceSpriteButton;
KeyEquivButtonGL *mAddSpriteButton;
KeyEquivButtonGL *mEditSelectedSpriteButton;
KeyEquivButtonGL *mRemoveSpriteButton;
char mEditingSelectedLayer;
TwoSpriteButtonGL *mObjectLayerViewButton;
LeftButtonGL *mObjectPrevLayerButton;
RightButtonGL *mObjectNextLayerButton;
SpriteButtonGL *mObjectBottomLayerButton;
SpriteButtonGL *mObjectTopLayerButton;
SpriteButtonGL *mLayerUpButton;
SpriteButtonGL *mLayerDownButton;
ToggleSpriteButtonGL *mLayerGlowButton;
SelectableButtonGL *mTransButton;
SelectableButtonGL *mGridButton;
SpriteButtonGL *mZoomButton;
SpriteButtonGL *mUnZoomButton;
TextFieldGL *mNameField;
char mDragFromObjectPicker;
char mClickStartedOnLayer;
Image *mNoDropImage;
Image *mCanDropImage;
// for usage tracking of objects in current state
uniqueID mCurrentWorkingStateID;
};
#endif
+80
View File
@@ -0,0 +1,80 @@
#include "StateObjectPicker.h"
#include "TilePicker.h"
extern TilePicker *mainTilePicker;
// all share one stack
SimpleVector<uniqueID> globalObjectStack;
StateObjectPicker::StateObjectPicker(
double inAnchorX, double inAnchorY )
: ResourcePicker<StateObject>( inAnchorX, inAnchorY, &globalObjectStack ) {
mainTilePicker->addActionListener( this );
mTransButton = new SelectableButtonGL(
new Sprite( "transSmall.tga", true ),
1,
mAnchorX + 2 * (mWidth - 12) / 3 + 2,
mAnchorY,
12, 12 );
add( mTransButton );
mTransButton->setSelected( true );
mTransButton->addActionListener( this );
mTransButton->setToolTip( "tip_trans" );
}
StateObjectPicker::~StateObjectPicker() {
}
Sprite *StateObjectPicker::getButtonGridBackground() {
if( mTransButton->getSelected() ) {
return mainTilePicker->getBackgroundTile().getSprite();
}
else {
// no trans
return NULL;
}
}
void StateObjectPicker::actionPerformed( GUIComponent *inTarget ) {
if( inTarget == mainTilePicker ) {
// tile changed, new background sprite in result display
// generate new result sprites
getFreshResults();
refreshSelectedResourceSprite();
}
else if( inTarget == mTransButton ) {
// toggle
mTransButton->setSelected( ! mTransButton->getSelected() );
getFreshResults();
refreshSelectedResourceSprite();
}
else {
ResourcePicker<StateObject>::actionPerformed( inTarget );
}
}

Some files were not shown because too many files have changed in this diff Show More