Exported latest revision from sourceforge cvs, discarding history.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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 ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
};
|
||||
|
||||
@@ -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" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
@@ -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 ) );
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 ];
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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 $< $@
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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" );
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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" );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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" );
|
||||
}
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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" );
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user