diff --git a/.#Makefile.1.19 b/.#Makefile.1.19 new file mode 100644 index 0000000..7eb1969 --- /dev/null +++ b/.#Makefile.1.19 @@ -0,0 +1,121 @@ +# Makefile +# author: iOS-Software +# July 2004 + +#BINDIR=/usr/games/ +DATADIR=\"data\" + +# SDL_CONFIG=/home/jeko/opt/bin/sdl-config +SDL_CONFIG=sdl-config +CC=g++ +CXX=g++ + +CFLAGS=`$(SDL_CONFIG) --cflags` -g -I/sw/include -DUSE_AUDIO=1 -DHAVE_OPENGL=1 +CXXFLAGS=${CFLAGS} + +HFILES= HiScores.h IosException.h IosImgProcess.h IosVector.h PuyoCommander.h PuyoGame.h \ + PuyoIA.h PuyoPlayer.h PuyoStory.h PuyoView.h SDL_prim.h audio.h menu.h \ + menuitems.h preferences.h scrollingtext.h sofont.h SDL_Painter.h PuyoVersion.h \ + InputManager.h GameControls.h HiScores.h + + +OBJFILES= SDL_prim.o HiScores.o scenar.y.o scenar.l.o PuyoCommander.o IosException.o \ + IosVector.o main.o PuyoGame.o PuyoVersion.o PuyoView.o PuyoIA.o sofont.o \ + menu.o menuitems.o audio.o scrollingtext.o preferences.o PuyoStory.o SDL_Painter.o \ + InputManager.o GameControls.o PuyoDoomMelt.o glSDL.o + +all: prelude flobopuyo + +flobopuyo: ${OBJFILES} + @echo "[flobopuyo]" && g++ $(CFLAGS) -o flobopuyo `$(SDL_CONFIG) --cflags --libs` -lSDL_mixer -lSDL_image ${OBJFILES} + @echo "--------------------------------------" + @echo " Compilation finished" + @[ "x`cat WARNINGS | wc -l`" != "x0" ] && echo -e "--------------------------------------\n There have been some warnings:\n" && cat WARNINGS && rm -f WARNINGS && echo "--------------------------------------" || true + @echo + @echo " Type ./flobopuyo to play." + @echo "--------------------------------------" + +prelude: + @rm -f WARNINGS + @touch WARNINGS + +%.o:%.c + @echo "[$@]" && $(CC) $(CFLAGS) -DDATADIR=${DATADIR} -c $< 2>> WARNINGS || (cat WARNINGS && false) + +%.o:%.cpp + @echo "[$@]" && $(CXX) $(CFLAGS) -DDATADIR=${DATADIR} -c $< 2>> WARNINGS || (cat WARNINGS && false) + +PuyoDoomMelt.o:PuyoDoomMelt.c ${HFILES} +HiScores.o:HiScores.cpp HiScores.h preferences.h +PuyoCommander.o:PuyoCommander.cpp ${HFILES} +PuyoGame.o:PuyoGame.cpp ${HFILES} +PuyoIA.o:PuyoIA.cpp ${HFILES} +PuyoStory.o:PuyoStory.cpp ${HFILES} +PuyoView.o:PuyoView.cpp ${HFILES} +main.o:main.cpp ${HFILES} +preferences.o:preferences.c preferences.h +scenar.l.o:scenar.l.c ${HFILES} +scenar.y.o:scenar.y.c ${HFILES} +InputManager.o:InputManager.cpp ${HFILES} +GameControls.o:GameControls.cpp ${HFILES} +SDL_Painter.o:SDL_Painter.cpp SDL_Painter.h +audio.o:audio.c audio.h +menu.o:menu.c menu.h menuitems.h +menuitems.o:menuitems.c menu.h menuitems.h +scrollingtext.o:scrollingtext.c +sofont.o:sofont.c +IosException.o:IosException.cpp +IosVector.o:IosVector.cpp +glSDL.o:glSDL.c + @echo "[$@]" && $(CC) $(CFLAGS) -DDATADIR=${DATADIR} -c $< 2>> EXT_WARNINGS + @rm -f EXT_WARNINGS +SDL_prim.o:SDL_prim.c + @echo "[$@]" && $(CC) $(CFLAGS) -DDATADIR=${DATADIR} -c $< 2>> EXT_WARNINGS + @rm -f EXT_WARNINGS + +scenar.l.c:scenar.l ${HFILES} + @echo "[$@]" && flex -oscenar.l.c scenar.l +scenar.y.c:scenar.y ${HFILES} + @echo "[$@]" && bison -y -d -o scenar.y.c scenar.y + +clean: + rm -f *~ scenar.y.c scenar.y.h scenar.l.c *.o flobopuyo* WARNINGS + rm -rf .xvpics data/.xvpics data/*/.xvpics + rm -rf FloboPuyo.app + rm -f .DS_Store data/.DS_Store data/*/.DS_Store .gdb_history + +#_install: ${OBJFILES} +# g++ $(CFLAGS) -o flobopuyo `$(SDL_CONFIG) --cflags --static-libs` -lSDL_mixer -lSDL_image ${OBJFILES} + +#install: _install +# strip --strip-all flobopuyo +# mkdir -p ${DATADIR} +# cp -r data/* ${DATADIR} +# chmod a+rx ${DATADIR} +# chmod a+rx ${DATADIR}/sfx +# chmod a+rx ${DATADIR}/gfx +# chmod a+rx ${DATADIR}/story +# chmod -R a+r ${DATADIR} +# cp ./flobopuyo ${BINDIR}/flobopuyo +# chmod a+rx ${BINDIR}/flobopuyo + +bundle_name = FloboPuyo.app +flobopuyo-static:SDL_prim.o + bison -y -d -o scenar.y.c scenar.y + flex -oscenar.l.c scenar.l + g++ -DMACOSX $(CFLAGS) -o flobopuyo-static HiScores.cpp SDL_prim.o PuyoCommander.cpp InputManager.cpp GameControls.cpp IosException.cpp IosVector.cpp main.cpp PuyoGame.cpp PuyoView.cpp PuyoIA.cpp PuyoVersion.c sofont.c menu.c menuitems.c audio.c scrollingtext.c preferences.c SDL_Painter.cpp PuyoStory.cpp scenar.y.c scenar.l.c /sw/lib/libSDL_mixer.a /sw/lib/libvorbisfile.a /sw/lib/libvorbis.a /sw/lib/libogg.a /sw/lib/libsmpeg.a /sw/lib/libSDL_image.a /sw/lib/libjpeg.a /sw/lib/libpng.a -lz `$(SDL_CONFIG) --cflags --static-libs` + +# /sw/lib/libvorbis.a + +bundle: flobopuyo-static + mkdir -p $(bundle_name)/Contents/MacOS + mkdir -p $(bundle_name)/Contents/Resources + echo "APPL????" > $(bundle_name)/Contents/PkgInfo + cp mac/Info.plist $(bundle_name)/Contents/ + cp mac/icon.icns $(bundle_name)/Contents/Resources/ + cp flobopuyo-static $(bundle_name)/Contents/MacOS/flobopuyo + cp -r data $(bundle_name)/Contents/Resources + rm -rf $(bundle_name)/Contents/Resources/data/CVS $(bundle_name)/Contents/Resources/data/*/CVS + rm -rf $(bundle_name)/Contents/Resources/data/.xvpics $(bundle_name)/Contents/Resources/data/*/.xvpics + rm -f $(bundle_name)/Contents/Resources/data/.DS_Store $(bundle_name)/Contents/Resources/data/*/.DS_Store + strip $(bundle_name)/Contents/MacOS/flobopuyo diff --git a/.#Makefile.1.27 b/.#Makefile.1.27 new file mode 100644 index 0000000..605dcbb --- /dev/null +++ b/.#Makefile.1.27 @@ -0,0 +1,165 @@ +# Makefile +# author: iOS-Software +# July 2004 + +ENABLE_AUDIO=true +ENABLE_OPENGL=false + +#BINDIR=/usr/games/ +DATADIR=\"data\" + +SDL_CONFIG=sdl-config +CC=g++ +CXX=g++ + +CFLAGS=`$(SDL_CONFIG) --cflags` -g -I/sw/include -DDATADIR=${DATADIR} + + + +HFILES= HiScores.h IosException.h IosImgProcess.h IosVector.h PuyoCommander.h\ + PuyoGame.h PuyoAnimations.h AnimatedPuyo.h PuyoIA.h PuyoPlayer.h \ + PuyoStory.h PuyoView.h SDL_prim.h audio.h menu.h menuitems.h \ + preferences.h scrollingtext.h sofont.h SDL_Painter.h PuyoVersion.h \ + InputManager.h GameControls.h HiScores.h IosImgProcess.h PuyoStarter.h + + +OBJFILES= SDL_prim.o HiScores.o scenar.y.o scenar.l.o PuyoCommander.o \ + IosException.o IosVector.o main.o PuyoGame.o PuyoVersion.o \ + PuyoView.o PuyoAnimations.o AnimatedPuyo.o PuyoIA.o sofont.o \ + menu.o menuitems.o audio.o scrollingtext.o preferences.o \ + PuyoStory.o SDL_Painter.o InputManager.o GameControls.o \ + PuyoDoomMelt.o IosImgProcess.o corona32.o corona.o corona_palette.o PuyoStarter.o + +PLATFORM=$(shell uname -s) +ifeq ($(PLATFORM), Darwin) +CFLAGS:=$(CFLAGS) -DMACOSX -UDATADIR +endif + +ifeq ($(ENABLE_AUDIO), true) +CFLAGS:=$(CFLAGS) -DUSE_AUDIO=1 +OBJFILES:=$(OBJFILES) glSDL.o +endif + +ifeq ($(ENABLE_OPENGL), true) +CFLAGS:=$(CFLAGS) -DHAVE_OPENGL=1 +OBJFILES:=$(OBJFILES) glSDL.o +endif + +CXXFLAGS=${CFLAGS} + +all: prelude flobopuyo + +flobopuyo: ${OBJFILES} + @echo "[flobopuyo]" && g++ $(CFLAGS) -o flobopuyo `$(SDL_CONFIG) --cflags --libs` -lSDL_mixer -lSDL_image ${OBJFILES} + @echo "--------------------------------------" + @echo " Compilation finished" + @[ "x`cat WARNINGS | wc -l`" != "x0" ] && echo -e "--------------------------------------\n There have been some warnings:\n" && cat WARNINGS && rm -f WARNINGS && echo "--------------------------------------" || true + @echo + @echo " Type ./flobopuyo to play." + @echo "--------------------------------------" + +prelude: + @rm -f WARNINGS + @touch WARNINGS + @echo "Compiling with CFLAGS=$(CFLAGS)" + +%.o:%.c + @echo "[$@]" && $(CC) $(CFLAGS) -c $< 2>> WARNINGS || (cat WARNINGS && false) + +%.o:%.cpp + @echo "[$@]" && $(CXX) $(CFLAGS) -c $< 2>> WARNINGS || (cat WARNINGS && false) + +PuyoDoomMelt.o:PuyoDoomMelt.c ${HFILES} +HiScores.o:HiScores.cpp HiScores.h preferences.h +PuyoCommander.o:PuyoCommander.cpp ${HFILES} +PuyoGame.o:PuyoGame.cpp ${HFILES} +PuyoIA.o:PuyoIA.cpp ${HFILES} +PuyoStory.o:PuyoStory.cpp ${HFILES} +PuyoView.o:PuyoView.cpp ${HFILES} +AnimatedPuyo.o: AnimatedPuyo.cpp ${HFILES} +PuyoAnimations.o: PuyoAnimations.cpp ${HFILES} +main.o:main.cpp ${HFILES} +preferences.o:preferences.c preferences.h +scenar.l.o:scenar.l.c ${HFILES} +scenar.y.o:scenar.y.c ${HFILES} +InputManager.o:InputManager.cpp ${HFILES} +GameControls.o:GameControls.cpp ${HFILES} +IosImgProcess.o:IosImgProcess.cpp ${HFILES} +SDL_Painter.o:SDL_Painter.cpp SDL_Painter.h +audio.o:audio.c audio.h +menu.o:menu.c menu.h menuitems.h +menuitems.o:menuitems.c menu.h menuitems.h +scrollingtext.o:scrollingtext.c +sofont.o:sofont.c +IosException.o:IosException.cpp +IosVector.o:IosVector.cpp +glSDL.o:glSDL.c + @echo "[$@]" && $(CC) $(CFLAGS) -c $< 2>> EXT_WARNINGS + @rm -f EXT_WARNINGS +SDL_prim.o:SDL_prim.c + @echo "[$@]" && $(CC) $(CFLAGS) -c $< 2>> EXT_WARNINGS + @rm -f EXT_WARNINGS +corona.o:corona.cpp +corona32.o:corona32.cpp +corona_palette.o:corona_palette.cpp + +scenar.l.c:scenar.l ${HFILES} + @echo "[$@]" && flex -oscenar.l.c scenar.l +scenar.y.c:scenar.y ${HFILES} + @echo "[$@]" && bison -y -d -o scenar.y.c scenar.y + +clean: + rm -f *~ scenar.y.c scenar.y.h scenar.l.c *.o flobopuyo* WARNINGS + rm -rf .xvpics data/.xvpics data/*/.xvpics + rm -rf FloboPuyo.app + rm -f .DS_Store data/.DS_Store data/*/.DS_Store .gdb_history + +#_install: ${OBJFILES} +# g++ $(CFLAGS) -o flobopuyo `$(SDL_CONFIG) --cflags --static-libs` -lSDL_mixer -lSDL_image ${OBJFILES} + +#install: _install +# strip --strip-all flobopuyo +# mkdir -p ${DATADIR} +# cp -r data/* ${DATADIR} +# chmod a+rx ${DATADIR} +# chmod a+rx ${DATADIR}/sfx +# chmod a+rx ${DATADIR}/gfx +# chmod a+rx ${DATADIR}/story +# chmod -R a+r ${DATADIR} +# cp ./flobopuyo ${BINDIR}/flobopuyo +# chmod a+rx ${BINDIR}/flobopuyo + +bundle_name = FloboPuyo.app + +flobopuyo-static: prelude ${OBJFILES} + @echo "[flobopuyo-static]" && g++ $(CFLAGS) -o flobopuyo-static ${OBJFILES}\ + /sw/lib/libSDL_mixer.a /sw/lib/libvorbisfile.a /sw/lib/libvorbis.a /sw/lib/libogg.a /sw/lib/libsmpeg.a /sw/lib/libSDL_image.a /sw/lib/libjpeg.a /sw/lib/libpng.a -lz `$(SDL_CONFIG) --static-libs` + @echo "--------------------------------------" + @echo " Compilation finished" + +flobopuyo-staticz:SDL_prim.o + bison -y -d -o scenar.y.c scenar.y + flex -oscenar.l.c scenar.l + g++ -DMACOSX $(CFLAGS) -o flobopuyo-static IosImgProcess.cpp HiScores.cpp SDL_prim.o PuyoCommander.cpp InputManager.cpp GameControls.cpp IosException.cpp IosVector.cpp main.cpp PuyoGame.cpp PuyoView.cpp PuyoIA.cpp PuyoVersion.c sofont.c menu.c menuitems.c audio.c scrollingtext.c preferences.c SDL_Painter.cpp PuyoStory.cpp scenar.y.c scenar.l.c /sw/lib/libSDL_mixer.a /sw/lib/libvorbisfile.a /sw/lib/libvorbis.a /sw/lib/libogg.a /sw/lib/libsmpeg.a /sw/lib/libSDL_image.a /sw/lib/libjpeg.a /sw/lib/libpng.a -lz `$(SDL_CONFIG) --cflags --static-libs` + +# /sw/lib/libvorbis.a + +bundle: flobopuyo-static + mkdir -p $(bundle_name)/Contents/MacOS + mkdir -p $(bundle_name)/Contents/Resources + echo "APPL????" > $(bundle_name)/Contents/PkgInfo + cp mac/Info.plist $(bundle_name)/Contents/ + cp mac/icon.icns $(bundle_name)/Contents/Resources/ + cp flobopuyo-static $(bundle_name)/Contents/MacOS/flobopuyo + cp -r data $(bundle_name)/Contents/Resources + rm -rf $(bundle_name)/Contents/Resources/data/CVS $(bundle_name)/Contents/Resources/data/*/CVS + rm -rf $(bundle_name)/Contents/Resources/data/.xvpics $(bundle_name)/Contents/Resources/data/*/.xvpics + rm -f $(bundle_name)/Contents/Resources/data/.DS_Store $(bundle_name)/Contents/Resources/data/*/.DS_Store + strip $(bundle_name)/Contents/MacOS/flobopuyo + +package_name = "FloBoPuyo\ 0.20" +mac-package: bundle + mkdir "$(package_name)" + mv $(bundle_name) "$(package_name)" + cp COPYING "$(package_name)" + hdiutil create -srcfolder "$(package_name)" "$(package_name).dmg" diff --git a/AnimatedPuyo.cpp b/AnimatedPuyo.cpp new file mode 100644 index 0000000..21e9cc0 --- /dev/null +++ b/AnimatedPuyo.cpp @@ -0,0 +1,198 @@ +/* FloboPuyo + * Copyright (C) 2004 + * Florent Boudet , + * Jean-Christophe Hoelt , + * Guillaume Borios + * + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#include "AnimatedPuyo.h" +#include "PuyoView.h" + +// crade, mais basta +extern IIM_Surface *puyoCircle[32]; +extern IIM_Surface *puyoEye[3]; + +AnimatedPuyo::AnimatedPuyo(PuyoState state, PuyoView *attachedView) : PuyoPuyo(state) +{ + puyoEyeState = random() % 700; + visibilityFlag = true; + this->attachedView = attachedView; +} + +AnimatedPuyo::~AnimatedPuyo() +{ + while (animationQueue.getSize() > 0) + removeCurrentAnimation(); +} + +void AnimatedPuyo::addAnimation(PuyoAnimation *animation) +{ + animationQueue.addElement(animation); +} + +PuyoAnimation * AnimatedPuyo::getCurrentAnimation() const +{ + if (animationQueue.getSize() == 0) + return NULL; + return (PuyoAnimation *)animationQueue.getElementAt(0); +} + +void AnimatedPuyo::removeCurrentAnimation() +{ + if (animationQueue.getSize() == 0) + return; + delete (PuyoAnimation *)animationQueue.getElementAt(0); + animationQueue.removeElementAt(0); +} + +void AnimatedPuyo::cycleAnimation() +{ + smallTicksCount+=2; + PuyoAnimation * animation = getCurrentAnimation(); + if ((animation != NULL)) { + animation->cycle(); + if (animation->isFinished()) { + removeCurrentAnimation(); + } + } +} + +bool AnimatedPuyo::isRenderingAnimation() const +{ + PuyoAnimation *animation = getCurrentAnimation(); + if (animation == NULL) + return false; + if (animation->isFinished()) + return false; + return animation->isEnabled(); +} + +void AnimatedPuyo::render() +{ + SDL_Painter &painter = attachedView->getPainter(); + + puyoEyeState++; + if (attachedView == NULL) + return; + if (!visibilityFlag) + return; + PuyoGame *attachedGame = attachedView->getAttachedGame(); + + bool falling = attachedGame->getFallingState() < PUYO_EMPTY; + + SDL_Rect drect; + PuyoAnimation *animation = getCurrentAnimation(); + if (!isRenderingAnimation()) { + IIM_Surface *currentSurface = attachedView->getSurfaceForPuyo(this); + if (currentSurface != NULL) { + drect.x = getScreenCoordinateX(); + drect.y = getScreenCoordinateY(); + if (this->getPuyoState() < PUYO_EMPTY) + drect.y -= attachedGame->getSemiMove() * TSIZE / 2; + drect.w = currentSurface->w; + drect.h = currentSurface->h; + painter.requestDraw(currentSurface, &drect); + + /* Main puyo show */ + if (falling && (this == attachedGame->getFallingPuyo())) + painter.requestDraw(puyoCircle[(smallTicksCount >> 2) & 0x1F], &drect); + + /* Eye management */ + if (getPuyoState() != PUYO_NEUTRAL) { + while (puyoEyeState >= 750) puyoEyeState -= 750; + int eyePhase = puyoEyeState; + if (eyePhase < 5) + painter.requestDraw(puyoEye[1], &drect); + else if (eyePhase < 15) + painter.requestDraw(puyoEye[2], &drect); + else if (eyePhase < 20) + painter.requestDraw(puyoEye[1], &drect); + else + painter.requestDraw(puyoEye[0], &drect); + } + } + } + else { + if (!animation->isFinished()) { + animation->draw(attachedGame->getSemiMove()); + } + } +} + +int AnimatedPuyo::getScreenCoordinateX() const +{ + return attachedView->getScreenCoordinateX(getPuyoX()); +} + +int AnimatedPuyo::getScreenCoordinateY() const +{ + return attachedView->getScreenCoordinateY(getPuyoY()); +} + + + + +AnimatedPuyoFactory::AnimatedPuyoFactory(PuyoView *attachedView) +{ + this->attachedView = attachedView; +} + +AnimatedPuyoFactory::~AnimatedPuyoFactory() +{ + while (puyoWalhalla.getSize() > 0) { + AnimatedPuyo *currentPuyo = (AnimatedPuyo *)(puyoWalhalla.getElementAt(0)); + puyoWalhalla.removeElementAt(0); + delete currentPuyo; + } +} + +PuyoPuyo *AnimatedPuyoFactory::createPuyo(PuyoState state) +{ + return new AnimatedPuyo(state, attachedView); +} + +void AnimatedPuyoFactory::deletePuyo(PuyoPuyo *target) +{ + puyoWalhalla.addElement(target); +} + + +void AnimatedPuyoFactory::renderWalhalla() +{ + for (int i = puyoWalhalla.getSize() - 1 ; i >= 0 ; i--) { + AnimatedPuyo *currentPuyo = (AnimatedPuyo *)(puyoWalhalla.getElementAt(i)); + currentPuyo->render(); + } +} + +void AnimatedPuyoFactory::cycleWalhalla() +{ + for (int i = puyoWalhalla.getSize() - 1 ; i >= 0 ; i--) { + AnimatedPuyo *currentPuyo = (AnimatedPuyo *)(puyoWalhalla.getElementAt(i)); + if (currentPuyo->getCurrentAnimation() != NULL) { + currentPuyo->cycleAnimation(); + } else { + puyoWalhalla.removeElementAt(i); + delete currentPuyo; + } + } +} + diff --git a/AnimatedPuyo.h b/AnimatedPuyo.h new file mode 100644 index 0000000..99dc6dc --- /dev/null +++ b/AnimatedPuyo.h @@ -0,0 +1,74 @@ +/* FloboPuyo + * Copyright (C) 2004 + * Florent Boudet , + * Jean-Christophe Hoelt , + * Guillaume Borios + * + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#ifndef _ANIMATEDPUYO +#define _ANIMATEDPUYO + +#include "SDL_Painter.h" +#include "PuyoGame.h" +#include "PuyoAnimations.h" +#include "IosVector.h" + +class PuyoView; + +class AnimatedPuyo : public PuyoPuyo { +public: + AnimatedPuyo(PuyoState state, PuyoView *attachedView); + virtual ~AnimatedPuyo(); + void addAnimation(PuyoAnimation *animation); + PuyoAnimation * getCurrentAnimation() const; + void removeCurrentAnimation(); + void cycleAnimation(); + void render(); + bool isRenderingAnimation() const; + void setVisible(bool flag) { visibilityFlag = flag; } + bool getVisible() const { return visibilityFlag; } + PuyoView *getAttachedView() const { return attachedView; } + int getScreenCoordinateX() const; + int getScreenCoordinateY() const; +private: + IosVector animationQueue; + int puyoEyeState; + unsigned int smallTicksCount; + bool visibilityFlag; + PuyoView *attachedView; +}; + + +class AnimatedPuyoFactory : public PuyoFactory { +public: + AnimatedPuyoFactory(PuyoView *attachedView); + virtual ~AnimatedPuyoFactory(); + virtual PuyoPuyo *createPuyo(PuyoState state); + virtual void deletePuyo(PuyoPuyo *target); + void renderWalhalla(); + void cycleWalhalla(); +private: + IosVector puyoWalhalla; + PuyoView *attachedView; +}; + +#endif // _ANIMATEDPUYO + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..45645b4 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Changelog b/Changelog new file mode 100644 index 0000000..0072413 --- /dev/null +++ b/Changelog @@ -0,0 +1,20 @@ +Since version 0.13: +- Improved game control settings +- Added key repetition +- Made the "main puyo" more visible by blinking +- Added score board and hall of fame +- Added a new music and game theme +- Added some sound effects +- Added OpenGL support +- Added a useless effect (corona) in the about menu +- Improved in-game animations +- Improved menu appearance +- Added a useless effect (corona) in the about menu +- Fixed eye blinking problem on rotating puyo +- Fixed a bug when rotating left in a single column +- Fixed Windows sound and fullscreen mode +- Improved the end of game detection +- Inverted the puyo orientation when they fall +- Version in about +- Improved the Makefile +- Added a changelog diff --git a/GameControls.cpp b/GameControls.cpp new file mode 100644 index 0000000..c0d2aec --- /dev/null +++ b/GameControls.cpp @@ -0,0 +1,197 @@ +#include "GameControls.h" +#include "preferences.h" + +#define NB_CONTROLS 10 +static InputSwitch *keyControls[NB_CONTROLS] = +{ + new KeyInputSwitch(SDLK_s,true), + new KeyInputSwitch(SDLK_f,true), + new KeyInputSwitch(SDLK_d,true), + new KeyInputSwitch(SDLK_UNKNOWN,true), + new KeyInputSwitch(SDLK_e,true), + new KeyInputSwitch(SDLK_LEFT,true), + new KeyInputSwitch(SDLK_RIGHT,true), + new KeyInputSwitch(SDLK_DOWN,true), + new KeyInputSwitch(SDLK_UNKNOWN,true), + new KeyInputSwitch(SDLK_UP,true) +}; + +void getKeyName(int gameEvent, char *keyName) +{ + strcpy(keyName," "); + if (keyControls[gameEvent]) + strcpy(keyName,keyControls[gameEvent]->name()); +} + +void getControlEvent(SDL_Event e, InputSwitch *input, GameControlEvent *result) +{ + result->gameEvent = GameControlEvent::kGameNone; + result->cursorEvent = GameControlEvent::kCursorNone; + result->isUp = true; + + if (e.type == SDL_QUIT) + result->cursorEvent = GameControlEvent::kQuit; + + if (input == NULL) + return; + + if (input->isValidate() && input->isDown()) + result->cursorEvent = GameControlEvent::kStart; + + if (input->isCancel() && input->isDown()) + result->cursorEvent = GameControlEvent::kBack; + + if (input->isArrowDown() && input->isDown()) + result->cursorEvent = GameControlEvent::kDown; + + if (input->isArrowUp() && input->isDown()) + result->cursorEvent = GameControlEvent::kUp; + + if (input->isPause() && input->isDown()) + result->gameEvent = GameControlEvent::kPauseGame; + + result->isUp = input->isUp(); + + if (*input == *keyControls[kPlayer1LeftControl]) + result->gameEvent = GameControlEvent::kPlayer1Left; + if (*input == *keyControls[kPlayer1RightControl]) + result->gameEvent = GameControlEvent::kPlayer1Right; + if (*input == *keyControls[kPlayer1ClockwiseControl]) + result->gameEvent = GameControlEvent::kPlayer1TurnRight; + if (*input == *keyControls[kPlayer1CounterclockwiseControl]) + result->gameEvent = GameControlEvent::kPlayer1TurnLeft; + if (*input == *keyControls[kPlayer1DownControl]) + result->gameEvent = GameControlEvent::kPlayer1Down; + + if (*input == *keyControls[kPlayer2LeftControl]) + result->gameEvent = GameControlEvent::kPlayer2Left; + if (*input == *keyControls[kPlayer2RightControl]) + result->gameEvent = GameControlEvent::kPlayer2Right; + if (*input == *keyControls[kPlayer2ClockwiseControl]) + result->gameEvent = GameControlEvent::kPlayer2TurnRight; + if (*input == *keyControls[kPlayer2CounterclockwiseControl]) + result->gameEvent = GameControlEvent::kPlayer2TurnLeft; + if (*input == *keyControls[kPlayer2DownControl]) + result->gameEvent = GameControlEvent::kPlayer2Down; +} + +void getControlEvent(SDL_Event e, GameControlEvent *result) +{ + InputSwitch *input = switchForEvent(&e); + getControlEvent(e, input, result); + + if (input) + delete input; +} + +void initGameControls() +{ + initControllers(); + loadControls(); +} + +bool tryChangeControl(int control, SDL_Event e, GameControlEvent *result) +{ + InputSwitch *input = switchForEvent(&e); + if (input == NULL) + return false; + + getControlEvent(e,input,result); + +/* + if (result->cursorEvent == GameControlEvent::kBack) + goto ret_false; + + if (result->cursorEvent == GameControlEvent::kStart) + goto ret_false; +*/ + + if (result->cursorEvent == GameControlEvent::kQuit) + goto ret_false; + + if (input->isUp()) + goto ret_false; + + if (keyControls[control] != NULL) + delete keyControls[control]; + + keyControls[control] = input; + return true; + +ret_false: + delete input; + return false; +} + +void saveControls() +{ + SetIntPreference("v50_P1Left", keyControls[kPlayer1LeftControl]->id()); + SetIntPreference("v50_P1Right", keyControls[kPlayer1RightControl]->id()); + SetIntPreference("v50_P1Down", keyControls[kPlayer1DownControl]->id()); + SetIntPreference("v50_P1Clockwise", keyControls[kPlayer1ClockwiseControl]->id()); + SetIntPreference("v50_P1Counterclockwise", keyControls[kPlayer1CounterclockwiseControl]->id()); + + SetStrPreference("v50_P1LeftS", keyControls[kPlayer1LeftControl]->name()); + SetStrPreference("v50_P1RightS", keyControls[kPlayer1RightControl]->name()); + SetStrPreference("v50_P1DownS", keyControls[kPlayer1DownControl]->name()); + SetStrPreference("v50_P1ClockwiseS", keyControls[kPlayer1ClockwiseControl]->name()); + SetStrPreference("v50_P1CounterclockwiseS", keyControls[kPlayer1CounterclockwiseControl]->name()); + + SetIntPreference("v50_P2Left", keyControls[kPlayer2LeftControl]->id()); + SetIntPreference("v50_P2Right", keyControls[kPlayer2RightControl]->id()); + SetIntPreference("v50_P2Down", keyControls[kPlayer2DownControl]->id()); + SetIntPreference("v50_P2Clockwise", keyControls[kPlayer2ClockwiseControl]->id()); + SetIntPreference("v50_P2Counterclockwise", keyControls[kPlayer2CounterclockwiseControl]->id()); + + SetStrPreference("v50_P2LeftS", keyControls[kPlayer2LeftControl]->name()); + SetStrPreference("v50_P2RightS", keyControls[kPlayer2RightControl]->name()); + SetStrPreference("v50_P2DownS", keyControls[kPlayer2DownControl]->name()); + SetStrPreference("v50_P2ClockwiseS", keyControls[kPlayer2ClockwiseControl]->name()); + SetStrPreference("v50_P2CounterclockwiseS", keyControls[kPlayer2CounterclockwiseControl]->name()); +} + +void loadControls() +{ + char name[256]; + int id; + + GetStrPreference("v50_P1LeftS", name, keyControls[kPlayer1LeftControl]->name()); + id = GetIntPreference("v50_P1Left", keyControls[kPlayer1LeftControl]->id()); + keyControls[kPlayer1LeftControl] = new InputFromIDAndName(id, name); + + GetStrPreference("v50_P1RightS", name, keyControls[kPlayer1RightControl]->name()); + id = GetIntPreference("v50_P1Right", keyControls[kPlayer1RightControl]->id()); + keyControls[kPlayer1RightControl] = new InputFromIDAndName(id, name); + + GetStrPreference("v50_P1DownS", name, keyControls[kPlayer1DownControl]->name()); + id = GetIntPreference("v50_P1Down", keyControls[kPlayer1DownControl]->id()); + keyControls[kPlayer1DownControl] = new InputFromIDAndName(id, name); + + GetStrPreference("v50_P1ClockwiseS", name, keyControls[kPlayer1ClockwiseControl]->name()); + id = GetIntPreference("v50_P1Clockwise", keyControls[kPlayer1ClockwiseControl]->id()); + keyControls[kPlayer1ClockwiseControl] = new InputFromIDAndName(id, name); + + GetStrPreference("v50_P1CounterclockwiseS", name, keyControls[kPlayer1CounterclockwiseControl]->name()); + id = GetIntPreference("v50_P1Counterclockwise", keyControls[kPlayer1CounterclockwiseControl]->id()); + keyControls[kPlayer1CounterclockwiseControl] = new InputFromIDAndName(id, name); + + GetStrPreference("v50_P2LeftS", name, keyControls[kPlayer2LeftControl]->name()); + id = GetIntPreference("v50_P2Left", keyControls[kPlayer2LeftControl]->id()); + keyControls[kPlayer2LeftControl] = new InputFromIDAndName(id, name); + + GetStrPreference("v50_P2RightS", name, keyControls[kPlayer2RightControl]->name()); + id = GetIntPreference("v50_P2Right", keyControls[kPlayer2RightControl]->id()); + keyControls[kPlayer2RightControl] = new InputFromIDAndName(id, name); + + GetStrPreference("v50_P2DownS", name, keyControls[kPlayer2DownControl]->name()); + id = GetIntPreference("v50_P2Down", keyControls[kPlayer2DownControl]->id()); + keyControls[kPlayer2DownControl] = new InputFromIDAndName(id, name); + + GetStrPreference("v50_P2ClockwiseS", name, keyControls[kPlayer2ClockwiseControl]->name()); + id = GetIntPreference("v50_P2Clockwise", keyControls[kPlayer2ClockwiseControl]->id()); + keyControls[kPlayer2ClockwiseControl] = new InputFromIDAndName(id, name); + + GetStrPreference("v50_P2CounterclockwiseS", name, keyControls[kPlayer2CounterclockwiseControl]->name()); + id = GetIntPreference("v50_P2Counterclockwise", keyControls[kPlayer2CounterclockwiseControl]->id()); + keyControls[kPlayer2CounterclockwiseControl] = new InputFromIDAndName(id, name); +} diff --git a/GameControls.h b/GameControls.h new file mode 100644 index 0000000..f304ce3 --- /dev/null +++ b/GameControls.h @@ -0,0 +1,62 @@ +#ifndef _GAME_CONT_H +#define _GAME_CONT_H + +#include "glSDL.h" +#include "InputManager.h" + +typedef struct GameControlEvent { + enum { + kGameNone, + kPauseGame, + kPlayer1Left, + kPlayer1Right, + kPlayer1TurnLeft, + kPlayer1TurnRight, + kPlayer1Down, + kPlayer2Left, + kPlayer2Right, + kPlayer2TurnLeft, + kPlayer2TurnRight, + kPlayer2Down, + kGameLastKey + } gameEvent; + enum { + kCursorNone, + kUp, + kDown, + kLeft, + kRight, + kStart, + kBack, + kQuit, + kCursorLastKey + } cursorEvent; + bool isUp; +} GameControlEvent; + + +enum { + kPlayer1LeftControl = 0, + kPlayer1RightControl = 1, + kPlayer1DownControl = 2, + kPlayer1ClockwiseControl = 3, + kPlayer1CounterclockwiseControl = 4, + kPlayer2LeftControl = 5, + kPlayer2RightControl = 6, + kPlayer2DownControl = 7, + kPlayer2ClockwiseControl = 8, + kPlayer2CounterclockwiseControl = 9 +}; + + +void initGameControls(); +void getControlEvent(SDL_Event e, GameControlEvent *result); + +void getKeyName(int control, char *keyName); +bool tryChangeControl(int control, SDL_Event e, GameControlEvent *result); + +void saveControls(); +void loadControls(); + +#endif + diff --git a/HiScores.cpp b/HiScores.cpp new file mode 100644 index 0000000..f939842 --- /dev/null +++ b/HiScores.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include "preferences.h" +#include "HiScores.h" + +static hiscore HS[kHiScoresNumber]; +static bool loaded = false; + +void initHiScores(const char * const defaultNames[kHiScoresNumber]) +{ + char HSID[8]; + + for (int i=0; iname,name,kHiScoreNameLenght); + tmp->name[kHiScoreNameLenght]=0; + tmp->score = score; + + char HSID[8]; + + for (int i=0; iscore >= HS[i].score) + { + if (retour == -1) retour = i; + + sprintf(HSID,"HSN%2d",i); + SetStrPreference(HSID,tmp->name); + sprintf(HSID,"HSS%2d",i); + SetIntPreference(HSID,tmp->score); + + memcpy(tmp2,&(HS[i]),sizeof(hiscore)); + memcpy(&(HS[i]),tmp,sizeof(hiscore)); + memcpy(tmp,tmp2,sizeof(hiscore)); + } + } + + free(tmp); + free(tmp2); + + return retour; +} diff --git a/HiScores.h b/HiScores.h new file mode 100644 index 0000000..2b6dc86 --- /dev/null +++ b/HiScores.h @@ -0,0 +1,19 @@ +#ifndef _HISCORE +#define _HISCORE + +#define kHiScoresNumber 10 + +#define kHiScoreNameLenght 20 + +typedef struct hiscore { + char name[kHiScoreNameLenght+1]; + int score; +} hiscore; + +void initHiScores(const char * const defaultNames[kHiScoresNumber]); + +hiscore * getHiScores(void); + +int setHiScore(int score, const char * name); + +#endif // _HISCORE diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..4f859a7 --- /dev/null +++ b/INSTALL @@ -0,0 +1,3 @@ +$ make + +Some options are available on top of the Makefile. diff --git a/InputManager.cpp b/InputManager.cpp new file mode 100644 index 0000000..3659b06 --- /dev/null +++ b/InputManager.cpp @@ -0,0 +1,254 @@ +#include "InputManager.h" + +typedef struct SdlKeyName { + int key; + char name[50]; +} SdlKeyName; + +static const SdlKeyName sdlKeyDictionnary[] = { + { SDLK_UNKNOWN, " "}, + { SDLK_BACKSPACE, "Backspace" }, + { SDLK_TAB, "Tab" }, + { SDLK_CLEAR, "Clear" }, + { SDLK_RETURN, "Return" }, + { SDLK_PAUSE, "Pause" }, + { SDLK_ESCAPE, "Escape" }, + { SDLK_SPACE, "Space" }, + { SDLK_DELETE, "Delete" }, + { SDLK_KP0, "KP 0" }, + { SDLK_KP1, "KP 1" }, + { SDLK_KP2, "KP 2" }, + { SDLK_KP3, "KP 3" }, + { SDLK_KP4, "KP 4" }, + { SDLK_KP5, "KP 5" }, + { SDLK_KP6, "KP 6" }, + { SDLK_KP7, "KP 7" }, + { SDLK_KP8, "KP 8" }, + { SDLK_KP9, "KP 9" }, + { SDLK_UP, "Up Arrow" }, + { SDLK_DOWN, "Down Arrow" }, + { SDLK_LEFT, "Left Arrow" }, + { SDLK_RIGHT, "Right Arrow"}, + { SDLK_INSERT, "Insert" }, + { SDLK_HOME, "Home" }, + { SDLK_END, "End" }, + { SDLK_PAGEUP, "Page Up" }, + { SDLK_PAGEDOWN, "Page Down" }, + { SDLK_F1, "F1" }, + { SDLK_F2, "F2" }, + { SDLK_F3, "F3" }, + { SDLK_F4, "F4" }, + { SDLK_F5, "F5" }, + { SDLK_F6, "F6" }, + { SDLK_F7, "F7" }, + { SDLK_F8, "F8" }, + { SDLK_F9, "F9" }, + { SDLK_F10, "F10" }, + { SDLK_F11, "F11" }, + { SDLK_F12, "F12" }, + { SDLK_F13, "F13" }, + { SDLK_F14, "F14" }, + { SDLK_F15, "F15" }, + { SDLK_NUMLOCK, "Num Lock" }, + { SDLK_CAPSLOCK, "Caps Lock" }, + { SDLK_SCROLLOCK, "Scroll Lock"}, + { SDLK_RSHIFT, "Right Shift"}, + { SDLK_LSHIFT, "Left Shift" }, + { SDLK_RCTRL, "Right Ctrl" }, + { SDLK_LCTRL, "Left Ctrl" }, + { SDLK_RALT, "Right Alt" }, + { SDLK_LALT, "Left Alt" }, + { SDLK_RMETA, "Right Meta" }, + { SDLK_LMETA, "Left Meta" }, + { SDLK_RSUPER, "Right Windows" }, + { SDLK_LSUPER, "Left Windows" }, + { SDLK_MODE, "Mode Shift" }, + { SDLK_HELP, "Help" }, + { SDLK_PRINT, "Print Screen" }, + { SDLK_SYSREQ, "Sys Rq" }, + { SDLK_BREAK, "Break" }, + { SDLK_MENU, "Menu" }, + { SDLK_POWER, "Power" }, + { SDLK_EURO, "Euro" } +}; + +static const int sdlKeyDictionnarySize = sizeof(sdlKeyDictionnary) / sizeof(SdlKeyName); + +static SDL_Joystick *joystick[16]; +static int numJoysticks; +static int axisSave[16][16]; + +/* KEY Input */ + +KeyInputSwitch::KeyInputSwitch(int keysym, bool isup) : InputSwitch(isup), keysym(keysym) { + keyName[0] = 0; +} + +const char *KeyInputSwitch::name() const { + + if (keyName[0] == 0) { + for (int i = 0 ; i < sdlKeyDictionnarySize ; i++) { + if (sdlKeyDictionnary[i].key == keysym) { + strcpy(keyName, sdlKeyDictionnary[i].name); + break; + } + } + if (keyName[0] == 0) { + keyName[0] = (char)keysym; + keyName[1] = 0; + } + } + return keyName; +} + +int KeyInputSwitch::id() const { + return (1000 - keysym); +} + +bool KeyInputSwitch::isArrowUp() const { + return keysym == SDLK_UP; +} +bool KeyInputSwitch::isArrowDown() const { + return keysym == SDLK_DOWN; +} +bool KeyInputSwitch::isValidate() const { + return keysym == SDLK_RETURN; +} +bool KeyInputSwitch::isCancel() const { + return keysym == SDLK_ESCAPE; +} +bool KeyInputSwitch::isPause() const { + return keysym == SDLK_p; +} + + +/* JOY BUTTON */ + +JoystickSwitch::JoystickSwitch(int which, int button, bool isup) +: InputSwitch(isup),which(which),button(button) +{ + keyName[0] = 0; +} + +const char *JoystickSwitch::name() const { + + if (keyName[0] == 0) + sprintf(keyName, "JOY%d - Button %d", which, button); + return keyName; +} + +int JoystickSwitch::id() const { + return 2000 + which * 50 + button; +} + +bool JoystickSwitch::isValidate() const { + return button == 0; +} +bool JoystickSwitch::isCancel() const { + return button == 2; +} + +/* JOY AXIS SWITCH */ + +JoystickAxisSwitch::JoystickAxisSwitch(int which, int axis, bool maximum, bool isup) + : InputSwitch(isup),which(which),axis(axis),maximum(maximum) +{ + keyName[0] = 0; +} + +const char *JoystickAxisSwitch::name() const { + + if (keyName[0] == 0) + sprintf(keyName, "JOY%d - Axis %d - %s", which, axis, (maximum?"Max":"Min")); + return keyName; +} + +int JoystickAxisSwitch::id() const { + return 3000 + which * 50 + axis + (maximum?1:0); +} + +bool JoystickAxisSwitch::isArrowUp() const { + return (axis == 1) && (!maximum); +} +bool JoystickAxisSwitch::isArrowDown() const { + return (axis == 1) && (maximum); +} +/** EVENT HANDLERS */ + +InputSwitch *waitInputSwitch() +{ + SDL_Event e; + if (!SDL_WaitEvent(&e)) + return NULL; + + return switchForEvent(&e); +} + +InputSwitch *checkInputSwitch() +{ + SDL_Event e; + if (!SDL_PollEvent(&e)) + return NULL; + + return switchForEvent(&e); +} + + + +InputSwitch *switchForEvent(SDL_Event *e) +{ + int prevaxis; + switch (e->type) + { + case SDL_JOYBUTTONDOWN: + return new JoystickSwitch(e->jbutton.which, e->jbutton.button, false); + + case SDL_JOYBUTTONUP: + return new JoystickSwitch(e->jbutton.which, e->jbutton.button, true); + + case SDL_JOYAXISMOTION: + prevaxis = axisSave[e->jaxis.which][e->jaxis.axis]; + axisSave[e->jaxis.which][e->jaxis.axis] = e->jaxis.value; + + if ((e->jaxis.value > JOYSTICK_THRESHOLD) && (prevaxis <= JOYSTICK_THRESHOLD)) + return new JoystickAxisSwitch(e->jaxis.which, e->jaxis.axis, true, false); + if ((e->jaxis.value <= JOYSTICK_THRESHOLD) && (prevaxis > JOYSTICK_THRESHOLD)) + return new JoystickAxisSwitch(e->jaxis.which, e->jaxis.axis, true, true); + if ((e->jaxis.value < -JOYSTICK_THRESHOLD) && (prevaxis >= -JOYSTICK_THRESHOLD)) + return new JoystickAxisSwitch(e->jaxis.which, e->jaxis.axis, false, false); + if ((e->jaxis.value >= -JOYSTICK_THRESHOLD) && (prevaxis < -JOYSTICK_THRESHOLD)) + return new JoystickAxisSwitch(e->jaxis.which, e->jaxis.axis, false, true); + + return NULL; + + case SDL_KEYDOWN: + return new KeyInputSwitch(e->key.keysym.sym, false); + case SDL_KEYUP: + return new KeyInputSwitch(e->key.keysym.sym, true); + default: + return NULL; + } +} + + +void initControllers() +{ + numJoysticks = SDL_NumJoysticks(); + for( int i=0 ; i < numJoysticks ; i++ ) + { + joystick[numJoysticks - i - 1] = SDL_JoystickOpen(i); + } + SDL_JoystickEventState(SDL_ENABLE); + + for (int i = 0 ; i < 16 ; i++) + for (int j = 0 ; j < 16 ; j++) + axisSave[i][j] = 0; +} + +void closeControllers() +{ + for( int i=0 ; i < numJoysticks ; i++ ) + { + SDL_JoystickClose(joystick[i]); + } +} diff --git a/InputManager.h b/InputManager.h new file mode 100644 index 0000000..43f7917 --- /dev/null +++ b/InputManager.h @@ -0,0 +1,104 @@ +#ifndef _INPUT_SW_MAN_H +#define _INPUT_SW_MAN_H + +#include "glSDL.h" +#include + +const int JOYSTICK_THRESHOLD = 25000; + +class InputSwitch +{ + public: + + InputSwitch(bool isup = false) : isup(isup) {} + virtual ~InputSwitch() {} + virtual const char *name() const = 0; + virtual int id() const = 0; + + inline bool operator==(const InputSwitch &other) const { + return id() == other.id(); + } + + inline bool isUp() const { return isup; } + inline bool isDown() const { return !isup; } + + /* Meta-Informations about the switch */ + virtual bool isArrowUp() const { return false; } + virtual bool isArrowDown() const { return false; } + virtual bool isValidate() const { return false; } + virtual bool isCancel() const { return false; } + virtual bool isPause() const { return false; } + + private: + bool isup; +}; + +void initControllers(); +void closeControllers(); + +InputSwitch *switchForEvent(SDL_Event *e); +InputSwitch *waitInputSwitch(); +InputSwitch *checkInputSwitch(); + +class KeyInputSwitch : public InputSwitch +{ + public: + KeyInputSwitch(int keysym, bool isup); + const char *name() const; + int id() const; + + virtual bool isArrowUp() const; + virtual bool isArrowDown() const; + virtual bool isValidate() const; + virtual bool isCancel() const; + virtual bool isPause() const; + + private: + int keysym; + mutable char keyName[256]; +}; + +class JoystickSwitch : public InputSwitch +{ + private: + int which; + int button; + mutable char keyName[256]; + + public: + JoystickSwitch(int which, int button, bool isup); + const char *name() const; + int id() const; + + virtual bool isValidate() const; + virtual bool isCancel() const; +}; + +class JoystickAxisSwitch : public InputSwitch +{ + private: + int which; + int axis; + bool maximum; + mutable char keyName[256]; + + public: + JoystickAxisSwitch(int which, int axis, bool maximum, bool isup); + const char *name() const; + int id() const; + + virtual bool isArrowUp() const; + virtual bool isArrowDown() const; +}; + +class InputFromIDAndName : public InputSwitch +{ + int _id; + char _name[256]; + public: + InputFromIDAndName(int _id, const char *_name) : _id(_id) {strcpy(this->_name, _name);} + const char *name() const { return _name; } + int id() const { return _id; } +}; + +#endif diff --git a/IosException.cpp b/IosException.cpp new file mode 100644 index 0000000..0a11618 --- /dev/null +++ b/IosException.cpp @@ -0,0 +1,47 @@ +/* Ultimate Othello 1678 + * Copyright (C) 2002 Florent Boudet + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#include "IosException.h" +#include +#include +#include + +Exception::Exception(char *exception) +{ + message = (char *) malloc(strlen(exception) + 1); + strcpy(message, exception); +} + +Exception::~Exception() +{ + free(message); +} + +const char *Exception::getMessage() +{ + return message; +} + +void Exception::printMessage() +{ + fprintf(stderr, "Exception thrown: %s\n", message); +} + diff --git a/IosException.h b/IosException.h new file mode 100644 index 0000000..0a19b6b --- /dev/null +++ b/IosException.h @@ -0,0 +1,36 @@ +/* Ultimate Othello 1678 + * Copyright (C) 2002 Florent Boudet + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#ifndef _EXCEPTION +#define _EXCEPTION + +class Exception { + public: + Exception(char *exception); + ~Exception(); + void printMessage(); + const char *getMessage(); + private: + char *message; +}; + +#endif // _EXCEPTION + diff --git a/IosImgProcess.cpp b/IosImgProcess.cpp new file mode 100644 index 0000000..078a6e0 --- /dev/null +++ b/IosImgProcess.cpp @@ -0,0 +1,402 @@ +#include "IosImgProcess.h" +#include +#include + + +extern char *dataFolder; + + +/** Image loading... free... conversion */ + +bool fullscreen = true; +bool useGL = false; + +IIM_Surface imgList[4096]; +int imgListSize = 0; + +IIM_Surface * IIM_Load_DisplayFormat (const char *fname) +{ + char path[1024]; + SDL_Surface *tmpsurf, *retsurf; + sprintf(path, "%s/gfx/%s", dataFolder, fname); + tmpsurf = IMG_Load (path); + if (tmpsurf==0) { + fprintf(stderr,"Could not load %s\n", path); + exit(1); + } + retsurf = SDL_DisplayFormat (tmpsurf); + SDL_FreeSurface (tmpsurf); + return IIM_RegisterImg(retsurf, false); +} + +IIM_Surface * IIM_Load_DisplayFormatAlpha (const char *fname) +{ + char path[1024]; + SDL_Surface *tmpsurf, *retsurf; + sprintf(path, "%s/gfx/%s", dataFolder, fname); + tmpsurf = IMG_Load (path); + if (tmpsurf==0) { + fprintf(stderr,"Could not load %s\n", path); + exit(1); + } + retsurf = SDL_DisplayFormatAlpha (tmpsurf); + SDL_SetAlpha (retsurf, SDL_SRCALPHA | (useGL?0:SDL_RLEACCEL), SDL_ALPHA_OPAQUE); + SDL_FreeSurface (tmpsurf); + return IIM_RegisterImg(retsurf, true); +} + +void IIM_Free(IIM_Surface *img) +{ + for (int i=0; iw; + imgList[imgListSize].h = img->h; + return &(imgList[imgListSize++]); +} + +void IIM_ReConvertAll(void) +{ + for (int i=0; i=i2)&&(i1>=i3)) return i1; + if ((i2>=i3)&&(i2>=i1)) return i2; + return i3; +} + +static int min3(int i1, int i2, int i3) +{ + if ((i1<=i2)&&(i1<=i3)) return i1; + if ((i2<=i3)&&(i2<=i1)) return i2; + return i3; +} + +/* Extracting color components from a 32-bit color value + * pre: SDL_Locked(surface) */ +RGBA iim_surface_get_rgba(SDL_Surface *surface, Uint32 x, Uint32 y) +{ + Uint32 temp, pixel; + RGBA result; + int index = x*surface->format->BytesPerPixel + y*surface->pitch; + SDL_PixelFormat *fmt = surface->format; + pixel=*(Uint32*)((char*)surface->pixels+index); + + /* Get Red component */ + temp=pixel&fmt->Rmask; /* Isolate red component */ + temp=temp>>fmt->Rshift;/* Shift it down to 8-bit */ + temp=temp<Rloss; /* Expand to a full 8-bit number */ + result.red=(Uint8)temp; + + /* Get Green component */ + temp=pixel&fmt->Gmask; /* Isolate green component */ + temp=temp>>fmt->Gshift;/* Shift it down to 8-bit */ + temp=temp<Gloss; /* Expand to a full 8-bit number */ + result.green=(Uint8)temp; + + /* Get Blue component */ + temp=pixel&fmt->Bmask; /* Isolate blue component */ + temp=temp>>fmt->Bshift;/* Shift it down to 8-bit */ + temp=temp<Bloss; /* Expand to a full 8-bit number */ + result.blue=(Uint8)temp; + + /* Get Alpha component */ + temp=pixel&fmt->Amask; /* Isolate alpha component */ + temp=temp>>fmt->Ashift;/* Shift it down to 8-bit */ + temp=temp<Aloss; /* Expand to a full 8-bit number */ + result.alpha=(Uint8)temp; + + return result; +} + +Uint8 iim_rgba2gray(RGBA col) +{ + unsigned int level; + level = col.red; + level += col.green; + level += col.blue; + level /= 3; // Volontairement assombrie. + return level; +} + +/* pre: SDL_Locked(surface) */ +void iim_surface_set_rgb(SDL_Surface *surface, + Uint32 x, Uint32 y, RGBA c) +{ + Uint32 temp, pixel; + int index = x*surface->format->BytesPerPixel + y*surface->pitch; + SDL_PixelFormat *fmt = surface->format; + temp = ~(fmt->Rmask | fmt->Gmask | fmt->Bmask); + + pixel = *(Uint32*)((char*)surface->pixels+index); + pixel &= temp; + + /* Get Red component */ + temp = c.red >> fmt->Rloss; + temp = temp << fmt->Rshift; + pixel |= temp; + + /* Get Green component */ + temp = c.green >> fmt->Gloss; + temp = temp << fmt->Gshift; + pixel |= temp; + + /* Get Blue component */ + temp = c.blue >> fmt->Bloss; + temp = temp << fmt->Bshift; + pixel |= temp; + + *(Uint32*)((char*)surface->pixels+index) = pixel; +} + +/* pre: SDL_Locked(surface) */ +void iim_surface_set_rgba(SDL_Surface *surface, + Uint32 x, Uint32 y, RGBA c) +{ + Uint32 temp, pixel = 0; + int index = x*surface->format->BytesPerPixel + y*surface->pitch; + SDL_PixelFormat *fmt = surface->format; + + /* Get Red component */ + temp = c.red >> fmt->Rloss; + temp = temp << fmt->Rshift; + pixel |= temp; + + /* Get Green component */ + temp = c.green >> fmt->Gloss; + temp = temp << fmt->Gshift; + pixel |= temp; + + /* Get Blue component */ + temp = c.blue >> fmt->Bloss; + temp = temp << fmt->Bshift; + pixel |= temp; + + /* Get Alpha component */ + temp = c.alpha >> fmt->Aloss; + temp = temp << fmt->Ashift; + pixel |= temp; + + *(Uint32*)((char*)surface->pixels+index) = pixel; +} + +//-- RGB<->HSV conversion + +//-- RGB, each 0 to 255 +//-- H = 0.0 to 360.0 (corresponding to 0..360.0 degrees around hexcone) +//-- S = 0.0 (shade of gray) to 1.0 (pure color) +//-- V = 0.0 (black) to 1.0 (white) + +//-- Based on C Code in "Computer Graphics -- Principles and Practice," +//-- Foley et al, 1996, pp. 592,593. +HSVA iim_rgba2hsva(RGBA c) +{ + HSVA res; + float minVal = (float)min3(c.red, c.blue, c.green); + res.value = (float)max3(c.red, c.green, c.blue); + float delta = res.value - minVal; + + // -- Calculate saturation: saturation is 0 if r, g and b are all 0 + if (res.value == 0.0f) + res.saturation = 0.0f; + else + res.saturation = delta / res.value; + + if (res.saturation == 0.0f) + res.hue = 0.0f; // Achromatic: When s = 0, h is undefined but who cares + else // Chromatic + if (res.value == c.red) // between yellow and magenta [degrees] + res.hue = 60.0f * (c.green - c.blue) / delta; + else if (res.value == c.green) // between cyan and yellow + res.hue = 120.0 + 60.0f * (c.blue - c.red) / delta; + else // between magenta and cyan + res.hue = 240.0f + 60.0f * (c.red - c.green) / delta; + + if (res.hue < 0.0f) res.hue += 360.0f; + // return a list of values as an rgb object would not be sensible + res.value /= 255.0f; + res.alpha = c.alpha; + return res; +} + +RGBA iim_hsva2rgba(HSVA c) +{ + float red=0.0f,green=0.0f,blue=0.0f,hueTemp=0.0f; + if (c.saturation == 0.0f) // color is on black-and-white center line + { + red = c.value; // achromatic: shades of gray + green = c.value; // supposedly invalid for h=0 but who cares + blue = c.value; + } + else { // chromatic color + if (c.hue == 360.0f) // 360 degrees same as 0 degrees + hueTemp=0.0f; + else + hueTemp = c.hue; + + hueTemp = hueTemp/60.0f; // h is now in [0,6) + float i = floor(hueTemp); // largest integer <= h + float f = hueTemp-i; // fractional part of h + + float p = c.value*(1.0f - c.saturation); + float q = c.value*(1.0f-(c.saturation*f)); + float t = c.value*(1.0f-(c.saturation*(1.0-f))); + + switch((int)i) { + case 0: + red = c.value; + green = t; + blue = p; + break; + case 1: + red = q; + green = c.value; + blue = p; + break; + case 2: + red = p; + green = c.value; + blue = t; + break; + case 3: + red = p; + green = q; + blue = c.value; + break; + case 4: + red = t; + green = p; + blue = c.value; + break; + case 5: + red = c.value; + green = p; + blue = q; + break; + } + } + RGBA ret; + ret.red = (Uint8)(red * 255.0f); + ret.green = (Uint8)(green * 255.0f); + ret.blue = (Uint8)(blue * 255.0f); + ret.alpha = c.alpha; + return ret; +} + +IIM_Surface *iim_surface_shift_hue(IIM_Surface *isrc, float hue_offset) +{ + SDL_Surface *src = isrc->surf; + SDL_PixelFormat *fmt = src->format; + SDL_Surface *ret = SDL_CreateRGBSurface(src->flags, src->w, src->h, 32, + fmt->Rmask, fmt->Gmask, + fmt->Bmask, fmt->Amask); + SDL_LockSurface(src); + SDL_LockSurface(ret); + for (int y=src->h; y--;) + { + for (int x=src->w; x--;) + { + RGBA rgba = iim_surface_get_rgba(src,x,y); + HSVA hsva = iim_rgba2hsva(rgba); + hsva.hue += hue_offset; + if (hsva.hue > 360.0f) hsva.hue -= 360.0f; + if (hsva.hue < 0.0f) hsva.hue += 360.0f; + rgba = iim_hsva2rgba(hsva); + iim_surface_set_rgba(ret,x,y,rgba); + } + } + SDL_UnlockSurface(ret); + SDL_UnlockSurface(src); + SDL_Surface *ret2 = SDL_DisplayFormatAlpha(ret); + SDL_SetAlpha(ret2, SDL_SRCALPHA | (useGL?0:SDL_RLEACCEL), SDL_ALPHA_OPAQUE); + SDL_FreeSurface(ret); + ret = isrc->surf; + isrc->surf = SDL_DisplayFormatAlpha(ret); + SDL_SetAlpha(isrc->surf, SDL_SRCALPHA | (useGL?0:SDL_RLEACCEL), SDL_ALPHA_OPAQUE); + SDL_FreeSurface(ret); + return IIM_RegisterImg(ret2, true); +} + +IIM_Surface *iim_surface_set_value(IIM_Surface *isrc, float value) +{ + SDL_Surface *src = isrc->surf; + SDL_PixelFormat *fmt = src->format; + SDL_Surface *ret = SDL_CreateRGBSurface(src->flags, src->w, src->h, 32, + fmt->Rmask, fmt->Gmask, + fmt->Bmask, fmt->Amask); + SDL_LockSurface(src); + SDL_LockSurface(ret); + for (int y=src->h; y--;) + { + for (int x=src->w; x--;) + { + RGBA rgba = iim_surface_get_rgba(src,x,y); + HSVA hsva = iim_rgba2hsva(rgba); + hsva.value = value; + if (hsva.value > 1.0f) hsva.value = 1.0f; + if (hsva.value < 0.0f) hsva.value = 0.0f; + rgba = iim_hsva2rgba(hsva); + iim_surface_set_rgba(ret,x,y,rgba); + } + } + SDL_UnlockSurface(src); + SDL_UnlockSurface(ret); + SDL_Surface *ret2 = SDL_DisplayFormatAlpha(ret); + SDL_SetAlpha(ret2, SDL_SRCALPHA | (useGL?0:SDL_RLEACCEL), SDL_ALPHA_OPAQUE); + SDL_FreeSurface(ret); + return IIM_RegisterImg(ret2, true); +} + +void iim_surface_convert_to_gray(IIM_Surface *isrc) +{ + SDL_Surface *src = isrc->surf; + SDL_PixelFormat *fmt = src->format; + SDL_LockSurface(src); + for (int y=src->h; y--;) + { + for (int x=src->w; x--;) + { + RGBA rgba = iim_surface_get_rgba(src,x,y); + Uint8 l = iim_rgba2gray(rgba); + rgba.red = rgba.blue = rgba.green = l; + iim_surface_set_rgb(src,x,y,rgba); + } + } + SDL_UnlockSurface(src); + isrc->surf = SDL_DisplayFormat(src); + SDL_FreeSurface(src); +} diff --git a/IosImgProcess.h b/IosImgProcess.h new file mode 100644 index 0000000..a9d21e1 --- /dev/null +++ b/IosImgProcess.h @@ -0,0 +1,61 @@ +#ifndef _IOS_IMG_PROCESS +#define _IOS_IMG_PROCESS + +#include "glSDL.h" + +typedef struct _RGBA { + Uint8 red; + Uint8 green; + Uint8 blue; + Uint8 alpha; +} RGBA; + +typedef struct _HSVA { + float hue; + float saturation; + float value; + Uint8 alpha; +} HSVA; + +/* Extracting color components from a 32-bit color value + * pre: SDL_Locked(surface) */ +RGBA iim_surface_get_rgba(SDL_Surface *surface, Uint32 x, Uint32 y); + +/* pre: SDL_Locked(surface) */ +void iim_surface_set_rgb(SDL_Surface *surface, + Uint32 x, Uint32 y, RGBA c); + +/* pre: SDL_Locked(surface) */ +void iim_surface_set_rgba(SDL_Surface *surface, + Uint32 x, Uint32 y, RGBA c); + +//-- RGB<->HSV conversion + +//-- RGB, each 0 to 255 +//-- H = 0.0 to 360.0 (corresponding to 0..360.0 degrees around hexcone) +//-- S = 0.0 (shade of gray) to 1.0 (pure color) +//-- V = 0.0 (black) to 1.0 (white) + +//-- Based on C Code in "Computer Graphics -- Principles and Practice," +//-- Foley et al, 1996, pp. 592,593. +HSVA iim_rgba2hsva(RGBA c); +RGBA iim_hsva2rgba(HSVA c); + +typedef struct { + SDL_Surface *surf; + int w; + int h; + bool isAlpha; +} IIM_Surface; + +IIM_Surface *iim_surface_shift_hue(IIM_Surface *src, float hue_offset); +IIM_Surface *iim_surface_set_value(IIM_Surface *src, float value); +void iim_surface_convert_to_gray(IIM_Surface *src); + +IIM_Surface * IIM_Load_DisplayFormat (const char *path); +IIM_Surface * IIM_Load_DisplayFormatAlpha (const char *path); +void IIM_Free(IIM_Surface *img); +IIM_Surface * IIM_RegisterImg(SDL_Surface *img, bool isAlpha); +void IIM_ReConvertAll(void); + +#endif diff --git a/IosVector.cpp b/IosVector.cpp new file mode 100644 index 0000000..3881acb --- /dev/null +++ b/IosVector.cpp @@ -0,0 +1,107 @@ +/* Ultimate Othello 1678 + * Copyright (C) 2002 Florent Boudet + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#include "IosVector.h" +#include "IosException.h" +#include +#include + +IosVector::IosVector() +{ + vectorData = (void **) malloc(sizeof(void *) * vectorInitialSize); + vectorCapacity = vectorInitialSize; + vectorSize = 0; +} + +IosVector::IosVector(int size) +{ + vectorData = (void **) malloc(sizeof(void *) * size); + vectorCapacity = size; + vectorSize = 0; +} + + +IosVector::~IosVector() +{ +} + +void IosVector::addElement(void *element) +{ + if (vectorSize >= vectorCapacity) + increaseVectorSize(); + vectorData[vectorSize++] = element; +} + +void * IosVector::getElementAt(const int index) const +{ + if (index >= vectorSize) + throw new Exception("Vector index out of bounds"); + return vectorData[index]; +} + +void IosVector::removeElementAt(const int index) +{ + if (index >= vectorSize) + throw new Exception("Vector index out of bounds"); + vectorSize--; + if (index == vectorSize) + return; + for (int i = index ; i < vectorSize ; i++) { + vectorData[i] = vectorData[i+1]; + } +} + +void IosVector::removeElement(void *element) +{ + for (int i = 0 ; i < vectorSize ; i++) { + if (vectorData[i] == element) { + removeElementAt(i--); + } + } +} + +void IosVector::removeAllElements() +{ + vectorSize = 0; +} + +int IosVector::getSize() const +{ + return vectorSize; +} + +int IosVector::getCapacity() const +{ + return vectorCapacity; +} + +void IosVector::increaseVectorSize() +{ + vectorData = (void **) realloc(vectorData, sizeof(void *) * (vectorSize + vectorSizeIncrement)); + vectorCapacity += vectorSizeIncrement; +} + +void IosVector::dumpVector() const { + fprintf(stderr, "Size: %d\n", getSize()); + for (int i = 0, j = getSize() ; i < j ; i++) + fprintf(stderr, "elt[%d]=%d ", i, (int)getElementAt(i)); + fprintf(stderr, "\n"); +} diff --git a/IosVector.h b/IosVector.h new file mode 100644 index 0000000..809f10f --- /dev/null +++ b/IosVector.h @@ -0,0 +1,48 @@ +/* Ultimate Othello 1678 + * Copyright (C) 2002 Florent Boudet + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#ifndef _IOSVECTOR +#define _IOSVECTOR + +class IosVector { +public: + IosVector(); + IosVector(const int size); + ~IosVector(); + void addElement(void *element); + void * getElementAt(const int index) const; + void removeElementAt(const int index); + void removeElement(void *element); + void removeAllElements(); + int getSize() const; + int getCapacity() const; + void dumpVector() const; +private: + void **vectorData; + static const int vectorInitialSize = 10; + static const int vectorSizeIncrement = 10; + int vectorCapacity; + int vectorSize; + + void increaseVectorSize(); +}; + +#endif // _IOSVECTOR diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a02b747 --- /dev/null +++ b/Makefile @@ -0,0 +1,225 @@ +# Makefile +# author: iOS-Software +# July 2004 + +############### +# Settings + +ENABLE_AUDIO=true +ENABLE_OPENGL=false +ENABLE_DGA=false + +DEBUG_MODE=false + +# Unix/Linux settings +PREFIX=/usr/local +DATADIR=$(PREFIX)/share/games/flobopuyo +INSTALL_BINDIR=$(DESTDIR)/$(PREFIX)/games +INSTALL_DATADIR=$(DESTDIR)/$(DATADIR) + +# Mac settings +macimage_name=FloBoPuyo\ $(VERSION) +bundle_name=FloboPuyo.app + +# Win32 settings +CYGWIN_VERSION=CYGWIN_NT-5.1 +WINZIP_NAME=flobopuyowin32 +WINSDLINCLUDE=/home/$(USER)/SDL-1.2.7/include +WINSDLDEVLIBS=/home/$(USER)/SDL-1.2.7/lib +WINSDLRUNTIME=/home/$(USER)/SDL-1.2.7/lib +# +########## + + +############## +# Autodetection + +PLATFORM=$(shell uname -s) + +VERSION=$(shell grep "\#define FLOBOPUYOVERSION" PuyoVersion.c | cut -d "\"" -f 2) +# +############## + +SDL_CONFIG=sdl-config +CC=g++ +CXX=g++ + +CFLAGS= -DDATADIR=\"${DATADIR}\" +LDFLAGS= + +ifneq ($(PLATFORM), $(CYGWIN_VERSION)) +CFLAGS:=$(CFLAGS) `$(SDL_CONFIG) --cflags` -I/sw/include +LDFLAGS:=$(LDFLAGS) `$(SDL_CONFIG) --cflags --libs` +endif + +HFILES= HiScores.h IosException.h IosImgProcess.h IosVector.h PuyoCommander.h\ + PuyoGame.h PuyoAnimations.h AnimatedPuyo.h PuyoIA.h PuyoPlayer.h \ + PuyoStory.h PuyoView.h SDL_prim.h audio.h menu.h menuitems.h \ + preferences.h scrollingtext.h sofont.h SDL_Painter.h PuyoVersion.h \ + InputManager.h GameControls.h HiScores.h IosImgProcess.h PuyoStarter.h + + +OBJFILES= SDL_prim.o HiScores.o scenar.y.o scenar.l.o PuyoCommander.o \ + IosException.o IosVector.o main.o PuyoGame.o PuyoVersion.o \ + PuyoView.o PuyoAnimations.o AnimatedPuyo.o PuyoIA.o sofont.o \ + menu.o menuitems.o audio.o scrollingtext.o preferences.o \ + PuyoStory.o SDL_Painter.o InputManager.o GameControls.o \ + PuyoDoomMelt.o IosImgProcess.o corona32.o corona.o corona_palette.o\ + PuyoStarter.o + + +################ +# Mac OS X +ifeq ($(PLATFORM), Darwin) +CFLAGS:=$(CFLAGS) -DMACOSX -UDATADIR +endif + +################ +# Win32 +ifeq ($(PLATFORM), $(CYGWIN_VERSION)) +CFLAGS:=$(CFLAGS) -mno-cygwin -mwindows -DWIN32 -DYY_NEVER_INTERACTIVE=1 -I$(WINSDLINCLUDE) +LDFLAGS:=$(LDFLAGS) -L$(WINSDLDEVLIBS) -lmingw32 -ljpeg -lzlib -lpng1 -lSDL_image -lSDL_mixer -lSDL -lSDLmain +endif + +ifeq ($(ENABLE_AUDIO), true) +CFLAGS:=$(CFLAGS) -DUSE_AUDIO=1 +OBJFILES:=$(OBJFILES) +endif + +ifeq ($(ENABLE_OPENGL), true) +CFLAGS:=$(CFLAGS) -DHAVE_OPENGL=1 +OBJFILES:=$(OBJFILES) glSDL.o +ifeq ($(PLATFORM), Linux) +LDFLAGS:=$(LDFLAGS) -lGL +endif +endif + +ifeq ($(ENABLE_DGA), true) +ifeq ($(PLATFORM), Linux) +CFLAGS:=$(CFLAGS) -DUSE_DGA=1 +endif +endif + +ifeq ($(DEBUG), true) +CFLAGS:=$(CFLAGS) -DDEBUG=1 -g +LDFLAGS:=$(LDFLAGS) -g +endif + +CXXFLAGS=${CFLAGS} + +all: prelude flobopuyo + +flobopuyo: ${OBJFILES} + @echo "[flobopuyo]" && g++ $(CFLAGS) $(LDFLAGS) -o flobopuyo -lSDL_mixer -lSDL_image ${OBJFILES} + @echo "--------------------------------------" + @echo " Compilation finished" + @[ "x`cat WARNINGS | wc -l`" != "x0" ] && echo -e "--------------------------------------\n There have been some warnings:\n" && cat WARNINGS && rm -f WARNINGS && echo "--------------------------------------" || true + @echo + @echo " Type ./flobopuyo to play." + @echo "--------------------------------------" + +prelude: + @rm -f WARNINGS + @touch WARNINGS + @echo "Compiling with CFLAGS=$(CFLAGS)" + @echo "Compiling with LDFLAGS=$(LDFLAGS)" + +%.o:%.c + @echo "[$@]" && $(CC) $(CFLAGS) -c $< 2>> WARNINGS || (cat WARNINGS && false) + +%.o:%.cpp + @echo "[$@]" && $(CXX) $(CFLAGS) -c $< 2>> WARNINGS || (cat WARNINGS && false) + +PuyoDoomMelt.o:PuyoDoomMelt.c ${HFILES} +HiScores.o:HiScores.cpp HiScores.h preferences.h +PuyoCommander.o:PuyoCommander.cpp ${HFILES} +PuyoGame.o:PuyoGame.cpp ${HFILES} +PuyoIA.o:PuyoIA.cpp ${HFILES} +PuyoStory.o:PuyoStory.cpp ${HFILES} +PuyoView.o:PuyoView.cpp ${HFILES} +AnimatedPuyo.o: AnimatedPuyo.cpp ${HFILES} +PuyoAnimations.o: PuyoAnimations.cpp ${HFILES} +main.o:main.cpp ${HFILES} +preferences.o:preferences.c preferences.h +scenar.l.o:scenar.l.c ${HFILES} +scenar.y.o:scenar.y.c ${HFILES} +InputManager.o:InputManager.cpp ${HFILES} +GameControls.o:GameControls.cpp ${HFILES} +IosImgProcess.o:IosImgProcess.cpp ${HFILES} +SDL_Painter.o:SDL_Painter.cpp SDL_Painter.h +audio.o:audio.c audio.h +menu.o:menu.c menu.h menuitems.h +menuitems.o:menuitems.c menu.h menuitems.h +scrollingtext.o:scrollingtext.c +sofont.o:sofont.c +IosException.o:IosException.cpp +IosVector.o:IosVector.cpp +glSDL.o:glSDL.c + @echo "[$@]" && $(CC) $(CFLAGS) -c $< 2>> EXT_WARNINGS + @rm -f EXT_WARNINGS +SDL_prim.o:SDL_prim.c + @echo "[$@]" && $(CC) $(CFLAGS) -c $< 2>> EXT_WARNINGS + @rm -f EXT_WARNINGS +corona.o:corona.cpp +corona32.o:corona32.cpp +corona_palette.o:corona_palette.cpp + +scenar.l.c:scenar.l ${HFILES} + @echo "[$@]" && flex -oscenar.l.c scenar.l +scenar.y.c:scenar.y ${HFILES} + @echo "[$@]" && bison -y -d -o scenar.y.c scenar.y + +clean: + rm -f *~ scenar.y.c scenar.y.h scenar.l.c *.o flobopuyo* WARNINGS + rm -rf .xvpics data/.xvpics data/*/.xvpics + rm -rf $(bundle_name) + rm -rf $(macimage_name) + rm -f $(macimage_name).dmg + rm -f .DS_Store */.DS_Store */*/.DS_Store .gdb_history + +install: flobopuyo + strip flobopuyo + mkdir -p ${INSTALL_BINDIR} + mkdir -p ${INSTALL_DATADIR} + cp -r data/* ${INSTALL_DATADIR} + chmod a+rx ${INSTALL_DATADIR} + chmod a+rx ${INSTALL_DATADIR}/sfx + chmod a+rx ${INSTALL_DATADIR}/gfx + chmod a+rx ${INSTALL_DATADIR}/story + chmod -R a+r ${INSTALL_DATADIR} + cp ./flobopuyo ${INSTALL_BINDIR}/flobopuyo + chmod a+rx ${INSTALL_BINDIR}/flobopuyo + +flobopuyo-static: prelude ${OBJFILES} + @echo "[flobopuyo-static]" && g++ $(CFLAGS) -o flobopuyo-static ${OBJFILES}\ + /sw/lib/libSDL_mixer.a /sw/lib/libvorbisfile.a /sw/lib/libvorbis.a /sw/lib/libogg.a /sw/lib/libsmpeg.a /sw/lib/libSDL_image.a /sw/lib/libjpeg.a /sw/lib/libpng.a -lz `$(SDL_CONFIG) --static-libs` + @echo "--------------------------------------" + @echo " Compilation finished" + +bundle: flobopuyo-static + mkdir -p $(bundle_name)/Contents/MacOS + mkdir -p $(bundle_name)/Contents/Resources + echo "APPL????" > $(bundle_name)/Contents/PkgInfo + sed "s/@@VERSION@@/$(VERSION)/" mac/Info.plist > $(bundle_name)/Contents/Info.plist + cp mac/icon.icns $(bundle_name)/Contents/Resources/ + cp flobopuyo-static $(bundle_name)/Contents/MacOS/flobopuyo + cp -r data $(bundle_name)/Contents/Resources + rm -rf $(bundle_name)/Contents/Resources/data/CVS $(bundle_name)/Contents/Resources/data/*/CVS + rm -rf $(bundle_name)/Contents/Resources/data/.xvpics $(bundle_name)/Contents/Resources/data/*/.xvpics + rm -f $(bundle_name)/Contents/Resources/data/.DS_Store $(bundle_name)/Contents/Resources/data/*/.DS_Store + strip $(bundle_name)/Contents/MacOS/flobopuyo + +mac-package: bundle + mkdir -p $(macimage_name) + cp -r $(bundle_name) $(macimage_name) + cp COPYING $(macimage_name) + hdiutil create -srcfolder $(macimage_name) $(macimage_name).dmg + hdiutil internet-enable $(macimage_name).dmg + +win-package: flobopuyo + mkdir -p $(WINZIP_NAME) + cp -r data $(WINZIP_NAME) + cp flobopuyo.exe $(WINZIP_NAME) + cp COPYING $(WINZIP_NAME) + cp $(WINSDLRUNTIME)/*.dll $(WINZIP_NAME) + zip -r $(WINZIP_NAME) $(WINZIP_NAME) diff --git a/Makefile.flobo b/Makefile.flobo new file mode 100644 index 0000000..c777deb --- /dev/null +++ b/Makefile.flobo @@ -0,0 +1,151 @@ +# Makefile +# author: iOS-Software +# July 2004 + +ENABLE_AUDIO=true +ENABLE_OPENGL=true + + +DATADIR=\"data\" + +SDL_CONFIG=sdl-config +CC=g++ +CXX=g++ + +CFLAGS=`$(SDL_CONFIG) --cflags` -g -I/sw/include -DDATADIR=${DATADIR} + +HFILES= HiScores.h IosException.h IosImgProcess.h IosVector.h PuyoCommander.h\ + PuyoGame.h PuyoAnimations.h AnimatedPuyo.h PuyoIA.h PuyoPlayer.h \ + PuyoStory.h PuyoView.h SDL_prim.h audio.h menu.h menuitems.h \ + preferences.h scrollingtext.h sofont.h SDL_Painter.h PuyoVersion.h \ + InputManager.h GameControls.h HiScores.h IosImgProcess.h PuyoStarter.h + + +OBJFILES= SDL_prim.o HiScores.o scenar.y.o scenar.l.o PuyoCommander.o \ + IosException.o IosVector.o main.o PuyoGame.o PuyoVersion.o \ + PuyoView.o PuyoAnimations.o AnimatedPuyo.o PuyoIA.o sofont.o \ + menu.o menuitems.o audio.o scrollingtext.o preferences.o \ + PuyoStory.o SDL_Painter.o InputManager.o GameControls.o \ + PuyoDoomMelt.o IosImgProcess.o corona32.o corona.o corona_palette.o PuyoStarter.o + +PLATFORM=$(shell uname -s) +ifeq ($(PLATFORM), Darwin) +CFLAGS:=$(CFLAGS) -DMACOSX -UDATADIR +endif + +ifeq ($(ENABLE_AUDIO), true) +CFLAGS:=$(CFLAGS) -DUSE_AUDIO=1 +OBJFILES:=$(OBJFILES) +endif + +ifeq ($(ENABLE_OPENGL), true) +CFLAGS:=$(CFLAGS) -DHAVE_OPENGL=1 +OBJFILES:=$(OBJFILES) glSDL.o +ifeq ($(PLATFORM), Linux) +LDFLAGS:=$(LDFLAGS) -lGL +endif +endif + +CXXFLAGS=${CFLAGS} + +all: prelude flobopuyo + +flobopuyo: ${OBJFILES} + @echo "[flobopuyo]" && g++ $(CFLAGS) $(LDFLAGS) -o flobopuyo `$(SDL_CONFIG) --cflags --libs` -lSDL_mixer -lSDL_image ${OBJFILES} + @echo "--------------------------------------" + @echo " Compilation finished" + @[ "x`cat WARNINGS | wc -l`" != "x0" ] && echo -e "--------------------------------------\n There have been some warnings:\n" && cat WARNINGS && rm -f WARNINGS && echo "--------------------------------------" || true + @echo + @echo " Type ./flobopuyo to play." + @echo "--------------------------------------" + +prelude: + @rm -f WARNINGS + @touch WARNINGS + @echo "Compiling with CFLAGS=$(CFLAGS)" + +%.o:%.c + @echo "[$@]" && $(CC) $(CFLAGS) -c $< 2>> WARNINGS || (cat WARNINGS && false) + +%.o:%.cpp + @echo "[$@]" && $(CXX) $(CFLAGS) -c $< 2>> WARNINGS || (cat WARNINGS && false) + +PuyoDoomMelt.o:PuyoDoomMelt.c ${HFILES} +HiScores.o:HiScores.cpp HiScores.h preferences.h +PuyoCommander.o:PuyoCommander.cpp ${HFILES} +PuyoGame.o:PuyoGame.cpp ${HFILES} +PuyoIA.o:PuyoIA.cpp ${HFILES} +PuyoStory.o:PuyoStory.cpp ${HFILES} +PuyoView.o:PuyoView.cpp ${HFILES} +AnimatedPuyo.o: AnimatedPuyo.cpp ${HFILES} +PuyoAnimations.o: PuyoAnimations.cpp ${HFILES} +main.o:main.cpp ${HFILES} +preferences.o:preferences.c preferences.h +scenar.l.o:scenar.l.c ${HFILES} +scenar.y.o:scenar.y.c ${HFILES} +InputManager.o:InputManager.cpp ${HFILES} +GameControls.o:GameControls.cpp ${HFILES} +IosImgProcess.o:IosImgProcess.cpp ${HFILES} +SDL_Painter.o:SDL_Painter.cpp SDL_Painter.h +audio.o:audio.c audio.h +menu.o:menu.c menu.h menuitems.h +menuitems.o:menuitems.c menu.h menuitems.h +scrollingtext.o:scrollingtext.c +sofont.o:sofont.c +IosException.o:IosException.cpp +IosVector.o:IosVector.cpp +glSDL.o:glSDL.c + @echo "[$@]" && $(CC) $(CFLAGS) -c $< 2>> EXT_WARNINGS + @rm -f EXT_WARNINGS +SDL_prim.o:SDL_prim.c + @echo "[$@]" && $(CC) $(CFLAGS) -c $< 2>> EXT_WARNINGS + @rm -f EXT_WARNINGS +corona.o:corona.cpp +corona32.o:corona32.cpp +corona_palette.o:corona_palette.cpp + +scenar.l.c:scenar.l ${HFILES} + @echo "[$@]" && flex -oscenar.l.c scenar.l +scenar.y.c:scenar.y ${HFILES} + @echo "[$@]" && bison -y -d -o scenar.y.c scenar.y + +clean: + rm -f *~ scenar.y.c scenar.y.h scenar.l.c *.o flobopuyo* WARNINGS + rm -rf .xvpics data/.xvpics data/*/.xvpics + rm -rf FloboPuyo.app + rm -f .DS_Store data/.DS_Store data/*/.DS_Store .gdb_history + +bundle_name = FloboPuyo.app + +flobopuyo-static: prelude ${OBJFILES} + @echo "[flobopuyo-static]" && g++ $(CFLAGS) -o flobopuyo-static ${OBJFILES}\ + /sw/lib/libSDL_mixer.a /sw/lib/libvorbisfile.a /sw/lib/libvorbis.a /sw/lib/libogg.a /sw/lib/libsmpeg.a /sw/lib/libSDL_image.a /sw/lib/libjpeg.a /sw/lib/libpng.a -lz `$(SDL_CONFIG) --static-libs` + @echo "--------------------------------------" + @echo " Compilation finished" + +flobopuyo-staticz:SDL_prim.o + bison -y -d -o scenar.y.c scenar.y + flex -oscenar.l.c scenar.l + g++ -DMACOSX $(CFLAGS) -o flobopuyo-static IosImgProcess.cpp HiScores.cpp SDL_prim.o PuyoCommander.cpp InputManager.cpp GameControls.cpp IosException.cpp IosVector.cpp main.cpp PuyoGame.cpp PuyoView.cpp PuyoIA.cpp PuyoVersion.c sofont.c menu.c menuitems.c audio.c scrollingtext.c preferences.c SDL_Painter.cpp PuyoStory.cpp scenar.y.c scenar.l.c /sw/lib/libSDL_mixer.a /sw/lib/libvorbisfile.a /sw/lib/libvorbis.a /sw/lib/libogg.a /sw/lib/libsmpeg.a /sw/lib/libSDL_image.a /sw/lib/libjpeg.a /sw/lib/libpng.a -lz `$(SDL_CONFIG) --cflags --static-libs` + +# /sw/lib/libvorbis.a + +bundle: flobopuyo-static + mkdir -p $(bundle_name)/Contents/MacOS + mkdir -p $(bundle_name)/Contents/Resources + echo "APPL????" > $(bundle_name)/Contents/PkgInfo + cp mac/Info.plist $(bundle_name)/Contents/ + cp mac/icon.icns $(bundle_name)/Contents/Resources/ + cp flobopuyo-static $(bundle_name)/Contents/MacOS/flobopuyo + cp -r data $(bundle_name)/Contents/Resources + rm -rf $(bundle_name)/Contents/Resources/data/CVS $(bundle_name)/Contents/Resources/data/*/CVS + rm -rf $(bundle_name)/Contents/Resources/data/.xvpics $(bundle_name)/Contents/Resources/data/*/.xvpics + rm -f $(bundle_name)/Contents/Resources/data/.DS_Store $(bundle_name)/Contents/Resources/data/*/.DS_Store + strip $(bundle_name)/Contents/MacOS/flobopuyo + +package_name = "FloBoPuyo\ 0.20" +mac-package: bundle + mkdir "$(package_name)" + mv $(bundle_name) "$(package_name)" + cp COPYING "$(package_name)" + hdiutil create -srcfolder "$(package_name)" "$(package_name).dmg" diff --git a/PuyoAnimations.cpp b/PuyoAnimations.cpp new file mode 100644 index 0000000..1ecbf58 --- /dev/null +++ b/PuyoAnimations.cpp @@ -0,0 +1,374 @@ +/* FloboPuyo + * Copyright (C) 2004 + * Florent Boudet , + * Jean-Christophe Hoelt , + * Guillaume Borios + * + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#include "PuyoAnimations.h" +#include "AnimatedPuyo.h" +#include "PuyoView.h" +#include "IosImgProcess.h" +#include "SDL_Painter.h" +#include "audio.h" + +/* not clean, but basta */ +extern SDL_Painter painter; +extern IIM_Surface *puyoEyes; +extern IIM_Surface *puyoEye[3]; +extern IIM_Surface *puyoEyesSwirl[4]; +extern IIM_Surface *shrinkingPuyo[5][5]; +extern IIM_Surface *explodingPuyo[5][5]; + +/* Base class implementation */ +Animation::Animation() +{ + finishedFlag = false; + enabled = true; +} + +bool Animation::isFinished() const +{ + return finishedFlag; +} + +bool Animation::isEnabled() const +{ + return enabled; +} + + +/* Neutral falling animation */ +IIM_Surface *NeutralAnimation::neutral = NULL; +NeutralAnimation::NeutralAnimation(AnimatedPuyo &puyo, int delay) : PuyoAnimation(puyo) +{ + if (neutral == NULL) + neutral = PuyoView::getSurfaceForState(PUYO_NEUTRAL); + this->X = attachedPuyo.getScreenCoordinateX(); + this->Y = attachedPuyo.getScreenCoordinateY(); + this->currentY = attachedPuyo.getAttachedView()->getScreenCoordinateY(0); + step = 0; + this->delay = delay; + attachedPuyo.getAttachedView()->disallowCycle(); +} + +void NeutralAnimation::cycle() +{ + if (delay >=0) { + delay--; + } + else { + currentY += (int)step; + step += 0.5; + if (currentY >= Y) { + audio_sound_play(sound_bim[random() % 2]); + finishedFlag = true; + attachedPuyo.getAttachedView()->allowCycle(); + } + } +} + +void NeutralAnimation::draw(int semiMove) +{ + SDL_Rect drect; + drect.x = X; + drect.y = currentY; + drect.w = neutral->w; + drect.h = neutral->h; + painter.requestDraw(neutral, &drect); +} + +/* Animation synchronization helper */ +AnimationSynchronizer::AnimationSynchronizer() +{ + currentCounter = 0; + currentUsage = 0; +} + +void AnimationSynchronizer::push() +{ + currentCounter++; +} + +void AnimationSynchronizer::pop() +{ + currentCounter--; +} + +bool AnimationSynchronizer::isSynchronized() +{ + return (currentCounter <= 0); +} + +void AnimationSynchronizer::incrementUsage() +{ + currentUsage++; +} + +void AnimationSynchronizer::decrementUsage() +{ + currentUsage--; + if (currentUsage == 0) + delete this; +} + +/* Companion turning around main puyo animation */ +TurningAnimation::TurningAnimation(AnimatedPuyo &companionPuyo, int vector, + bool counterclockwise) : PuyoAnimation(companionPuyo) +{ + this->counterclockwise = counterclockwise; + companionVector = vector; + targetSurface = attachedPuyo.getAttachedView()->getSurfaceForPuyo(&attachedPuyo); + cpt = 0; + angle = 0; + step = (3.14 / 2) / 4; +} + +void TurningAnimation::cycle() +{ + if (cpt == 0) { + audio_sound_play(sound_fff); + } + cpt++; + angle += step; + if (cpt == 4) + finishedFlag = true; +} + +void TurningAnimation::draw(int semiMove) +{ + if (targetSurface == NULL) + return; + X = attachedPuyo.getScreenCoordinateX(); + Y = attachedPuyo.getScreenCoordinateY(); + + float offsetA = sin(angle) * TSIZE; + float offsetB = cos(angle) * TSIZE * (counterclockwise ? -1 : 1); + SDL_Rect drect, drect2; + drect.w = targetSurface->w; + drect.h = targetSurface->h; + drect.y = -semiMove * TSIZE / 2; + switch (companionVector) { + case 0: + drect.x = (short)(X - offsetB); + drect.y += (short)(Y + offsetA - TSIZE); + break; + case 1: + drect.x = (short)(X - offsetA + TSIZE); + drect.y += (short)(Y - offsetB); + break; + case 2: + drect.x = (short)(X + offsetB); + drect.y += (short)(Y - offsetA + TSIZE); + break; + case 3: + drect.x = (short)(X + offsetA - TSIZE); + drect.y += (short)(Y + offsetB); + break; + + case -3: + drect.x = (short)(X + offsetB); + drect.y += (short)(Y + offsetA - TSIZE); + break; + } + drect2 = drect; + painter.requestDraw(targetSurface, &drect); + painter.requestDraw(puyoEyes, &drect2); +} + +/* Puyo falling and bouncing animation */ + +const int FallingAnimation::BOUNCING_OFFSET_NUM = 12; +const int FallingAnimation::BOUNCING_OFFSET[] = { -1, -3, -5, -4, -2, 0, -6, -9, -11, -9, -6, 0 }; + +FallingAnimation::FallingAnimation(AnimatedPuyo &puyo, int originY, int xOffset, int yOffset, int step) : PuyoAnimation(puyo) +{ + this->xOffset = xOffset; + this->yOffset = yOffset; + this->step = 0/*step*/; + this->X = (attachedPuyo.getPuyoX()*TSIZE) + xOffset; + this->Y = (originY*TSIZE) + yOffset; + puyoFace = PuyoView::getSurfaceForState(attachedPuyo.getPuyoState()); + bouncing = BOUNCING_OFFSET_NUM - 1; + if (originY == attachedPuyo.getPuyoY()) { + bouncing = -1; + } + attachedPuyo.getAttachedView()->disallowCycle(); +} + +void FallingAnimation::cycle() +{ + Y += step++; + if (Y >= (attachedPuyo.getPuyoY()*TSIZE) + yOffset) + { + bouncing--; + if (bouncing < 0) { + finishedFlag = true; + audio_sound_play(sound_bam1); + attachedPuyo.getAttachedView()->allowCycle(); + } + else { + if (BOUNCING_OFFSET[bouncing] == 0) + audio_sound_play(sound_bam1); + } + Y = (attachedPuyo.getPuyoY()*TSIZE) + yOffset; + } +} + +void FallingAnimation::draw(int semiMove) +{ + if (puyoFace) { + SDL_Rect drect; + drect.x = X; + drect.y = Y + (bouncing>=0?BOUNCING_OFFSET[bouncing]:0); + // drect.y = -semiMove() * TSIZE / 2; + drect.w = puyoFace->w; + drect.h = puyoFace->h; + painter.requestDraw(puyoFace, &drect); + if (attachedPuyo.getPuyoState() != PUYO_NEUTRAL) + painter.requestDraw(puyoEyesSwirl[(bouncing/2)%4], &drect); + } +} + +/* Puyo exploding and vanishing animation */ +VanishAnimation::VanishAnimation(AnimatedPuyo &puyo, int delay, int xOffset, int yOffset, AnimationSynchronizer *synchronizer) : PuyoAnimation(puyo) +{ + puyoFace = PuyoView::getSurfaceForState(attachedPuyo.getPuyoState()); + this->xOffset = xOffset; + this->yOffset = yOffset; + this->X = (attachedPuyo.getPuyoX()*TSIZE) + xOffset; + this->Y = (attachedPuyo.getPuyoY()*TSIZE) + yOffset; + this->color = attachedPuyo.getPuyoState(); + if (color > PUYO_EMPTY) + color -= PUYO_BLUE; + iter = 0; + once = false; + enabled = false; + this->synchronizer = synchronizer; + synchronizer->incrementUsage(); + synchronizer->push(); + this->delay = delay; + attachedPuyo.getAttachedView()->disallowCycle(); +} + +VanishAnimation::~VanishAnimation() +{ + synchronizer->decrementUsage(); +} + +void VanishAnimation::cycle() +{ + if (once == false) { + once = true; + synchronizer->pop(); + } + else if (synchronizer->isSynchronized()) { + enabled = true; + iter ++; + if (iter == 20 + delay) { + attachedPuyo.getAttachedView()->allowCycle(); + } + else if (iter == 50 + delay) { + finishedFlag = true; + attachedPuyo.setVisible(false); + } + } +} + +void VanishAnimation::draw(int semiMove) +{ + if (iter < (10 + delay)) { + if (puyoFace && (iter % 2 == 0)) { + SDL_Rect drect; + drect.x = X; + drect.y = Y; + drect.w = puyoFace->w; + drect.h = puyoFace->h; + painter.requestDraw(puyoFace, &drect); + if (color != PUYO_NEUTRAL) + painter.requestDraw(puyoEyes, &drect); + } + } + else { + if (puyoFace) { + SDL_Rect drect, xrect; + drect.x = X; + drect.y = Y;// + (2.5 * pow(iter - 16, 2) - 108); + drect.w = puyoFace->w; + drect.h = puyoFace->h; + int iter2 = iter - 10 - delay; + int shrinkingImage = (iter - 10 - delay) / 4; + if (shrinkingImage < 4) { + painter.requestDraw(shrinkingPuyo[shrinkingImage][color], &drect); + int xrectY = Y + (int)(2.5 * pow(iter - 16 - delay, 2) - 108); + xrect.w = explodingPuyo[shrinkingImage][color]->w; + xrect.h = explodingPuyo[shrinkingImage][color]->h; + xrect.x = X - iter2 * iter2; + xrect.y = xrectY; + painter.requestDraw(explodingPuyo[shrinkingImage][color], &xrect); + xrect.x = X - iter2; + xrect.y = xrectY + iter2; + painter.requestDraw(explodingPuyo[shrinkingImage][color], &xrect); + xrect.x = X + iter2; + xrect.y = xrectY + iter2; + painter.requestDraw(explodingPuyo[shrinkingImage][color], &xrect); + xrect.x = X + iter2 * iter2; + xrect.y = xrectY; + painter.requestDraw(explodingPuyo[shrinkingImage][color], &xrect); + } + } + } +} + +VanishSoundAnimation::VanishSoundAnimation(int phase, AnimationSynchronizer *synchronizer) +{ + once = false; + step = 0; + this->phase = phase; + this->synchronizer = synchronizer; + synchronizer->incrementUsage(); + synchronizer->push(); +} + +VanishSoundAnimation::~VanishSoundAnimation() +{ + synchronizer->decrementUsage(); +} + +void VanishSoundAnimation::cycle() +{ + if (once == false) { + once = true; + synchronizer->pop(); + } + else if (synchronizer->isSynchronized()) { + step++; + if (step == 1) { + audio_sound_play(sound_splash[phase>7?7:phase]); + finishedFlag = true; + } + } +} + +void VanishSoundAnimation::draw(int semiMove) +{ + // do nothing +} diff --git a/PuyoAnimations.h b/PuyoAnimations.h new file mode 100644 index 0000000..5ea2130 --- /dev/null +++ b/PuyoAnimations.h @@ -0,0 +1,146 @@ +/* FloboPuyo + * Copyright (C) 2004 + * Florent Boudet , + * Jean-Christophe Hoelt , + * Guillaume Borios + * + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#ifndef _PUYOANIMATIONS +#define _PUYOANIMATIONS + +#include +#include +#include "glSDL.h" +#include "IosImgProcess.h" +#include "PuyoGame.h" + +class AnimatedPuyo; + +/* Abstract Animation class */ +class Animation { +public: + Animation(); + bool isFinished() const; + bool isEnabled() const; + virtual void cycle() = 0; + virtual void draw(int semiMove) = 0; +protected: + bool finishedFlag; + bool enabled; +}; + +/* Abstract animation class for puyos */ +class PuyoAnimation : public Animation{ +public: + PuyoAnimation(AnimatedPuyo &puyo):attachedPuyo(puyo) {} +protected: + AnimatedPuyo &attachedPuyo; +}; + +/* Animation synchronization helper */ +class AnimationSynchronizer { +public: + AnimationSynchronizer(); + void push(); + void pop(); + bool isSynchronized(); + void incrementUsage(); + void decrementUsage(); +private: + int currentCounter; + int currentUsage; +}; + +/* Neutral falling animation */ +class NeutralAnimation : public PuyoAnimation { + public: + NeutralAnimation(AnimatedPuyo &puyo, int delay); + void cycle(); + void draw(int semiMove); + private: + static IIM_Surface *neutral; + int X, Y, currentY; + float step; + int delay; +}; + +/* Companion turning around main puyo animation */ +class TurningAnimation : public PuyoAnimation { +public: + TurningAnimation(AnimatedPuyo &companionPuyo, + int vector, bool counterclockwise); + void cycle(); + void draw(int semiMove); +private: + int X, Y, companionVector, cpt; + float angle; + float step; + IIM_Surface *targetSurface; + bool counterclockwise; +}; + +/* Puyo falling and bouncing animation */ +class FallingAnimation : public PuyoAnimation { +public: + FallingAnimation(AnimatedPuyo &puyo, + int originY, int xOffset, int yOffset, int step); + void cycle(); + void draw(int semiMove); +private: + int xOffset, yOffset, step; + int X, Y; + int bouncing; + IIM_Surface *puyoFace; + static const int BOUNCING_OFFSET_NUM; + static const int BOUNCING_OFFSET[]; +}; + +/* Puyo exploding and vanishing animation */ +class VanishAnimation : public PuyoAnimation { +public: + VanishAnimation(AnimatedPuyo &puyo, int delay, int xOffset, int yOffset, AnimationSynchronizer *synchronizer); + virtual ~VanishAnimation(); + void cycle(); + void draw(int semiMove); +private: + IIM_Surface *puyoFace; + int xOffset, yOffset; + int X, Y, iter, color; + AnimationSynchronizer *synchronizer; + bool once; + int delay; +}; + +class VanishSoundAnimation : public Animation { +public: + VanishSoundAnimation(int phase, AnimationSynchronizer *synchronizer); + virtual ~VanishSoundAnimation(); + void cycle(); + void draw(int semiMove); +private: + int phase; + int step; + bool once; + AnimationSynchronizer *synchronizer; +}; + +#endif // _PUYOANIMATIONS + diff --git a/PuyoCommander.cpp b/PuyoCommander.cpp new file mode 100644 index 0000000..b9a6ddf --- /dev/null +++ b/PuyoCommander.cpp @@ -0,0 +1,1458 @@ +#include +#include +#include +#include +#include "glSDL.h" +#include "PuyoCommander.h" +#include "PuyoStarter.h" +#include "PuyoVersion.h" +#include "PuyoStory.h" +#include "preferences.h" +#include "InputManager.h" +#include "HiScores.h" +#include "PuyoDoomMelt.h" +#include "IosImgProcess.h" + +#ifndef DATADIR +extern char *DATADIR; +#endif + +extern const char *p1name; +extern const char *p2name; +extern int GAME_ACCEL; +extern int gameLevel; + +char *kYouDidIt = "You Dit It!!!"; +char *kNextLevel = "Next:\t\t"; +char *kLooser = "Looser!!!"; +char *kCurrentLevel = "Level:\t\t"; +char *kContinueLeft = "Continues:\t\t"; +char *kGameOver = "Game Over!!!"; +char *kYouGotToLevel = "You Get To "; +char *kHitActionToContinue = "Hit Action to continue..."; +char *kHitActionForMenu = "Hit Action for menu..."; +char *kContinue = "Continue?"; +char *kContinueGame= "Continue Game"; +char *kAbortGame = "Abort Game"; +char *kOptions = "Options"; +char *kPlayer = "Player"; +char *kScore = "Score:\t"; +char *kPlayerName = "Player Name:\t"; +char *kPlayer1Name = "P1 Name:\t"; +char *kPlayer2Name = "P2 Name:\t"; + + +static const char *k01 = " 1 - "; +static const char *k02 = " 2 - "; +static const char *k03 = " 3 - "; +static const char *k04 = " 4 - "; +static const char *k05 = " 5 - "; +static const char *k06 = " 6 - "; +static const char *k07 = " 7 - "; +static const char *k08 = " 8 - "; +static const char *k09 = " 9 - "; +static const char *k10 = "10 - "; + +static const char *kMustRestart1 = "Major Video Mode Change..."; +static const char *kMustRestart2 = "This will take effect next time you restart Flobo Puyo."; +char *kHighScores = "Hall of Fame"; + +static char *kMusicVolume = "MusicVolume"; +static char *kAudioVolume = "AudioVolume"; + +char *kCongratulations = "Congratulations!!!"; +char *kPuyosInvasion = " You stopped Puyo's invasion.\n" + " Peace on Earth is restored!!"; + +static char *kAudioFX = "Audio FX\t"; +static char *kMusic = "Music\t"; +static char *kFullScreen = "FullScreen\t"; +static char *kOpenGL = "Use OpenGL\t"; +static char *kControls = "Change controls..."; +static char *kGameLevel = "Choose Game Level"; +static char *kLevelEasy = "Easy"; +static char *kLevelMedium = "Medium"; +static char *kLevelHard = "Hard"; + +static char *kPlayer1Left = "P2 Left:\t"; +static char *kPlayer1Right = "P2 Right:\t"; +static char *kPlayer1Down = "P2 Down:\t"; +static char *kPlayer1Clockwise = "P2 Turn L:\t"; +static char *kPlayer1Counterclockwise = "P2 Turn R:\t"; + +static char *kPlayer2Left = "P1 Left:\t"; +static char *kPlayer2Right = "P1 Right:\t"; +static char *kPlayer2Down = "P1 Down:\t"; +static char *kPlayer2Clockwise = "P1 Turn L:\t"; +static char *kPlayer2Counterclockwise = "P1 Turn R:\t"; + +static char *kRules01 = "Puyos are fancy smiling bubbles..."; +static char *kRules02 = "But they can really be invading sometimes!"; +static char *kRules03 = "Send them away by making groups of 4 or more."; +static char *kRules04 = "Try to make large groups, or many at the same time"; +static char *kRules05 = "to send more bad ghost Puyos to your opponent."; + +static char *kAbout01 = "FloboPuyo is an iOS-software production.\t\t"; +static char *kAbout02 = "Puyo's world Meta God:\t\t"; +static char *kAbout03 = "\t\tFlorent 'flobo' Boudet"; +static char *kAbout04 = "X Men:\t\t"; +static char *kAbout05 = "\t\tJean-Christophe 'jeko' Hoelt"; +static char *kAbout06 = "\t\tGuillaume 'gyom' Borios"; +static char *kAbout07 = "Beta Goddess:\t\t"; +static char *kAbout08 = "\t\tTania"; + +char *AI_NAMES[] = { "Fanzy", "Garou", "Big Rabbit", "Gizmo", + "Satanas", "Doctor X", "Tanya", "Mr Gyom", + "The Duke","Jeko","--------" }; + + +extern SDL_Surface *display; +extern IIM_Surface *image, *gameScreen; +IIM_Surface *menuBGImage = 0; +PuyoCommander *theCommander; + +const int cycle_duration = 20; + +extern bool fullscreen; +extern bool useGL; +static bool sound = true; +static bool fx = true; + +#define SINGLE_PLAYER_GAME "Single Player Game" +#define TWO_PLAYERS_GAME "Two Players Game" + +MenuItems +main_menu_load (SoFont * font) +{ + static MenuItemsTab main_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM (SINGLE_PLAYER_GAME), + MENUITEM (TWO_PLAYERS_GAME), + MENUITEM_BLANKLINE, + MENUITEM ("Options"), + MENUITEM_BLANKLINE, + MENUITEM (kHighScores), + MENUITEM_BLANKLINE, + MENUITEM ("Rules"), + MENUITEM ("About FloboPuyo"), + MENUITEM_BLANKLINE, + MENUITEM ("Quit"), + MENUITEM_END + }; + static int loaded = 0; + + if (!loaded) { + menu_items_set_font_for (main_menu, SINGLE_PLAYER_GAME, font); + menu_items_set_font_for (main_menu, TWO_PLAYERS_GAME, font); + menu_items_set_font_for (main_menu, kHighScores, font); + menu_items_set_font_for (main_menu, "Options", font); + menu_items_set_font_for (main_menu, "Rules", font); + menu_items_set_font_for (main_menu, "About FloboPuyo", font); + menu_items_set_font_for (main_menu, "Quit", font); + loaded = 1; + menuBGImage = IIM_Load_DisplayFormat("MenuBackground.jpg"); + } + + return main_menu; +} + + +MenuItems two_player_game_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = + { + MENUITEM(kPlayer1Name), + MENUITEM(kPlayer2Name), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kGameLevel), + MENUITEM_BLANKLINE, + MENUITEM(kLevelEasy), + MENUITEM(kLevelMedium), + MENUITEM(kLevelHard), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kGameLevel, font); + menu_items_set_font_for(go_menu, kLevelEasy, font); + menu_items_set_font_for(go_menu, kLevelMedium, font); + menu_items_set_font_for(go_menu, kLevelHard, font); + menu_items_set_font_for(go_menu, kPlayer1Name, font); + menu_items_set_font_for(go_menu, kPlayer2Name, font); + return go_menu; +} + +MenuItems single_game_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = + { + MENUITEM(kPlayerName), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kGameLevel), + MENUITEM_BLANKLINE, + MENUITEM(kLevelEasy), + MENUITEM(kLevelMedium), + MENUITEM(kLevelHard), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kGameLevel, font); + menu_items_set_font_for(go_menu, kLevelEasy, font); + menu_items_set_font_for(go_menu, kLevelMedium, font); + menu_items_set_font_for(go_menu, kLevelHard, font); + menu_items_set_font_for(go_menu, kPlayerName, font); + return go_menu; +} + +MenuItems gameover_2p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kPlayer), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kScore), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kContinue), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kPlayer, font); + menu_items_set_font_for(go_menu, kScore, font); + menu_items_set_font_for(go_menu, kContinue, font); + return go_menu; +} + +MenuItems finished_1p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kCongratulations), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kPuyosInvasion), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kHitActionForMenu), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kCongratulations, font); + menu_items_set_font_for(go_menu, kPuyosInvasion, font); + menu_items_set_font_for(go_menu, kHitActionForMenu, small_font); + return go_menu; +} + +MenuItems nextlevel_1p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kYouDidIt), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kNextLevel), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kHitActionToContinue), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kYouDidIt, font); + menu_items_set_font_for(go_menu, kNextLevel, font); + menu_items_set_font_for(go_menu, kHitActionToContinue, small_font); + return go_menu; +} + +MenuItems looser_1p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kLooser), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kCurrentLevel), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kContinueLeft), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kHitActionToContinue), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kLooser, font); + menu_items_set_font_for(go_menu, kCurrentLevel, font); + menu_items_set_font_for(go_menu, kContinueLeft, font); + menu_items_set_font_for(go_menu, kHitActionToContinue, small_font); + return go_menu; +} + +MenuItems gameover_1p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kGameOver), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kYouGotToLevel), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kHitActionForMenu), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kGameOver, font); + menu_items_set_font_for(go_menu, kYouGotToLevel, font); + menu_items_set_font_for(go_menu, kHitActionForMenu, small_font); + return go_menu; +} + +MenuItems rules_menu_load (SoFont *font) { + static MenuItemsTab option_menu = { + MENUITEM_INACTIVE(kRules01), + MENUITEM_INACTIVE(kRules02), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kRules03), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kRules04), + MENUITEM_INACTIVE(kRules05), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kRules01, font); + menu_items_set_font_for(option_menu, kRules02, font); + menu_items_set_font_for(option_menu, kRules03, font); + menu_items_set_font_for(option_menu, kRules04, font); + menu_items_set_font_for(option_menu, kRules05, font); + + return option_menu; +} + +MenuItems high_scores_menu_load (SoFont *font) +{ + static MenuItemsTab option_menu = + { + MENUITEM_INACTIVE(kHighScores), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(k01), + MENUITEM_INACTIVE(k02), + MENUITEM_INACTIVE(k03), + MENUITEM_INACTIVE(k04), + MENUITEM_INACTIVE(k05), + MENUITEM_INACTIVE(k06), + MENUITEM_INACTIVE(k07), + MENUITEM_INACTIVE(k08), + MENUITEM_INACTIVE(k09), + MENUITEM_INACTIVE(k10), + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kHighScores, font); + menu_items_set_font_for(option_menu, k01, font); + menu_items_set_font_for(option_menu, k02, font); + menu_items_set_font_for(option_menu, k03, font); + menu_items_set_font_for(option_menu, k04, font); + menu_items_set_font_for(option_menu, k05, font); + menu_items_set_font_for(option_menu, k06, font); + menu_items_set_font_for(option_menu, k07, font); + menu_items_set_font_for(option_menu, k08, font); + menu_items_set_font_for(option_menu, k09, font); + menu_items_set_font_for(option_menu, k10, font); + + return option_menu; +} + +MenuItems about_menu_load (SoFont *font) +{ + static MenuItemsTab option_menu = { + MENUITEM_INACTIVE(kAbout01), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kVersion), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kAbout02), + MENUITEM_INACTIVE(kAbout03), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kAbout04), + MENUITEM_INACTIVE(kAbout05), + MENUITEM_INACTIVE(kAbout06), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kAbout07), + MENUITEM_INACTIVE(kAbout08), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kAbout01, font); + menu_items_set_font_for(option_menu, kVersion, font); + menu_items_set_font_for(option_menu, kAbout02, font); + menu_items_set_font_for(option_menu, kAbout03, font); + menu_items_set_font_for(option_menu, kAbout04, font); + menu_items_set_font_for(option_menu, kAbout05, font); + menu_items_set_font_for(option_menu, kAbout06, font); + menu_items_set_font_for(option_menu, kAbout07, font); + menu_items_set_font_for(option_menu, kAbout08, font); + + return option_menu; +} + +MenuItems must_restart_menu_load (SoFont *font) +{ + static MenuItemsTab option_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kMustRestart1), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kMustRestart2), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kMustRestart1, font); + menu_items_set_font_for(option_menu, kMustRestart2, font); + return option_menu; +} + +MenuItems options_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab option_menu = { + MENUITEM(kFullScreen), +#ifdef HAVE_OPENGL + MENUITEM(kOpenGL), +#endif + MENUITEM_BLANKLINE, + MENUITEM(kMusic), + MENUITEM(kAudioFX), + MENUITEM_BLANKLINE, + MENUITEM(kControls), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kMusic, font); + menu_items_set_font_for(option_menu, kAudioFX, font); + menu_items_set_font_for(option_menu, kControls, font); + menu_items_set_font_for(option_menu, "Back", font); + menu_items_set_font_for(option_menu, kFullScreen, font); + menu_items_set_value_for(option_menu, kFullScreen, fullscreen?"ON":"OFF"); +#ifdef HAVE_OPENGL + menu_items_set_font_for(option_menu, kOpenGL, font); + menu_items_set_value_for(option_menu, kOpenGL, useGL?"ON":"OFF"); +#endif + menu_items_set_value_for(option_menu, kMusic, sound?"ON":"OFF"); + menu_items_set_value_for(option_menu, kAudioFX, fx?"ON":"OFF"); + return option_menu; +} + +MenuItems controls_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab controls_menu = { + //MENUITEM("Player 1 Joystick"), + //MENUITEM("Player 2 Joystick"), + MENUITEM_BLANKLINE, + MENUITEM(kPlayer2Left), + MENUITEM(kPlayer2Right), + MENUITEM(kPlayer2Down), + MENUITEM(kPlayer2Clockwise), + MENUITEM(kPlayer2Counterclockwise), + MENUITEM_BLANKLINE, + MENUITEM(kPlayer1Left), + MENUITEM(kPlayer1Right), + MENUITEM(kPlayer1Down), + MENUITEM(kPlayer1Clockwise), + MENUITEM(kPlayer1Counterclockwise), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + //menu_items_set_font_for(controls_menu, "Player 1 Joystick", font); + + menu_items_set_font_for(controls_menu, kPlayer1Left, font); + menu_items_set_font_for(controls_menu, kPlayer1Right, font); + menu_items_set_font_for(controls_menu, kPlayer1Down, font); + menu_items_set_font_for(controls_menu, kPlayer1Clockwise, font); + menu_items_set_font_for(controls_menu, kPlayer1Counterclockwise, font); + + menu_items_set_font_for(controls_menu, kPlayer2Left, font); + menu_items_set_font_for(controls_menu, kPlayer2Right, font); + menu_items_set_font_for(controls_menu, kPlayer2Down, font); + menu_items_set_font_for(controls_menu, kPlayer2Clockwise, font); + menu_items_set_font_for(controls_menu, kPlayer2Counterclockwise, font); + + menu_items_set_font_for(controls_menu, "Back", font); + //menu_items_set_value_for(controls_menu, "Player 1 Joystick", SDL_JoystickName(0)); + //menu_items_set_value_for(controls_menu, kPlayer1Left, "s"); + return controls_menu; +} + +Menu *menu_pause = NULL; + +MenuItems pause_menu_load (SoFont * font) +{ + static MenuItemsTab main_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM (kContinueGame), + MENUITEM_BLANKLINE, + MENUITEM (kOptions), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM (kAbortGame), + MENUITEM_END + }; + static int loaded = 0; + + if (!loaded) { + menu_items_set_font_for (main_menu, kContinueGame, font); + menu_items_set_font_for (main_menu, kOptions, font); + menu_items_set_font_for (main_menu, kAbortGame, font); + loaded = 1; + } + + return main_menu; +} + +PuyoCommander::PuyoCommander(bool fs, bool snd, bool audio) +{ + int init_flags = SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_JOYSTICK; + + SDL_Delay(500); + corona = NULL; + fullscreen = GetBoolPreference(kFullScreen,fs); +#ifdef HAVE_OPENGL + useGL = GetBoolPreference(kOpenGL,false); +#endif + sound = GetBoolPreference(kMusic,snd); + fx = GetBoolPreference(kAudioFX,audio); + + int music_volume = GetIntPreference(kMusicVolume, 100); + int audio_volume = GetIntPreference(kAudioVolume, 80); + + initGameControls(); +#ifdef USE_DGA + /* This Hack Allows Hardware Surface on Linux */ + if (fullscreen) + setenv("SDL_VIDEODRIVER","dga",0); + + if (SDL_Init(init_flags) < 0) { + setenv("SDL_VIDEODRIVER","x11",1); + if (SDL_Init(init_flags) < 0) { + fprintf(stderr, "SDL initialisation error: %s\n", SDL_GetError()); + exit(1); + } + } + else { + if (fullscreen) + SDL_WM_GrabInput(SDL_GRAB_ON); + } +#else +#ifdef WIN32 + _putenv("SDL_VIDEODRIVER=windib"); +#endif + if ( SDL_Init(init_flags) < 0 ) { + fprintf(stderr, "SDL initialisation error: %s\n", SDL_GetError()); + exit(1); + } +#endif + + initControllers(); + initHiScores(AI_NAMES); + +#ifdef USE_AUDIO + audio_init(); + audio_music_start(0); + if (sound==false) Mix_PauseMusic(); + audio_set_music_on_off(sound); + audio_set_sound_on_off(fx); + + audio_set_volume(audio_volume); + audio_music_set_volume(music_volume); +#endif + + display = SDL_SetVideoMode( 640, 480, 0, SDL_ANYFORMAT|SDL_HWSURFACE|SDL_DOUBLEBUF|(fullscreen?SDL_FULLSCREEN:0)|(useGL?SDL_GLSDL:0)); + if ( display == NULL ) { + fprintf(stderr, "SDL_SetVideoMode error: %s\n", + SDL_GetError()); + exit(1); + } + atexit(SDL_Quit); + SDL_ShowCursor(SDL_DISABLE); + + smallFont = SoFont_new(); + SoFont_load (smallFont, IIM_Load_DisplayFormatAlpha ("font4b.png")); + menuFont = SoFont_new(); + SoFont_load (menuFont, IIM_Load_DisplayFormatAlpha ("font3b.png")); + darkFont = SoFont_new(); + SoFont_load (darkFont, IIM_Load_DisplayFormatAlpha ("fontdark.png")); + + IIM_Surface * menuselector = IIM_Load_DisplayFormatAlpha("menusel.png"); + + mainMenu = menu_new(main_menu_load(menuFont),menuselector); + gameOver1PMenu = menu_new(gameover_1p_menu_load(menuFont, smallFont),menuselector); + gameOver2PMenu = menu_new(gameover_2p_menu_load(menuFont, smallFont),menuselector); + nextLevelMenu = menu_new(nextlevel_1p_menu_load(menuFont, smallFont),menuselector); + looserMenu = menu_new(looser_1p_menu_load(menuFont, smallFont),menuselector); + finishedMenu = menu_new(finished_1p_menu_load(menuFont, smallFont),menuselector); + gameOverMenu = gameOver2PMenu; + optionMenu = menu_new(options_menu_load(menuFont, smallFont),menuselector); + controlsMenu = menu_new(controls_menu_load(menuFont, smallFont),menuselector); + rulesMenu = menu_new(rules_menu_load(menuFont),menuselector); + highScoresMenu = menu_new(high_scores_menu_load(menuFont),menuselector); + aboutMenu = menu_new(about_menu_load(menuFont),menuselector); + singleGameMenu = menu_new(single_game_menu_load(menuFont,smallFont),menuselector); + twoPlayerGameMenu = menu_new(two_player_game_menu_load(menuFont,smallFont),menuselector); + mustRestartMenu = menu_new(must_restart_menu_load(menuFont),menuselector); + + if (menu_pause == NULL) menu_pause = menu_new(pause_menu_load(menuFont),menuselector); + + menu_set_sounds (optionMenu, sound_pop, sound_slide); + menu_set_sounds (controlsMenu, sound_pop, sound_slide); + menu_set_sounds (mainMenu, sound_pop, sound_slide); + menu_set_sounds (rulesMenu, sound_pop, sound_slide); + menu_set_sounds (highScoresMenu, sound_pop, sound_slide); + menu_set_sounds (aboutMenu, sound_pop, sound_slide); + menu_set_sounds (singleGameMenu, sound_pop, sound_slide); + menu_set_sounds (twoPlayerGameMenu, sound_pop, sound_slide); + menu_set_sounds (menu_pause , sound_pop, sound_slide); + melt = doom_melt_new(); + + scrollingText = scrolling_text_new( + "Welcome to the wonderful world of FloboPuyo !!! Enjoy its nice graphics, " + "happy music and entertaining gameplay... " + "Will you be able to defeat all of the mighty players ? " + "Will you beat the Puyo Gods ??? Have a try ! " + "We wish you good luck. " + " Hello from PuyoLand !", smallFont); + theCommander = this; +} + +void PuyoCommander::run() +{ + Menu *menu = mainMenu; + + cycle = 0; + lastRenderedCycle = 0; + maxFrameDrop = 10; + + start_time = SDL_GetTicks (); + + audio_music_start (0); + + /* PuyoStory *introStory = new PuyoStory(this, 0); + introStory->loop(); + delete introStory; */ + + menu_show (menu); + + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + goto mml_fin; + break; + case GameControlEvent::kDown: + menu_next_item (menu); + break; + case GameControlEvent::kUp: + menu_prev_item (menu); + break; + case GameControlEvent::kStart: + menu_validate (menu); + if (menu_active_is (menu, "Quit")) + goto mml_fin; + if (menu_active_is (menu, "Options")) { + menu_hide (menu); + optionMenuLoop(NULL); + menu_show (menu); + } + if (menu_active_is (menu, kHighScores)) { + menu_hide(menu); + updateHighScoresMenu(); + backLoop(highScoresMenu); + menu_show(menu); + } + if (menu_active_is (menu, "Rules")) { + menu_hide(menu); + backLoop(rulesMenu); + menu_show(menu); + } + if (menu_active_is (menu, "About FloboPuyo")) { + menu_hide(menu); + backLoop(aboutMenu); + menu_show(menu); + } + if (menu_active_is (menu, SINGLE_PLAYER_GAME)) { + menu_hide (menu); + startSingleGameLoop(); + menu_show (menu); + } + if (menu_active_is (menu, TWO_PLAYERS_GAME)) { + menu_hide (menu); + startTwoPlayerGameLoop(); + menu_show (menu); + audio_music_start(0); + } + break; + case GameControlEvent::kBack: + goto mml_fin; + break; + default: + break; + } + } + updateAll(NULL); + } +mml_fin: + menu_hide (menu); +} + +void PuyoCommander::updateHighScoresMenu(int newOne) +{ + hiscore *scores = getHiScores(); + char tmp[256]; +#define PAS_DE_COMMENTAIRES(X,kXX) \ + if (newOne == X) \ + sprintf(tmp, "%s\t** %d", scores[X].name, scores[X].score); \ + else \ + sprintf(tmp, "%s\t%d", scores[X].name, scores[X].score); \ + menu_set_value(highScoresMenu,kXX,tmp,0); + + PAS_DE_COMMENTAIRES(0,k01); + PAS_DE_COMMENTAIRES(1,k02); + PAS_DE_COMMENTAIRES(2,k03); + PAS_DE_COMMENTAIRES(3,k04); + PAS_DE_COMMENTAIRES(4,k05); + PAS_DE_COMMENTAIRES(5,k06); + PAS_DE_COMMENTAIRES(6,k07); + PAS_DE_COMMENTAIRES(7,k08); + PAS_DE_COMMENTAIRES(8,k09); + PAS_DE_COMMENTAIRES(9,k10); +} + +bool PuyoCommander::changeControlLoop(int controlIndex, PuyoDrawable *starter) +{ + bool keyPressed = false; + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + bool tryOk = tryChangeControl(controlIndex, e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + goto mml_fin; + break; + case GameControlEvent::kBack: + goto mml_fin; + break; + default: + break; + } + if (tryOk) { + keyPressed = true; + goto mml_fin; + } + } + updateAll(starter); + } +mml_fin: + return keyPressed; +} + +void PuyoCommander::controlsMenuLoop(PuyoDrawable *d) +{ + char newKeyName[250]; + + getKeyName(kPlayer1LeftControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Left, newKeyName,0); + getKeyName(kPlayer1RightControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Right, newKeyName,0); + getKeyName(kPlayer1DownControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Down, newKeyName,0); + getKeyName(kPlayer1ClockwiseControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Clockwise, newKeyName,0); + getKeyName(kPlayer1CounterclockwiseControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Counterclockwise, newKeyName,0); + + getKeyName(kPlayer2LeftControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Left, newKeyName,0); + getKeyName(kPlayer2RightControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Right, newKeyName,0); + getKeyName(kPlayer2DownControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Down, newKeyName,0); + getKeyName(kPlayer2ClockwiseControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Clockwise, newKeyName,0); + getKeyName(kPlayer2CounterclockwiseControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Counterclockwise, newKeyName,1); + + menu_show(controlsMenu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + char *chosenControl = NULL; + int chosenControlIndex; + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + goto mml_fin; + break; + case GameControlEvent::kUp: + menu_prev_item (controlsMenu); + break; + case GameControlEvent::kDown: + menu_next_item (controlsMenu); + break; + case GameControlEvent::kStart: + menu_validate (controlsMenu); + if (menu_active_is (controlsMenu, "Back")) + goto mml_fin; + + if (menu_active_is (controlsMenu, kPlayer1Left)) { + chosenControl = kPlayer1Left; + chosenControlIndex = kPlayer1LeftControl; + } + if (menu_active_is (controlsMenu, kPlayer1Right)) { + chosenControl = kPlayer1Right; + chosenControlIndex = kPlayer1RightControl; + } + if (menu_active_is (controlsMenu, kPlayer1Down)) { + chosenControl = kPlayer1Down; + chosenControlIndex = kPlayer1DownControl; + } + if (menu_active_is (controlsMenu, kPlayer1Clockwise)) { + chosenControl = kPlayer1Clockwise; + chosenControlIndex = kPlayer1ClockwiseControl; + } + if (menu_active_is (controlsMenu, kPlayer1Counterclockwise)) { + chosenControl = kPlayer1Counterclockwise; + chosenControlIndex = kPlayer1CounterclockwiseControl; + } + + if (menu_active_is (controlsMenu, kPlayer2Left)) { + chosenControl = kPlayer2Left; + chosenControlIndex = kPlayer2LeftControl; + } + if (menu_active_is (controlsMenu, kPlayer2Right)) { + chosenControl = kPlayer2Right; + chosenControlIndex = kPlayer2RightControl; + } + if (menu_active_is (controlsMenu, kPlayer2Down)) { + chosenControl = kPlayer2Down; + chosenControlIndex = kPlayer2DownControl; + } + if (menu_active_is (controlsMenu, kPlayer2Clockwise)) { + chosenControl = kPlayer2Clockwise; + chosenControlIndex = kPlayer2ClockwiseControl; + } + if (menu_active_is (controlsMenu, kPlayer2Counterclockwise)) { + chosenControl = kPlayer2Counterclockwise; + chosenControlIndex = kPlayer2CounterclockwiseControl; + } + + if (chosenControl != NULL) { + char prevValue[255]; + strcpy(prevValue, menu_get_value (controlsMenu, chosenControl)); + menu_set_value(controlsMenu, chosenControl, ""); + if (changeControlLoop(chosenControlIndex, d)) { + getKeyName(chosenControlIndex, newKeyName); + menu_set_value(controlsMenu, chosenControl, newKeyName); + menu_next_item (controlsMenu); + } + else { + menu_set_value(controlsMenu, chosenControl, prevValue); + } + } + break; + case GameControlEvent::kBack: + goto mml_fin; + break; + default: + break; + } + } + + updateAll(d); + } +mml_fin: + saveControls(); + menu_hide (controlsMenu); +} + +void PuyoCommander::optionMenuLoop(PuyoDrawable *d) +{ + menu_show(optionMenu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + goto mml_fin; + break; + case GameControlEvent::kUp: + menu_prev_item (optionMenu); + break; + case GameControlEvent::kDown: + menu_next_item (optionMenu); + break; + case GameControlEvent::kStart: + menu_validate (optionMenu); + if (menu_active_is (optionMenu, "Back")) + goto mml_fin; + if (menu_active_is (optionMenu, kFullScreen)) { + fullscreen = menu_switch_on_off(optionMenu, kFullScreen); + SetBoolPreference(kFullScreen,fullscreen); + if (useGL) + { + menu_hide(optionMenu); + backLoop(mustRestartMenu,d); + menu_show(optionMenu); + } + else + { +#ifdef _WIN32 + menu_hide(optionMenu); + backLoop(mustRestartMenu,d); + menu_show(optionMenu); +#else + SDL_QuitSubSystem(SDL_INIT_VIDEO); + SDL_InitSubSystem(SDL_INIT_VIDEO); + display = SDL_SetVideoMode(640, 480, 0, + SDL_ANYFORMAT|SDL_HWSURFACE|SDL_DOUBLEBUF + |(fullscreen?SDL_FULLSCREEN:0)|(useGL?SDL_GLSDL:0)); +#endif + /* IIM_ReConvertAll(); + SoFont_Refresh(menuFont); + SoFont_Refresh(smallFont); + SoFont_Refresh(darkFont); */ + } + } + if (menu_active_is (optionMenu, kOpenGL)) { + bool useGL2 = menu_switch_on_off(optionMenu, kOpenGL); + SetBoolPreference(kOpenGL,useGL2); + menu_hide(optionMenu); + backLoop(mustRestartMenu,d); + menu_show(optionMenu); + } + if (menu_active_is (optionMenu, kMusic)) { + sound = menu_switch_on_off(optionMenu, kMusic); + SetBoolPreference(kMusic,sound); + audio_set_music_on_off(sound); + if (sound) audio_music_start(0); + } + if (menu_active_is (optionMenu, kAudioFX)) { + fx = menu_switch_on_off(optionMenu, kAudioFX); + SetBoolPreference(kAudioFX,fx); + audio_set_sound_on_off(fx); + } + if (menu_active_is (optionMenu, kControls)) { + menu_hide (optionMenu); + controlsMenuLoop(d); + menu_show(optionMenu); + } + break; + case GameControlEvent::kBack: + goto mml_fin; + break; + default: + break; + } + } + + updateAll(d); + } +mml_fin: + menu_hide (optionMenu); +} + +void PuyoCommander::backLoop(Menu *menu, PuyoDrawable *starter) +{ + + if (menu == aboutMenu) { + corona = corona32_new(); + corona32_resize(corona, 640, CORONA_HEIGHT); + corona_screen = (int*)calloc(640 * CORONA_HEIGHT + 64, sizeof(int)); + } + + menu_show(menu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + goto mml_fin; + case GameControlEvent::kStart: + case GameControlEvent::kBack: + goto mml_fin; + break; + } + } + updateAll(starter); + } +mml_fin: + menu_hide (menu); + if (corona) + { + corona32_delete(corona); + free(corona_screen); + corona = NULL; + } +} + +void PuyoCommander::enterStringLoop(Menu *menu, const char *kItem, char out[256]) +{ + int len = 0; + char prevValue[256]; + strcpy(prevValue, menu_get_value (menu, kItem)); + out[0] = '_'; + out[1] = 0; + menu_set_value(menu, kItem, "_"); + + while (1) + { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + break; + case GameControlEvent::kStart: + out[len] = 0; + menu_set_value(menu, kItem, out); + menu_validate (menu); + return; + case GameControlEvent::kBack: + strcpy(out, prevValue); + menu_set_value(menu, kItem, prevValue); + return; + } + switch (e.type) { + case SDL_KEYDOWN: + { + char ch = 0; + if ((e.key.keysym.sym >= SDLK_a) && (e.key.keysym.sym <= SDLK_z)) + ch = e.key.keysym.sym; + + if ((ch >= 'a') && (ch <= 'z')) { + if ((len == 0) || (out[len-1] == ' ')) + ch = ch - 'a' + 'A'; + } + + if (e.key.keysym.sym == SDLK_SPACE) + ch = ' '; + + if ((ch!=0) && (len < 10)) { + out[len++] = ch; + out[len] = '_'; + out[len+1] = 0; + } + + if ((e.key.keysym.sym == SDLK_BACKSPACE) && (len > 0)) + { + out[len] = 0; + out[--len] = '_'; + } + menu_set_value(menu, kItem, out, 0); + } + break; + } + } + updateAll(NULL); + } +} + + +void PuyoCommander::startTwoPlayerGameLoop() +{ + char player1Name[256]; + char player2Name[256]; + + GetStrPreference("Player1 Name", player1Name, "Player 1"); + GetStrPreference("Player2 Name", player2Name, "Player 2"); + menu_set_value(twoPlayerGameMenu, kPlayer1Name, player1Name, 0); + menu_set_value(twoPlayerGameMenu, kPlayer2Name, player2Name, 0); + + while (!menu_active_is(twoPlayerGameMenu,kLevelMedium)) + menu_next_item(twoPlayerGameMenu); + + menu_show(twoPlayerGameMenu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + break; + case GameControlEvent::kDown: + menu_next_item (twoPlayerGameMenu); + break; + case GameControlEvent::kUp: + menu_prev_item (twoPlayerGameMenu); + break; + case GameControlEvent::kStart: + menu_validate (twoPlayerGameMenu); + if (menu_active_is(twoPlayerGameMenu,kPlayer2Name)) { + enterStringLoop(twoPlayerGameMenu,kPlayer2Name,player2Name); + menu_next_item (twoPlayerGameMenu); + } + else if (menu_active_is(twoPlayerGameMenu,kPlayer1Name)) { + enterStringLoop(twoPlayerGameMenu,kPlayer1Name,player1Name); + menu_next_item (twoPlayerGameMenu); + } + else + goto mml_play; + break; + case GameControlEvent::kBack: + menu_hide(twoPlayerGameMenu); + return; + break; + } + } + updateAll(NULL); + } + +mml_play: + menu_hide (twoPlayerGameMenu); + + GAME_ACCEL = 2000; + gameLevel = 1; + if (menu_active_is (twoPlayerGameMenu, kLevelMedium)) { + GAME_ACCEL = 1500; + gameLevel = 2; + } + else if (menu_active_is (twoPlayerGameMenu, kLevelHard)) { + GAME_ACCEL = 1000; + gameLevel = 3; + } + + int score1 = 0; + int score2 = 0; + gameOverMenu = gameOver2PMenu; + int currentMusicTheme = 0; + if (menu_active_is(gameOverMenu, "NO")) + menu_next_item(gameOverMenu); + while (menu_active_is(gameOverMenu, "YES")) { + menu_next_item(gameOverMenu); + PuyoStarter myStarter(this,false,0,RANDOM,currentMusicTheme); + audio_music_switch_theme(currentMusicTheme); + p1name = player1Name; + p2name = player2Name; + GAME_ACCEL = 1500; + doom_melt_start(melt, menuBGImage); + myStarter.run(score1, score2, 0, 0, 0); + score1 += myStarter.leftPlayerWin(); + score2 += myStarter.rightPlayerWin(); + currentMusicTheme = (currentMusicTheme + 1) % NB_MUSIC_THEME; + } + + SetStrPreference("Player1 Name", player1Name); + SetStrPreference("Player2 Name", player2Name); + doom_melt_start(melt, gameScreen); +} + + +struct SelIA { + IA_Type type; + int level; +}; + +void PuyoCommander::startSingleGameLoop() +{ + char playerName[256]; + + char * defaultName = getenv("USER"); + if (defaultName == NULL) + defaultName = "Player"; + if (!(defaultName[0]>=32)) + defaultName = "Player"; + if ((defaultName[0]>='a') && (defaultName[0]<='z')) + defaultName[0] += 'A' - 'a'; + + GetStrPreference("Player Name", playerName, defaultName); + menu_set_value(singleGameMenu, kPlayerName, playerName, 0); + + while (!menu_active_is(singleGameMenu,kLevelMedium)) + menu_next_item(singleGameMenu); + + menu_show(singleGameMenu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + break; + case GameControlEvent::kDown: + menu_next_item (singleGameMenu); + break; + case GameControlEvent::kUp: + menu_prev_item (singleGameMenu); + break; + case GameControlEvent::kStart: + menu_validate (singleGameMenu); + if (menu_active_is(singleGameMenu,kPlayerName)) + enterStringLoop(singleGameMenu,kPlayerName,playerName); + else + goto mml_play; + break; + case GameControlEvent::kBack: + menu_hide(singleGameMenu); + return; + break; + } + } + updateAll(NULL); + } + +mml_play: + menu_hide (singleGameMenu); + + SelIA ia1[] = { {RANDOM, 350}, {FLOBO, 350}, {FLOBO, 250}, {FLOBO, 180}, {FLOBO, 90}, {JEKO, 350}, {TANIA, 320}, {FLOBO, 62}, {RANDOM,0} }; + SelIA ia2[] = { {FLOBO, 190}, {JEKO , 180}, {TANIA, 160}, {FLOBO, 90}, {GYOM , 210}, {TANIA, 90}, {JEKO, 80}, {GYOM, 90}, {RANDOM,0} }; + SelIA ia3[] = { {TANIA, 130}, {JEKO , 100}, {GYOM , 90}, {JEKO, 80}, {TANIA, 60}, {GYOM, 60}, {GYOM, 40}, {GYOM, 30}, {RANDOM,0} }; + + SelIA *ia = &(ia1[0]); + + gameLevel = 1; + GAME_ACCEL = 2000; + if (menu_active_is (singleGameMenu, kLevelMedium)) { + ia = &(ia2[0]); + GAME_ACCEL = 1500; + gameLevel = 2; + } + else if (menu_active_is (singleGameMenu, kLevelHard)) { + ia = &(ia3[0]); + GAME_ACCEL = 1000; + gameLevel = 3; + } + + int score1 = 0; + int score2 = 0; + int lives = 3; + if (menu_active_is(finishedMenu, "NO")) + menu_next_item(finishedMenu); + if (menu_active_is(looserMenu , "NO")) + menu_next_item(looserMenu); + if (menu_active_is(nextLevelMenu, "NO")) + menu_next_item(nextLevelMenu); + if (menu_active_is(gameOver2PMenu, "NO")) + menu_next_item(gameOver2PMenu); + gameOverMenu = nextLevelMenu; + + int lastPoints = 0; + int currentMusicTheme = 0; + + int fini = 0; + while (!fini) + { + PuyoStory myStory(this, score2+1); + myStory.loop(); + PuyoStarter myStarter(this, true, ia[score2].level, ia[score2].type, currentMusicTheme); + p1name = playerName; + p2name = AI_NAMES[score2]; + doom_melt_start(melt, menuBGImage); + audio_music_switch_theme(currentMusicTheme); + myStarter.run(score1, score2, lives, lastPoints, 0); + lastPoints = myStarter.rightPlayerPoints(); + score1 += myStarter.leftPlayerWin(); + score2 += myStarter.rightPlayerWin(); + if (!myStarter.rightPlayerWin()) + lives--; + else { + currentMusicTheme = (currentMusicTheme + 1) % NB_MUSIC_THEME; + } + + if (ia[score2].level == 0) { + lastPoints += 100000; + if (menu_active_is (singleGameMenu, kLevelMedium)) + lastPoints += 150000; + if (menu_active_is (singleGameMenu, kLevelHard)) + lastPoints += 250000; + fini = 1; + } + + if (!(menu_active_is(gameOverMenu, "YES") && (lives >= 0))) + fini = 1; + + if (fini) + { + audio_music_start(0); + int newOne = setHiScore(lastPoints, p1name); + if (newOne >= 0) + { + updateHighScoresMenu(newOne); + backLoop(highScoresMenu, &myStarter); + } + } + } + SetStrPreference("Player Name", playerName); + doom_melt_start(melt, gameScreen); +} + +#define TIME_TOLERANCE 4 + +void PuyoCommander::updateAll(PuyoDrawable *starter, SDL_Surface *extra_surf) +{ + Uint32 now = 0; + + // mise a jour + menu_update (mainMenu, display); + + menu_update(gameOver2PMenu, display); + menu_update(gameOver1PMenu, display); + menu_update(nextLevelMenu, display); + menu_update(finishedMenu, display); + menu_update(looserMenu, display); + + menu_update (optionMenu, display); + menu_update (controlsMenu, display); + menu_update (rulesMenu, display); + menu_update (highScoresMenu, display); + menu_update (aboutMenu, display); + menu_update (mustRestartMenu, display); + menu_update (singleGameMenu, display); + menu_update (twoPlayerGameMenu, display); + menu_update (menu_pause,display); + scrolling_text_update(scrollingText, display); + doom_melt_update(melt); + + // affichage eventuel (pourrait ne pas avoir lieu de tps en tps si machine + // trop lente) + cycle++; + now = SDL_GetTicks (); + + if ((now < (start_time + (cycle + TIME_TOLERANCE) * cycle_duration)) + || (cycle - lastRenderedCycle > maxFrameDrop)) + { + lastRenderedCycle = cycle; + + if (starter) { + starter->draw(); + } + else { + SDL_BlitSurface (menuBGImage->surf, NULL, display, NULL); + } + + if (corona) + { + short frequency[2][512]; + for (int i=0; i<512; ++i) { // Generate random sound. + frequency[0][i] = rand(); + frequency[1][i] = rand(); + } + corona32_update(corona, SDL_GetTicks(), frequency); + corona32_displayRGBA(corona, corona_screen); + SDL_Surface *tmpsurf = + SDL_CreateRGBSurfaceFrom (corona_screen, 640, CORONA_HEIGHT, + 32, 640*4, + 0x00ff0000, 0x0000ff00, 0x000000ff, + 0xff000000); + SDL_Rect rect; + rect.x = 0; + rect.y = 480 - tmpsurf->h; + rect.w = tmpsurf->w; + rect.h = tmpsurf->h; + SDL_BlitSurface(tmpsurf, NULL, display, &rect); + SDL_FreeSurface (tmpsurf); + } + if (extra_surf) + { + SDL_Rect rect; + rect.x = 0; + rect.y = 480 - extra_surf->h; + rect.w = extra_surf->w; + rect.h = extra_surf->h; + SDL_BlitSurface(extra_surf, NULL, display, &rect); + } + + if (!starter) + scrolling_text_draw(scrollingText, display, 460); + + menu_draw (mainMenu, display); + menu_draw(gameOver2PMenu, display); + menu_draw(gameOver1PMenu, display); + menu_draw(nextLevelMenu, display); + menu_draw(finishedMenu, display); + menu_draw(looserMenu, display); + menu_draw (optionMenu, display); + menu_draw (controlsMenu, display); + menu_draw (rulesMenu, display); + menu_draw (highScoresMenu, display); + menu_draw (aboutMenu, display); + menu_draw (mustRestartMenu, display); + menu_draw (singleGameMenu, display); + menu_draw (twoPlayerGameMenu, display); + menu_draw(menu_pause,display); + doom_melt_display(melt, display); + SDL_Flip (display); + } + + // delay si machine trop rapide + now = SDL_GetTicks (); + if (now < (start_time + cycle * cycle_duration)) { + int ttw = ((start_time + cycle * cycle_duration) - now); + ttw /= 2; + SDL_Delay (ttw); + } +} + + diff --git a/PuyoCommander.cpp.actual b/PuyoCommander.cpp.actual new file mode 100644 index 0000000..1651c6d --- /dev/null +++ b/PuyoCommander.cpp.actual @@ -0,0 +1,1438 @@ +#include +#include +#include +#include +#include "glSDL.h" +#include "PuyoCommander.h" +#include "PuyoStarter.h" +#include "PuyoVersion.h" +#include "PuyoStory.h" +#include "preferences.h" +#include "InputManager.h" +#include "HiScores.h" +#include "PuyoDoomMelt.h" +#include "IosImgProcess.h" + +#ifndef DATADIR +extern char *DATADIR; +#endif + +extern const char *p1name; +extern const char *p2name; +extern int GAME_ACCEL; +extern int gameLevel; + +char *kYouDidIt = "You Dit It!!!"; +char *kNextLevel = "Next:\t\t"; +char *kLooser = "Looser!!!"; +char *kCurrentLevel = "Level:\t\t"; +char *kContinueLeft = "Continues:\t\t"; +char *kGameOver = "Game Over!!!"; +char *kYouGotToLevel = "You Get To "; +char *kHitActionToContinue = "Hit Action to continue..."; +char *kHitActionForMenu = "Hit Action for menu..."; +char *kContinue = "Continue?"; +char *kContinueGame= "Continue Game"; +char *kAbortGame = "Abort Game"; +char *kOptions = "Options"; +char *kPlayer = "Player"; +char *kScore = "Score:\t"; +char *kPlayerName = "Player Name:\t"; +char *kPlayer1Name = "P1 Name:\t"; +char *kPlayer2Name = "P2 Name:\t"; + + +static const char *k01 = " 1 - "; +static const char *k02 = " 2 - "; +static const char *k03 = " 3 - "; +static const char *k04 = " 4 - "; +static const char *k05 = " 5 - "; +static const char *k06 = " 6 - "; +static const char *k07 = " 7 - "; +static const char *k08 = " 8 - "; +static const char *k09 = " 9 - "; +static const char *k10 = "10 - "; + +static const char *kMustRestart1 = "Major Video Mode Change..."; +static const char *kMustRestart2 = "This will take effect next time you restart Flobo Puyo."; +char *kHighScores = "Hall of Fame"; + +static char *kMusicVolume = "MusicVolume"; +static char *kAudioVolume = "AudioVolume"; + +char *kCongratulations = "Congratulations!!!"; +char *kPuyosInvasion = "You stopped Puyo's invasion.\n" + "Peace on Earth is restored!!"; + +static char *kAudioFX = "Audio FX\t"; +static char *kMusic = "Music\t"; +static char *kFullScreen = "FullScreen\t"; +static char *kOpenGL = "Use OpenGL\t"; +static char *kControls = "Change controls..."; +static char *kGameLevel = "Choose Game Level"; +static char *kLevelEasy = "Easy"; +static char *kLevelMedium = "Medium"; +static char *kLevelHard = "Hard"; + +static char *kPlayer1Left = "P2 Left:\t"; +static char *kPlayer1Right = "P2 Right:\t"; +static char *kPlayer1Down = "P2 Down:\t"; +static char *kPlayer1Clockwise = "P2 Turn L:\t"; +static char *kPlayer1Counterclockwise = "P2 Turn R:\t"; + +static char *kPlayer2Left = "P1 Left:\t"; +static char *kPlayer2Right = "P1 Right:\t"; +static char *kPlayer2Down = "P1 Down:\t"; +static char *kPlayer2Clockwise = "P1 Turn L:\t"; +static char *kPlayer2Counterclockwise = "P1 Turn R:\t"; + +static char *kRules01 = "Puyos are fancy smiling bubbles..."; +static char *kRules02 = "But they can really be invading sometimes!"; +static char *kRules03 = "Send them away by making groups of 4 or more."; +static char *kRules04 = "Try to make large groups, or many at the same time"; +static char *kRules05 = "to send more bad ghost Puyos to your opponent."; + +static char *kAbout01 = "FloboPuyo is an iOS-software production.\t\t"; +static char *kAbout02 = "Puyo's world Meta God:\t\t"; +static char *kAbout03 = "\t\tFlorent 'flobo' Boudet"; +static char *kAbout04 = "X Men:\t\t"; +static char *kAbout05 = "\t\tJean-Christophe 'jeko' Hoelt"; +static char *kAbout06 = "\t\tGuillaume 'gyom' Borios"; +static char *kAbout07 = "Beta Goddess:\t\t"; +static char *kAbout08 = "\t\tTania"; + +char *AI_NAMES[] = { "Fanzy", "Garou", "Big Rabbit", "Gizmo", + "Satanas", "Doctor X", "Tanya", "Mr Gyom", + "The Duke","Jeko","--------" }; + + +extern SDL_Surface *display; +extern IIM_Surface *image, *gameScreen; +IIM_Surface *menuBGImage = 0; +PuyoCommander *theCommander; + +const int cycle_duration = 20; + +extern bool fullscreen; +extern bool useGL; +static bool sound = true; +static bool fx = true; + +#define SINGLE_PLAYER_GAME "Single Player Game" +#define TWO_PLAYERS_GAME "Two Players Game" + +MenuItems +main_menu_load (SoFont * font) +{ + static MenuItemsTab main_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM (SINGLE_PLAYER_GAME), + MENUITEM (TWO_PLAYERS_GAME), + MENUITEM_BLANKLINE, + MENUITEM ("Options"), + MENUITEM_BLANKLINE, + MENUITEM (kHighScores), + MENUITEM_BLANKLINE, + MENUITEM ("Rules"), + MENUITEM ("About FloboPuyo"), + MENUITEM_BLANKLINE, + MENUITEM ("Quit"), + MENUITEM_END + }; + static int loaded = 0; + + if (!loaded) { + menu_items_set_font_for (main_menu, SINGLE_PLAYER_GAME, font); + menu_items_set_font_for (main_menu, TWO_PLAYERS_GAME, font); + menu_items_set_font_for (main_menu, kHighScores, font); + menu_items_set_font_for (main_menu, "Options", font); + menu_items_set_font_for (main_menu, "Rules", font); + menu_items_set_font_for (main_menu, "About FloboPuyo", font); + menu_items_set_font_for (main_menu, "Quit", font); + loaded = 1; + menuBGImage = IIM_Load_DisplayFormat("MenuBackground.jpg"); + } + + return main_menu; +} + + +MenuItems two_player_game_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = + { + MENUITEM(kPlayer1Name), + MENUITEM(kPlayer2Name), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kGameLevel), + MENUITEM_BLANKLINE, + MENUITEM(kLevelEasy), + MENUITEM(kLevelMedium), + MENUITEM(kLevelHard), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kGameLevel, font); + menu_items_set_font_for(go_menu, kLevelEasy, font); + menu_items_set_font_for(go_menu, kLevelMedium, font); + menu_items_set_font_for(go_menu, kLevelHard, font); + menu_items_set_font_for(go_menu, kPlayer1Name, font); + menu_items_set_font_for(go_menu, kPlayer2Name, font); + return go_menu; +} + +MenuItems single_game_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = + { + MENUITEM(kPlayerName), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kGameLevel), + MENUITEM_BLANKLINE, + MENUITEM(kLevelEasy), + MENUITEM(kLevelMedium), + MENUITEM(kLevelHard), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kGameLevel, font); + menu_items_set_font_for(go_menu, kLevelEasy, font); + menu_items_set_font_for(go_menu, kLevelMedium, font); + menu_items_set_font_for(go_menu, kLevelHard, font); + menu_items_set_font_for(go_menu, kPlayerName, font); + return go_menu; +} + +MenuItems gameover_2p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kPlayer), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kScore), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kContinue), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kPlayer, font); + menu_items_set_font_for(go_menu, kScore, font); + menu_items_set_font_for(go_menu, kContinue, font); + return go_menu; +} + +MenuItems finished_1p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kCongratulations), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kPuyosInvasion), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kHitActionForMenu), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kCongratulations, font); + menu_items_set_font_for(go_menu, kPuyosInvasion, font); + menu_items_set_font_for(go_menu, kHitActionForMenu, small_font); + return go_menu; +} + +MenuItems nextlevel_1p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kYouDidIt), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kNextLevel), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kHitActionToContinue), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kYouDidIt, font); + menu_items_set_font_for(go_menu, kNextLevel, font); + menu_items_set_font_for(go_menu, kHitActionToContinue, small_font); + return go_menu; +} + +MenuItems looser_1p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kLooser), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kCurrentLevel), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kContinueLeft), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kHitActionToContinue), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kLooser, font); + menu_items_set_font_for(go_menu, kCurrentLevel, font); + menu_items_set_font_for(go_menu, kContinueLeft, font); + menu_items_set_font_for(go_menu, kHitActionToContinue, small_font); + return go_menu; +} + +MenuItems gameover_1p_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab go_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kGameOver), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kYouGotToLevel), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kHitActionForMenu), + MENUITEM("YES"), + MENUITEM("NO"), + MENUITEM_END + }; + menu_items_set_font_for(go_menu, kGameOver, font); + menu_items_set_font_for(go_menu, kYouGotToLevel, font); + menu_items_set_font_for(go_menu, kHitActionForMenu, small_font); + return go_menu; +} + +MenuItems rules_menu_load (SoFont *font) { + static MenuItemsTab option_menu = { + MENUITEM_INACTIVE(kRules01), + MENUITEM_INACTIVE(kRules02), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kRules03), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kRules04), + MENUITEM_INACTIVE(kRules05), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kRules01, font); + menu_items_set_font_for(option_menu, kRules02, font); + menu_items_set_font_for(option_menu, kRules03, font); + menu_items_set_font_for(option_menu, kRules04, font); + menu_items_set_font_for(option_menu, kRules05, font); + + return option_menu; +} + +MenuItems high_scores_menu_load (SoFont *font) +{ + static MenuItemsTab option_menu = + { + MENUITEM_INACTIVE(kHighScores), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(k01), + MENUITEM_INACTIVE(k02), + MENUITEM_INACTIVE(k03), + MENUITEM_INACTIVE(k04), + MENUITEM_INACTIVE(k05), + MENUITEM_INACTIVE(k06), + MENUITEM_INACTIVE(k07), + MENUITEM_INACTIVE(k08), + MENUITEM_INACTIVE(k09), + MENUITEM_INACTIVE(k10), + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kHighScores, font); + menu_items_set_font_for(option_menu, k01, font); + menu_items_set_font_for(option_menu, k02, font); + menu_items_set_font_for(option_menu, k03, font); + menu_items_set_font_for(option_menu, k04, font); + menu_items_set_font_for(option_menu, k05, font); + menu_items_set_font_for(option_menu, k06, font); + menu_items_set_font_for(option_menu, k07, font); + menu_items_set_font_for(option_menu, k08, font); + menu_items_set_font_for(option_menu, k09, font); + menu_items_set_font_for(option_menu, k10, font); + + return option_menu; +} + +MenuItems about_menu_load (SoFont *font) +{ + static MenuItemsTab option_menu = { + MENUITEM_INACTIVE(kAbout01), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kVersion), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kAbout02), + MENUITEM_INACTIVE(kAbout03), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kAbout04), + MENUITEM_INACTIVE(kAbout05), + MENUITEM_INACTIVE(kAbout06), + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kAbout07), + MENUITEM_INACTIVE(kAbout08), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kAbout01, font); + menu_items_set_font_for(option_menu, kVersion, font); + menu_items_set_font_for(option_menu, kAbout02, font); + menu_items_set_font_for(option_menu, kAbout03, font); + menu_items_set_font_for(option_menu, kAbout04, font); + menu_items_set_font_for(option_menu, kAbout05, font); + menu_items_set_font_for(option_menu, kAbout06, font); + menu_items_set_font_for(option_menu, kAbout07, font); + menu_items_set_font_for(option_menu, kAbout08, font); + + return option_menu; +} + +MenuItems must_restart_menu_load (SoFont *font) +{ + static MenuItemsTab option_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kMustRestart1), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM_INACTIVE(kMustRestart2), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kMustRestart1, font); + menu_items_set_font_for(option_menu, kMustRestart2, font); + return option_menu; +} + +MenuItems options_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab option_menu = { + MENUITEM(kFullScreen), +#ifdef HAVE_OPENGL + MENUITEM(kOpenGL), +#endif + MENUITEM_BLANKLINE, + MENUITEM(kMusic), + MENUITEM(kAudioFX), + MENUITEM_BLANKLINE, + MENUITEM(kControls), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + menu_items_set_font_for(option_menu, kMusic, font); + menu_items_set_font_for(option_menu, kAudioFX, font); + menu_items_set_font_for(option_menu, kControls, font); + menu_items_set_font_for(option_menu, "Back", font); + menu_items_set_font_for(option_menu, kFullScreen, font); + menu_items_set_value_for(option_menu, kFullScreen, fullscreen?"ON":"OFF"); +#ifdef HAVE_OPENGL + menu_items_set_font_for(option_menu, kOpenGL, font); + menu_items_set_value_for(option_menu, kOpenGL, useGL?"ON":"OFF"); +#endif + menu_items_set_value_for(option_menu, kMusic, sound?"ON":"OFF"); + menu_items_set_value_for(option_menu, kAudioFX, fx?"ON":"OFF"); + return option_menu; +} + +MenuItems controls_menu_load (SoFont *font, SoFont *small_font) +{ + static MenuItemsTab controls_menu = { + //MENUITEM("Player 1 Joystick"), + //MENUITEM("Player 2 Joystick"), + MENUITEM_BLANKLINE, + MENUITEM(kPlayer2Left), + MENUITEM(kPlayer2Right), + MENUITEM(kPlayer2Down), + MENUITEM(kPlayer2Clockwise), + MENUITEM(kPlayer2Counterclockwise), + MENUITEM_BLANKLINE, + MENUITEM(kPlayer1Left), + MENUITEM(kPlayer1Right), + MENUITEM(kPlayer1Down), + MENUITEM(kPlayer1Clockwise), + MENUITEM(kPlayer1Counterclockwise), + MENUITEM_BLANKLINE, + MENUITEM("Back"), + MENUITEM_END + }; + //menu_items_set_font_for(controls_menu, "Player 1 Joystick", font); + + menu_items_set_font_for(controls_menu, kPlayer1Left, font); + menu_items_set_font_for(controls_menu, kPlayer1Right, font); + menu_items_set_font_for(controls_menu, kPlayer1Down, font); + menu_items_set_font_for(controls_menu, kPlayer1Clockwise, font); + menu_items_set_font_for(controls_menu, kPlayer1Counterclockwise, font); + + menu_items_set_font_for(controls_menu, kPlayer2Left, font); + menu_items_set_font_for(controls_menu, kPlayer2Right, font); + menu_items_set_font_for(controls_menu, kPlayer2Down, font); + menu_items_set_font_for(controls_menu, kPlayer2Clockwise, font); + menu_items_set_font_for(controls_menu, kPlayer2Counterclockwise, font); + + menu_items_set_font_for(controls_menu, "Back", font); + //menu_items_set_value_for(controls_menu, "Player 1 Joystick", SDL_JoystickName(0)); + //menu_items_set_value_for(controls_menu, kPlayer1Left, "s"); + return controls_menu; +} + +Menu *menu_pause = NULL; + +MenuItems pause_menu_load (SoFont * font) +{ + static MenuItemsTab main_menu = { + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM (kContinueGame), + MENUITEM_BLANKLINE, + MENUITEM (kOptions), + MENUITEM_BLANKLINE, + MENUITEM_BLANKLINE, + MENUITEM (kAbortGame), + MENUITEM_END + }; + static int loaded = 0; + + if (!loaded) { + menu_items_set_font_for (main_menu, kContinueGame, font); + menu_items_set_font_for (main_menu, kOptions, font); + menu_items_set_font_for (main_menu, kAbortGame, font); + loaded = 1; + } + + return main_menu; +} + +PuyoCommander::PuyoCommander(bool fs, bool snd, bool audio) +{ + int init_flags = SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_JOYSTICK; + + SDL_Delay(500); + fullscreen = GetBoolPreference(kFullScreen,fs); +#ifdef HAVE_OPENGL + useGL = GetBoolPreference(kOpenGL,false); +#endif + sound = GetBoolPreference(kMusic,snd); + fx = GetBoolPreference(kAudioFX,audio); + + int music_volume = GetIntPreference(kMusicVolume, 100); + int audio_volume = GetIntPreference(kAudioVolume, 80); + + initGameControls(); +#ifdef __linux__ + /* This Hack Allows Hardware Surface on Linux */ + if (fullscreen) + setenv("SDL_VIDEODRIVER","dga",0); + + if (SDL_Init(init_flags) < 0) { + setenv("SDL_VIDEODRIVER","x11",1); + if (SDL_Init(init_flags) < 0) { + fprintf(stderr, "SDL initialisation error: %s\n", SDL_GetError()); + exit(1); + } + } + else { + if (fullscreen) + SDL_WM_GrabInput(SDL_GRAB_ON); + } +#else + if ( SDL_Init(init_flags) < 0 ) { + fprintf(stderr, "SDL initialisation error: %s\n", SDL_GetError()); + exit(1); + } +#endif + + initControllers(); + initHiScores(AI_NAMES); + +#ifdef USE_AUDIO + audio_init(); + audio_music_start(0); + if (sound==false) Mix_PauseMusic(); + audio_set_music_on_off(sound); + audio_set_sound_on_off(fx); + + audio_set_volume(audio_volume); + audio_music_set_volume(music_volume); +#endif + + display = SDL_SetVideoMode( 640, 480, 0, SDL_ANYFORMAT|SDL_HWSURFACE|SDL_DOUBLEBUF|(fullscreen?SDL_FULLSCREEN:0)|(useGL?SDL_GLSDL:0)); + if ( display == NULL ) { + fprintf(stderr, "SDL_SetVideoMode error: %s\n", + SDL_GetError()); + exit(1); + } + atexit(SDL_Quit); + SDL_ShowCursor(SDL_DISABLE); + + smallFont = SoFont_new(); + SoFont_load (smallFont, IIM_Load_DisplayFormatAlpha ("font4b.png")); + menuFont = SoFont_new(); + SoFont_load (menuFont, IIM_Load_DisplayFormatAlpha ("font3b.png")); + darkFont = SoFont_new(); + SoFont_load (darkFont, IIM_Load_DisplayFormatAlpha ("fontdark.png")); + + IIM_Surface * menuselector = IIM_Load_DisplayFormatAlpha("menusel.png"); + + mainMenu = menu_new(main_menu_load(menuFont),menuselector); + gameOver1PMenu = menu_new(gameover_1p_menu_load(menuFont, smallFont),menuselector); + gameOver2PMenu = menu_new(gameover_2p_menu_load(menuFont, smallFont),menuselector); + nextLevelMenu = menu_new(nextlevel_1p_menu_load(menuFont, smallFont),menuselector); + looserMenu = menu_new(looser_1p_menu_load(menuFont, smallFont),menuselector); + finishedMenu = menu_new(finished_1p_menu_load(menuFont, smallFont),menuselector); + gameOverMenu = gameOver2PMenu; + optionMenu = menu_new(options_menu_load(menuFont, smallFont),menuselector); + controlsMenu = menu_new(controls_menu_load(menuFont, smallFont),menuselector); + rulesMenu = menu_new(rules_menu_load(menuFont),menuselector); + highScoresMenu = menu_new(high_scores_menu_load(menuFont),menuselector); + aboutMenu = menu_new(about_menu_load(menuFont),menuselector); + singleGameMenu = menu_new(single_game_menu_load(menuFont,smallFont),menuselector); + twoPlayerGameMenu = menu_new(two_player_game_menu_load(menuFont,smallFont),menuselector); + mustRestartMenu = menu_new(must_restart_menu_load(menuFont),menuselector); + + if (menu_pause == NULL) menu_pause = menu_new(pause_menu_load(menuFont),menuselector); + + menu_set_sounds (optionMenu, sound_pop, sound_slide); + menu_set_sounds (controlsMenu, sound_pop, sound_slide); + menu_set_sounds (mainMenu, sound_pop, sound_slide); + menu_set_sounds (rulesMenu, sound_pop, sound_slide); + menu_set_sounds (highScoresMenu, sound_pop, sound_slide); + menu_set_sounds (aboutMenu, sound_pop, sound_slide); + menu_set_sounds (singleGameMenu, sound_pop, sound_slide); + menu_set_sounds (twoPlayerGameMenu, sound_pop, sound_slide); + menu_set_sounds (menu_pause , sound_pop, sound_slide); + melt = doom_melt_new(); + + scrollingText = scrolling_text_new( + "Welcome to the wonderful world of FloboPuyo !!! Enjoy its nice graphics, " + "happy music and entertaining gameplay... " + "Will you be able to defeat all of the mighty players ? " + "Will you beat the Puyo Gods ??? Have a try ! " + "We wish you good luck. " + " Hello from PuyoLand !", smallFont); + theCommander = this; +} + +void PuyoCommander::run() +{ + Menu *menu = mainMenu; + + cycle = 0; + lastRenderedCycle = 0; + maxFrameDrop = 10; + + start_time = SDL_GetTicks (); + + audio_music_start (0); + + /* PuyoStory *introStory = new PuyoStory(this, 0); + introStory->loop(); + delete introStory; */ + + menu_show (menu); + + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + goto mml_fin; + break; + case GameControlEvent::kDown: + menu_next_item (menu); + break; + case GameControlEvent::kUp: + menu_prev_item (menu); + break; + case GameControlEvent::kStart: + menu_validate (menu); + if (menu_active_is (menu, "Quit")) + goto mml_fin; + if (menu_active_is (menu, "Options")) { + menu_hide (menu); + optionMenuLoop(NULL); + menu_show (menu); + } + if (menu_active_is (menu, kHighScores)) { + menu_hide(menu); + updateHighScoresMenu(); + backLoop(highScoresMenu); + menu_show(menu); + } + if (menu_active_is (menu, "Rules")) { + menu_hide(menu); + backLoop(rulesMenu); + menu_show(menu); + } + if (menu_active_is (menu, "About FloboPuyo")) { + menu_hide(menu); + backLoop(aboutMenu); + menu_show(menu); + } + if (menu_active_is (menu, SINGLE_PLAYER_GAME)) { + menu_hide (menu); + startSingleGameLoop(); + menu_show (menu); + } + if (menu_active_is (menu, TWO_PLAYERS_GAME)) { + menu_hide (menu); + startTwoPlayerGameLoop(); + menu_show (menu); + audio_music_start(0); + } + break; + case GameControlEvent::kBack: + goto mml_fin; + break; + default: + break; + } + } + updateAll(NULL); + } +mml_fin: + menu_hide (menu); +} + +void PuyoCommander::updateHighScoresMenu(int newOne) +{ + hiscore *scores = getHiScores(); + char tmp[256]; +#define PAS_DE_COMMENTAIRES(X,kXX) \ + if (newOne == X) \ + sprintf(tmp, "%s\t** %d", scores[X].name, scores[X].score); \ + else \ + sprintf(tmp, "%s\t%d", scores[X].name, scores[X].score); \ + menu_set_value(highScoresMenu,kXX,tmp,0); + + PAS_DE_COMMENTAIRES(0,k01); + PAS_DE_COMMENTAIRES(1,k02); + PAS_DE_COMMENTAIRES(2,k03); + PAS_DE_COMMENTAIRES(3,k04); + PAS_DE_COMMENTAIRES(4,k05); + PAS_DE_COMMENTAIRES(5,k06); + PAS_DE_COMMENTAIRES(6,k07); + PAS_DE_COMMENTAIRES(7,k08); + PAS_DE_COMMENTAIRES(8,k09); + PAS_DE_COMMENTAIRES(9,k10); +} + +bool PuyoCommander::changeControlLoop(int controlIndex, PuyoDrawable *starter) +{ + bool keyPressed = false; + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + bool tryOk = tryChangeControl(controlIndex, e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + goto mml_fin; + break; + case GameControlEvent::kBack: + goto mml_fin; + break; + default: + break; + } + if (tryOk) { + keyPressed = true; + goto mml_fin; + } + } + updateAll(starter); + } +mml_fin: + return keyPressed; +} + +void PuyoCommander::controlsMenuLoop(PuyoDrawable *d) +{ + char newKeyName[250]; + + getKeyName(kPlayer1LeftControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Left, newKeyName,0); + getKeyName(kPlayer1RightControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Right, newKeyName,0); + getKeyName(kPlayer1DownControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Down, newKeyName,0); + getKeyName(kPlayer1ClockwiseControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Clockwise, newKeyName,0); + getKeyName(kPlayer1CounterclockwiseControl, newKeyName); + menu_set_value(controlsMenu, kPlayer1Counterclockwise, newKeyName,0); + + getKeyName(kPlayer2LeftControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Left, newKeyName,0); + getKeyName(kPlayer2RightControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Right, newKeyName,0); + getKeyName(kPlayer2DownControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Down, newKeyName,0); + getKeyName(kPlayer2ClockwiseControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Clockwise, newKeyName,0); + getKeyName(kPlayer2CounterclockwiseControl, newKeyName); + menu_set_value(controlsMenu, kPlayer2Counterclockwise, newKeyName,1); + + menu_show(controlsMenu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + char *chosenControl = NULL; + int chosenControlIndex; + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + goto mml_fin; + break; + case GameControlEvent::kUp: + menu_prev_item (controlsMenu); + break; + case GameControlEvent::kDown: + menu_next_item (controlsMenu); + break; + case GameControlEvent::kStart: + menu_validate (controlsMenu); + if (menu_active_is (controlsMenu, "Back")) + goto mml_fin; + + if (menu_active_is (controlsMenu, kPlayer1Left)) { + chosenControl = kPlayer1Left; + chosenControlIndex = kPlayer1LeftControl; + } + if (menu_active_is (controlsMenu, kPlayer1Right)) { + chosenControl = kPlayer1Right; + chosenControlIndex = kPlayer1RightControl; + } + if (menu_active_is (controlsMenu, kPlayer1Down)) { + chosenControl = kPlayer1Down; + chosenControlIndex = kPlayer1DownControl; + } + if (menu_active_is (controlsMenu, kPlayer1Clockwise)) { + chosenControl = kPlayer1Clockwise; + chosenControlIndex = kPlayer1ClockwiseControl; + } + if (menu_active_is (controlsMenu, kPlayer1Counterclockwise)) { + chosenControl = kPlayer1Counterclockwise; + chosenControlIndex = kPlayer1CounterclockwiseControl; + } + + if (menu_active_is (controlsMenu, kPlayer2Left)) { + chosenControl = kPlayer2Left; + chosenControlIndex = kPlayer2LeftControl; + } + if (menu_active_is (controlsMenu, kPlayer2Right)) { + chosenControl = kPlayer2Right; + chosenControlIndex = kPlayer2RightControl; + } + if (menu_active_is (controlsMenu, kPlayer2Down)) { + chosenControl = kPlayer2Down; + chosenControlIndex = kPlayer2DownControl; + } + if (menu_active_is (controlsMenu, kPlayer2Clockwise)) { + chosenControl = kPlayer2Clockwise; + chosenControlIndex = kPlayer2ClockwiseControl; + } + if (menu_active_is (controlsMenu, kPlayer2Counterclockwise)) { + chosenControl = kPlayer2Counterclockwise; + chosenControlIndex = kPlayer2CounterclockwiseControl; + } + + if (chosenControl != NULL) { + char prevValue[255]; + strcpy(prevValue, menu_get_value (controlsMenu, chosenControl)); + menu_set_value(controlsMenu, chosenControl, ""); + if (changeControlLoop(chosenControlIndex, d)) { + getKeyName(chosenControlIndex, newKeyName); + menu_set_value(controlsMenu, chosenControl, newKeyName); + menu_next_item (controlsMenu); + } + else { + menu_set_value(controlsMenu, chosenControl, prevValue); + } + } + break; + case GameControlEvent::kBack: + goto mml_fin; + break; + default: + break; + } + } + + updateAll(d); + } +mml_fin: + saveControls(); + menu_hide (controlsMenu); +} + +void PuyoCommander::optionMenuLoop(PuyoDrawable *d) +{ + menu_show(optionMenu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + goto mml_fin; + break; + case GameControlEvent::kUp: + menu_prev_item (optionMenu); + break; + case GameControlEvent::kDown: + menu_next_item (optionMenu); + break; + case GameControlEvent::kStart: + menu_validate (optionMenu); + if (menu_active_is (optionMenu, "Back")) + goto mml_fin; + if (menu_active_is (optionMenu, kFullScreen)) { + fullscreen = menu_switch_on_off(optionMenu, kFullScreen); + SetBoolPreference(kFullScreen,fullscreen); + if (useGL) + { + menu_hide(optionMenu); + backLoop(mustRestartMenu,d); + menu_show(optionMenu); + } + else + { +#ifdef _WIN32 + menu_hide(optionMenu); + backLoop(mustRestartMenu,d); + menu_show(optionMenu); +#else + SDL_QuitSubSystem(SDL_INIT_VIDEO); + SDL_InitSubSystem(SDL_INIT_VIDEO); + display = SDL_SetVideoMode(640, 480, 0, + SDL_ANYFORMAT|SDL_HWSURFACE|SDL_DOUBLEBUF + |(fullscreen?SDL_FULLSCREEN:0)|(useGL?SDL_GLSDL:0)); +#endif + /* IIM_ReConvertAll(); + SoFont_Refresh(menuFont); + SoFont_Refresh(smallFont); + SoFont_Refresh(darkFont); */ + } + } + if (menu_active_is (optionMenu, kOpenGL)) { + bool useGL2 = menu_switch_on_off(optionMenu, kOpenGL); + SetBoolPreference(kOpenGL,useGL2); + menu_hide(optionMenu); + backLoop(mustRestartMenu,d); + menu_show(optionMenu); + } + if (menu_active_is (optionMenu, kMusic)) { + sound = menu_switch_on_off(optionMenu, kMusic); + SetBoolPreference(kMusic,sound); + audio_set_music_on_off(sound); + if (sound) audio_music_start(0); + } + if (menu_active_is (optionMenu, kAudioFX)) { + fx = menu_switch_on_off(optionMenu, kAudioFX); + SetBoolPreference(kAudioFX,fx); + audio_set_sound_on_off(fx); + } + if (menu_active_is (optionMenu, kControls)) { + menu_hide (optionMenu); + controlsMenuLoop(d); + menu_show(optionMenu); + } + break; + case GameControlEvent::kBack: + goto mml_fin; + break; + default: + break; + } + } + + updateAll(d); + } +mml_fin: + menu_hide (optionMenu); +} + +#include "corona32.h" +#define CORONA_HEIGHT 120 + +void PuyoCommander::backLoop(Menu *menu, PuyoDrawable *starter) +{ + Corona32 *corona = NULL; + int *corona_screen; + + if (menu == aboutMenu) { + corona = corona32_new(); + corona32_resize(corona, 640, CORONA_HEIGHT); + corona_screen = (int*)calloc(640 * CORONA_HEIGHT + 64, sizeof(int)); + } + + menu_show(menu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + goto mml_fin; + case GameControlEvent::kStart: + case GameControlEvent::kBack: + goto mml_fin; + break; + } + } + + if (corona) + { + short frequency[2][512]; + for (int i=0; i<512; ++i) { // Generate random sound. + frequency[0][i] = rand(); + frequency[1][i] = rand(); + } + corona32_update(corona, SDL_GetTicks(), frequency); + corona32_displayRGBA(corona, corona_screen); + SDL_Surface *tmpsurf = + SDL_CreateRGBSurfaceFrom (corona_screen, 640, CORONA_HEIGHT, + 32, 640*4, + 0x00ff0000, 0x0000ff00, 0x000000ff, + 0xff000000); + updateAll(starter, tmpsurf); + SDL_FreeSurface (tmpsurf); + } + else + updateAll(starter); + } +mml_fin: + menu_hide (menu); + if (corona) + { + corona32_delete(corona); + free(corona_screen); + } +} + +void PuyoCommander::enterStringLoop(Menu *menu, const char *kItem, char out[256]) +{ + int len = 0; + char prevValue[256]; + strcpy(prevValue, menu_get_value (menu, kItem)); + out[0] = '_'; + out[1] = 0; + menu_set_value(menu, kItem, "_"); + + while (1) + { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + break; + case GameControlEvent::kStart: + out[len] = 0; + menu_set_value(menu, kItem, out); + menu_validate (menu); + return; + case GameControlEvent::kBack: + strcpy(out, prevValue); + menu_set_value(menu, kItem, prevValue); + return; + } + switch (e.type) { + case SDL_KEYDOWN: + { + char ch = 0; + if ((e.key.keysym.sym >= SDLK_a) && (e.key.keysym.sym <= SDLK_z)) + ch = e.key.keysym.sym; + + if ((ch >= 'a') && (ch <= 'z')) { + if ((len == 0) || (out[len-1] == ' ')) + ch = ch - 'a' + 'A'; + } + + if (e.key.keysym.sym == SDLK_SPACE) + ch = ' '; + + if ((ch!=0) && (len < 10)) { + out[len++] = ch; + out[len] = '_'; + out[len+1] = 0; + } + + if ((e.key.keysym.sym == SDLK_BACKSPACE) && (len > 0)) + { + out[len] = 0; + out[--len] = '_'; + } + menu_set_value(menu, kItem, out, 0); + } + break; + } + } + updateAll(NULL); + } +} + + +void PuyoCommander::startTwoPlayerGameLoop() +{ + char player1Name[256]; + char player2Name[256]; + + GetStrPreference("Player1 Name", player1Name, "Player 1"); + GetStrPreference("Player2 Name", player2Name, "Player 2"); + menu_set_value(twoPlayerGameMenu, kPlayer1Name, player1Name, 0); + menu_set_value(twoPlayerGameMenu, kPlayer2Name, player2Name, 0); + + while (!menu_active_is(twoPlayerGameMenu,kLevelMedium)) + menu_next_item(twoPlayerGameMenu); + + menu_show(twoPlayerGameMenu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + break; + case GameControlEvent::kDown: + menu_next_item (twoPlayerGameMenu); + break; + case GameControlEvent::kUp: + menu_prev_item (twoPlayerGameMenu); + break; + case GameControlEvent::kStart: + menu_validate (twoPlayerGameMenu); + if (menu_active_is(twoPlayerGameMenu,kPlayer2Name)) { + enterStringLoop(twoPlayerGameMenu,kPlayer2Name,player2Name); + menu_next_item (twoPlayerGameMenu); + } + else if (menu_active_is(twoPlayerGameMenu,kPlayer1Name)) { + enterStringLoop(twoPlayerGameMenu,kPlayer1Name,player1Name); + menu_next_item (twoPlayerGameMenu); + } + else + goto mml_play; + break; + case GameControlEvent::kBack: + menu_hide(twoPlayerGameMenu); + return; + break; + } + } + updateAll(NULL); + } + +mml_play: + menu_hide (twoPlayerGameMenu); + + GAME_ACCEL = 2000; + gameLevel = 1; + if (menu_active_is (twoPlayerGameMenu, kLevelMedium)) { + GAME_ACCEL = 1500; + gameLevel = 2; + } + else if (menu_active_is (twoPlayerGameMenu, kLevelHard)) { + GAME_ACCEL = 1000; + gameLevel = 3; + } + + int score1 = 0; + int score2 = 0; + gameOverMenu = gameOver2PMenu; + int currentMusicTheme = 0; + if (menu_active_is(gameOverMenu, "NO")) + menu_next_item(gameOverMenu); + while (menu_active_is(gameOverMenu, "YES")) { + menu_next_item(gameOverMenu); + PuyoStarter myStarter(this,false,0,RANDOM,currentMusicTheme); + audio_music_switch_theme(currentMusicTheme); + p1name = player1Name; + p2name = player2Name; + GAME_ACCEL = 1500; + doom_melt_start(melt, menuBGImage); + myStarter.run(score1, score2, 0, 0, 0); + score1 += myStarter.leftPlayerWin(); + score2 += myStarter.rightPlayerWin(); + currentMusicTheme = (currentMusicTheme + 1) % NB_MUSIC_THEME; + } + + SetStrPreference("Player1 Name", player1Name); + SetStrPreference("Player2 Name", player2Name); + doom_melt_start(melt, gameScreen); +} + + +struct SelIA { + IA_Type type; + int level; +}; + +void PuyoCommander::startSingleGameLoop() +{ + char playerName[256]; + + char * defaultName = getenv("USER"); + if (defaultName == NULL) + defaultName = "Player"; + if (!(defaultName[0]>=32)) + defaultName = "Player"; + if ((defaultName[0]>='a') && (defaultName[0]<='z')) + defaultName[0] += 'A' - 'a'; + + GetStrPreference("Player Name", playerName, defaultName); + menu_set_value(singleGameMenu, kPlayerName, playerName, 0); + + while (!menu_active_is(singleGameMenu,kLevelMedium)) + menu_next_item(singleGameMenu); + + menu_show(singleGameMenu); + while (1) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + GameControlEvent controlEvent; + getControlEvent(e, &controlEvent); + switch (controlEvent.cursorEvent) { + case GameControlEvent::kQuit: + exit(0); + break; + case GameControlEvent::kDown: + menu_next_item (singleGameMenu); + break; + case GameControlEvent::kUp: + menu_prev_item (singleGameMenu); + break; + case GameControlEvent::kStart: + menu_validate (singleGameMenu); + if (menu_active_is(singleGameMenu,kPlayerName)) + enterStringLoop(singleGameMenu,kPlayerName,playerName); + else + goto mml_play; + break; + case GameControlEvent::kBack: + menu_hide(singleGameMenu); + return; + break; + } + } + updateAll(NULL); + } + +mml_play: + menu_hide (singleGameMenu); + + SelIA ia1[] = { {RANDOM, 350}, {FLOBO, 350}, {FLOBO, 250}, {FLOBO, 180}, {FLOBO, 90}, {JEKO, 350}, {TANIA, 320}, {FLOBO, 62}, {RANDOM,0} }; + SelIA ia2[] = { {FLOBO, 190}, {JEKO , 180}, {TANIA, 160}, {FLOBO, 90}, {GYOM , 210}, {TANIA, 90}, {JEKO, 80}, {GYOM, 90}, {RANDOM,0} }; + SelIA ia3[] = { {TANIA, 130}, {JEKO , 100}, {GYOM , 90}, {JEKO, 80}, {TANIA, 60}, {GYOM, 60}, {GYOM, 40}, {GYOM, 30}, {RANDOM,0} }; + + SelIA *ia = &(ia1[0]); + + gameLevel = 1; + GAME_ACCEL = 2000; + if (menu_active_is (singleGameMenu, kLevelMedium)) { + ia = &(ia2[0]); + GAME_ACCEL = 1500; + gameLevel = 2; + } + else if (menu_active_is (singleGameMenu, kLevelHard)) { + ia = &(ia3[0]); + GAME_ACCEL = 1000; + gameLevel = 3; + } + + int score1 = 0; + int score2 = 0; + int lives = 3; + if (menu_active_is(finishedMenu, "NO")) + menu_next_item(finishedMenu); + if (menu_active_is(looserMenu , "NO")) + menu_next_item(looserMenu); + if (menu_active_is(nextLevelMenu, "NO")) + menu_next_item(nextLevelMenu); + if (menu_active_is(gameOver2PMenu, "NO")) + menu_next_item(gameOver2PMenu); + gameOverMenu = nextLevelMenu; + + int lastPoints = 0; + int currentMusicTheme = 0; + + int fini = 0; + while (!fini) + { + PuyoStory myStory(this, score2+1); + myStory.loop(); + PuyoStarter myStarter(this, true, ia[score2].level, ia[score2].type, currentMusicTheme); + p1name = playerName; + p2name = AI_NAMES[score2]; + doom_melt_start(melt, menuBGImage); + audio_music_switch_theme(currentMusicTheme); + myStarter.run(score1, score2, lives, lastPoints, 0); + lastPoints = myStarter.rightPlayerPoints(); + score1 += myStarter.leftPlayerWin(); + score2 += myStarter.rightPlayerWin(); + if (!myStarter.rightPlayerWin()) + lives--; + else { + currentMusicTheme = (currentMusicTheme + 1) % NB_MUSIC_THEME; + } + + if (ia[score2].level == 0) { + lastPoints += 100000; + if (menu_active_is (singleGameMenu, kLevelMedium)) + lastPoints += 150000; + if (menu_active_is (singleGameMenu, kLevelHard)) + lastPoints += 250000; + fini = 1; + } + + if (!(menu_active_is(gameOverMenu, "YES") && (lives >= 0))) + fini = 1; + + if (fini) + { + audio_music_start(0); + int newOne = setHiScore(lastPoints, p1name); + if (newOne >= 0) + { + updateHighScoresMenu(newOne); + backLoop(highScoresMenu, &myStarter); + } + } + } + SetStrPreference("Player Name", playerName); + doom_melt_start(melt, gameScreen); +} + +void PuyoCommander::updateAll(PuyoDrawable *starter, SDL_Surface *extra_surf) +{ + Uint32 now = 0; + + // mise a jour + menu_update (mainMenu, display); + menu_update (gameOverMenu, display); + menu_update (optionMenu, display); + menu_update (controlsMenu, display); + menu_update (rulesMenu, display); + menu_update (highScoresMenu, display); + menu_update (aboutMenu, display); + menu_update (mustRestartMenu, display); + menu_update (singleGameMenu, display); + menu_update (twoPlayerGameMenu, display); + menu_update (menu_pause,display); + scrolling_text_update(scrollingText, display); + doom_melt_update(melt); + + // affichage eventuel (pourrait ne pas avoir lieu de tps en tps si machine + // trop lente) + cycle++; + now = SDL_GetTicks (); + + if ((now < (start_time + cycle * cycle_duration)) || (cycle - lastRenderedCycle > maxFrameDrop)) { + lastRenderedCycle = cycle; + + if (starter) { + starter->draw(); + } + else { + SDL_BlitSurface (menuBGImage->surf, NULL, display, NULL); + } + + if (extra_surf) + { + SDL_Rect rect; + rect.x = 0; + rect.y = 480 - extra_surf->h; + rect.w = extra_surf->w; + rect.h = extra_surf->h; + SDL_BlitSurface(extra_surf, NULL, display, &rect); + } + + if (!starter) + scrolling_text_draw(scrollingText, display, 460); + + menu_draw (mainMenu, display); + menu_draw (gameOverMenu, display); + menu_draw (optionMenu, display); + menu_draw (controlsMenu, display); + menu_draw (rulesMenu, display); + menu_draw (highScoresMenu, display); + menu_draw (aboutMenu, display); + menu_draw (mustRestartMenu, display); + menu_draw (singleGameMenu, display); + menu_draw (twoPlayerGameMenu, display); + menu_draw(menu_pause,display); + doom_melt_display(melt, display); + SDL_Flip (display); + } + + // delay si machine trop rapide + now = SDL_GetTicks (); + if (now < (start_time + cycle * cycle_duration)) + SDL_Delay ((start_time + cycle * cycle_duration) - now); +} + + diff --git a/PuyoCommander.h b/PuyoCommander.h new file mode 100644 index 0000000..ab6be29 --- /dev/null +++ b/PuyoCommander.h @@ -0,0 +1,94 @@ +#ifndef _PUYOCOMMANDER +#define _PUYOCOMMANDER + +#include "sofont.h" +#include "menu.h" +#include "scrollingtext.h" +#include "GameControls.h" +#include "PuyoDoomMelt.h" + +#include "corona32.h" +#define CORONA_HEIGHT 120 + +class PuyoDrawable +{ + public: + virtual void draw() = 0; +}; + +class PuyoCommander +{ + public: + PuyoCommander(bool fullscreen,bool sound, bool audio); + + void run(); + void updateAll(PuyoDrawable *starter, SDL_Surface *extra_surf = NULL); + + void showGameOver() { menu_show(gameOverMenu); } + void hideGameOver() { menu_hide(gameOverMenu); } + + bool changeControlLoop(int controlIndex, PuyoDrawable *starter); + void controlsMenuLoop(PuyoDrawable *d); + void optionMenuLoop(PuyoDrawable *d = NULL); + void backLoop(Menu *menu, PuyoDrawable *starter = NULL); + void startSingleGameLoop(); + void startTwoPlayerGameLoop(); + void enterStringLoop(Menu *menu, const char *kItem, char out[256]); + + Menu *gameOverMenu; + + Menu *gameOver2PMenu; + Menu *gameOver1PMenu; + Menu *nextLevelMenu; + Menu *finishedMenu; + Menu *looserMenu; + Menu *highScoresMenu; + void updateHighScoresMenu(int newOne = -1); + + SoFont *smallFont; + SoFont *darkFont; + SoFont *menuFont; + DoomMelt *melt; + + private: + Menu *mainMenu; + Menu *singleGameMenu; + Menu *twoPlayerGameMenu; + Menu *optionMenu; + Menu *controlsMenu; + Menu *rulesMenu; + Menu *aboutMenu; + Menu *mustRestartMenu; + ScrollingText *scrollingText; + unsigned int cycle, start_time; + unsigned int lastRenderedCycle; + unsigned int maxFrameDrop; + + static const int player1Joystick = 1; + static const int player2Joystick = 0; + + int *corona_screen; + Corona32 *corona; + +}; + + +extern SDL_Surface *display; +extern class PuyoCommander *theCommander; + +extern char *kYouDidIt; +extern char *kNextLevel; +extern char *kLooser; +extern char *kCurrentLevel; +extern char *kContinueLeft; +extern char *kGameOver; +extern char *kYouGotToLevel; +extern char *kHitActionToContinue; +extern char *kContinue; +extern char *kPlayer; +extern char *kScore; +extern char *kContinueGame; +extern char *kAbortGame; +extern char *kOptions; + +#endif // _PUYOCOMMANDER diff --git a/PuyoDoomMelt.c b/PuyoDoomMelt.c new file mode 100644 index 0000000..66db5f8 --- /dev/null +++ b/PuyoDoomMelt.c @@ -0,0 +1,167 @@ +/* Copyright (C) 2002 W.P. van Paassen - peter@paassen.tmfweb.nl, + Ed Sinjiashvili - slimb@swes.saren.ru + dekoder - dekoder81@users.sourceforge.net + Copyright (C) 2004 flobo for this hacked version + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* There is still room for improvements... */ + +#include +#include +#include +#include +#include "PuyoDoomMelt.h" + +/* A few definitions */ + +#define SCREEN_WIDTH 640 +#define SCREEN_HEIGHT 480 +#define COL_WIDTH 8 +#define NUM_COLS SCREEN_WIDTH / COL_WIDTH + 1 + +static int rand_fun () { + return rand() % 256; +} + +typedef struct column { + short x; + short y; +} column_t; + +/* Private methods */ + +static void _init_columns (DoomMelt *_this); +static void _column_draw (column_t *column, SDL_Surface *meltImage, SDL_Surface *screen); +static void _column_think (column_t *column, int *isFinished); + + +/* Private members */ + +struct _DoomMelt { + column_t columns[NUM_COLS]; + IIM_Surface *surf; + int isFinished; +}; + +/* Public methods */ + +DoomMelt *doom_melt_new (void) +{ + DoomMelt *_this = (DoomMelt*)malloc(sizeof(DoomMelt)); + _this->isFinished = 1; + return _this; +} + +void doom_melt_delete (DoomMelt *_this) +{ + free(_this); +} + +void doom_melt_start (DoomMelt *_this, IIM_Surface *surf) +{ + _this->surf = surf; + _this->isFinished = 0; + _init_columns(_this); +} + +void doom_melt_update (DoomMelt *_this) +{ + if (!_this->isFinished) + { + int i = 0; + _this->isFinished = 1; + for (; i < NUM_COLS; i++) + _column_think (&_this->columns[i], &_this->isFinished); + } +} + +void doom_melt_display(DoomMelt *_this, SDL_Surface *display) +{ + if (!_this->isFinished) + { + int i = 0; + for (; i < NUM_COLS; i++) + _column_draw(&_this->columns[i], _this->surf->surf, display); + } +} + +/* + * Private methods + */ + +void _init_columns (DoomMelt *_this) +{ + int i, start_x = 1; + + _this->columns[0].y = -(rand_fun() % 16); + _this->columns[0].x = 0; + + for (i = 1; i < NUM_COLS; i++) + { + int r = (rand_fun() % 3) - 1; + _this->columns[i].y = _this->columns[i-1].y + r; + if (_this->columns[i].y > 0) + _this->columns[i].y = 0; + else if (_this->columns[i].y == -16) + _this->columns[i].y = -15; + + _this->columns[i].x = start_x; + + start_x += COL_WIDTH; + } +} + +void _column_draw (column_t *column, SDL_Surface *meltImage, SDL_Surface *display) +{ + static SDL_Rect image_rect = {0, 0, COL_WIDTH, }; + static SDL_Rect dest_rect = {0, 0, COL_WIDTH, SCREEN_HEIGHT}; + + int tmp = column->y; + if (tmp < 0) + tmp = 0; + + dest_rect.x = column->x; + dest_rect.y = tmp; + + image_rect.x = column->x; + image_rect.h = meltImage->h - tmp; + + SDL_BlitSurface(meltImage, &image_rect, display, &dest_rect); +} + +void _column_think (column_t *column, int *isFinished) +{ + static int grow = 0; + + if (column->y < 0) + { + *isFinished = 0; + grow = 1; + } + else if (column->y < SCREEN_HEIGHT) + { + *isFinished = 0; + if (column->y < 16) + grow = column->y+3; + else + { + grow = 15; + } + } + + column->y += grow; +} + diff --git a/PuyoDoomMelt.h b/PuyoDoomMelt.h new file mode 100644 index 0000000..bdd64c5 --- /dev/null +++ b/PuyoDoomMelt.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2002 W.P. van Paassen - peter@paassen.tmfweb.nl, + Ed Sinjiashvili - slimb@swes.saren.ru + dekoder - dekoder81@users.sourceforge.net + Copyright (C) 2004 flobo for this hacked version + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _PUYODOOMMELT +#define _PUYODOOMMELT + +#include "glSDL.h" +#include "IosImgProcess.h" + +typedef struct _DoomMelt DoomMelt; + +DoomMelt *doom_melt_new (void); +void doom_melt_delete (DoomMelt *_this); +void doom_melt_update (DoomMelt *_this); +void doom_melt_start (DoomMelt *_this, IIM_Surface *surf); +void doom_melt_display(DoomMelt *_this, SDL_Surface *display); + +#endif /*_PUYODOOMMELT*/ + diff --git a/PuyoGame.cpp b/PuyoGame.cpp new file mode 100644 index 0000000..66fa340 --- /dev/null +++ b/PuyoGame.cpp @@ -0,0 +1,686 @@ +/* FloboPuyo + * Copyright (C) 2004 + * Florent Boudet , + * Jean-Christophe Hoelt , + * Guillaume Borios + * + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#include +#include +#include "PuyoGame.h" +#include "audio.h" +#include "glSDL.h" + + +static int fallingTable[PUYODIMX] = {0, 3, 1, 4, 2, 5}; + +PuyoRandomSystem::PuyoRandomSystem() +{ + srandom(SDL_GetTicks()); +} + +PuyoState PuyoRandomSystem::getPuyoForSequence(int sequence) +{ + if (sequenceItems.getSize() <= sequence) { + int newItem = (random() % 5) + PUYO_FALLINGBLUE; + sequenceItems.addElement((void *)newItem); + return (PuyoState)newItem; + } + else + return (PuyoState)(int)(sequenceItems.getElementAt(sequence)); +} + +PuyoPuyo::PuyoPuyo(PuyoState state) +{ + this->state = state; + X = 0; Y = 0; +} + +bool PuyoPuyo::isFalling() +{ + return (state < PUYO_EMPTY); +} + +PuyoState PuyoPuyo::getPuyoState() +{ + if (this != NULL) + return state; + return PUYO_EMPTY; +} + +void PuyoPuyo::setPuyoState(PuyoState state) +{ + if (this != NULL) + this->state = state; +} + +int PuyoPuyo::getPuyoX() const +{ + if (this != NULL) + return X; + return 0; +} + +int PuyoPuyo::getPuyoY() const +{ + if (this != NULL) + return Y; + return 0; +} + +void PuyoPuyo::setPuyoXY(int X, int Y) +{ + if (this != NULL) + { + this->X = X; this->Y = Y; + } +} + +PuyoGame::PuyoGame(PuyoRandomSystem *attachedRandom, + PuyoFactory *attachedFactory) +{ + this->attachedFactory = attachedFactory; + InitGame(attachedRandom); +} + +PuyoGame::PuyoGame(PuyoRandomSystem *attachedRandom) +{ + attachedFactory = new PuyoDefaultFactory(); + InitGame(attachedRandom); +} + +void PuyoGame::InitGame(PuyoRandomSystem *attachedRandom) +{ + nbFalled = 0; + int i, j; + unmoveablePuyo = attachedFactory->createPuyo(PUYO_UNMOVEABLE); + for (i = 0 ; i < PUYODIMX ; i++) { + for (j = 0 ; j <= PUYODIMY ; j++) { + if (j == PUYODIMY) + setPuyoAt(i, j, unmoveablePuyo); + else + setPuyoAt(i, j, NULL); + } + } + this->attachedRandom = attachedRandom; + sequenceNr = 0; + semiMove = 0; + neutralPuyos = 0; + delegate = NULL; + endOfCycle = false; + gameRunning = true; + setFallingAtTop(true); + points = 0; +} + +PuyoGame::~PuyoGame() +{ + delete unmoveablePuyo; +} + +void PuyoGame::setDelegate(PuyoDelegate *delegate) +{ + this->delegate = delegate; +} + +void PuyoGame::cycle() +{ + if (!gameRunning) + return; + + semiMove = 1 - semiMove; + if (semiMove == 0) + return; + + if (endOfCycle) { + cycleEnding(); + if (delegate != NULL) + notifyReductions(); + return; + } + if ((fallingY >= 0)&&(getPuyoCellAt(fallingX, fallingY+1) > PUYO_EMPTY) || (getPuyoCellAt(getFallingCompanionX(), getFallingCompanionY()+1) > PUYO_EMPTY)) { + setPuyoAt(fallingX, getFallY(fallingX, fallingY), fallingPuyo); + fallingPuyo->setPuyoState((PuyoState)(fallingPuyo->getPuyoState()+PUYO_STILL)); + setPuyoAt(getFallingCompanionX(), getFallY(getFallingCompanionX(), getFallingCompanionY()), companionPuyo); + companionPuyo->setPuyoState((PuyoState)(companionPuyo->getPuyoState()+PUYO_STILL)); + if (delegate != NULL) { + delegate->puyoDidFall(fallingPuyo, fallingX, fallingY); + delegate->puyoDidFall(companionPuyo, getFallingCompanionX(), getFallingCompanionY()); + fallingY = -10; + notifyReductions(); + } + endOfCycle = true; + } + else { + fallingY++; + fallingPuyo->setPuyoXY(fallingX, fallingY); + companionPuyo->setPuyoXY(getFallingCompanionX(), getFallingCompanionY()); + } +} + +// Get the state of the puyo at the indicated coordinates +PuyoState PuyoGame::getPuyoCellAt(int X, int Y) const +{ + PuyoPuyo *thePuyo = getPuyoAt(X, Y); + if (thePuyo) + return thePuyo->getPuyoState(); + return PUYO_EMPTY; +} + +// Get the puyo at the indicated coordinates +PuyoPuyo *PuyoGame::getPuyoAt(int X, int Y) const +{ + if ((X >= PUYODIMX) || (Y >= PUYODIMY) || (X < 0) || (Y < 0)) + return unmoveablePuyo; + if (!endOfCycle) { + if ((X == fallingX) && (Y == fallingY)) + return fallingPuyo; + if ((X == getFallingCompanionX()) && (Y == getFallingCompanionY())) + return companionPuyo; + } + return puyoCells[X + Y * PUYODIMX]; +} + +// List access to the PuyoPuyo objects +int PuyoGame::getPuyoCount() const +{ + return puyoVector.getSize(); +} + +PuyoPuyo *PuyoGame::getPuyoAtIndex(int index) const +{ + return (PuyoPuyo *)(puyoVector.getElementAt(index)); +} + +void PuyoGame::moveLeft() +{ + if (endOfCycle) { + return; + } + if (((fallingY<0)&&(fallingX>0))||((getPuyoCellAt(fallingX-1, fallingY) <= PUYO_EMPTY) + && (getPuyoCellAt(getFallingCompanionX()-1, getFallingCompanionY()) <= PUYO_EMPTY))) + { + fallingX--; + } + fallingPuyo->setPuyoXY(fallingX, fallingY); + companionPuyo->setPuyoXY(getFallingCompanionX(), getFallingCompanionY()); +} + +void PuyoGame::moveRight() +{ + if (endOfCycle) { + return; + } + if (((fallingY<0)&&(fallingXsetPuyoXY(fallingX, fallingY); + companionPuyo->setPuyoXY(getFallingCompanionX(), getFallingCompanionY()); +} + +void PuyoGame::rotate(bool left) +{ + if (endOfCycle) { + return; + } + unsigned char backupCompanion = fallingCompanion; + /*int backupX = fallingX; + int backupY = fallingY;*/ + int newX, newY; + bool moved = true; + fallingCompanion = (unsigned char)(fallingCompanion + (left?3:1)) % 4; + unsigned char newCompanion = fallingCompanion; + int newCompanionX = getFallingCompanionX(); + int newCompanionY = getFallingCompanionY(); + fallingCompanion = backupCompanion; + if (getPuyoCellAt(newCompanionX, newCompanionY) > PUYO_EMPTY) { + //if (((fallingY<0)&&(fallingX>0)&&(fallingX PUYO_EMPTY)) { + newX = fallingX + (fallingX - newCompanionX); + newY = fallingY + (fallingY - newCompanionY); + // if ((fallingY >= 0) && (getPuyoCellAt(newX, newY) > PUYO_EMPTY)) { + if (getPuyoCellAt(newX, newY) > PUYO_EMPTY) { + moved = false; + } + else { + fallingCompanion = newCompanion; + fallingX = newX; + if (fallingY != newY) + { + semiMove = 0; + fallingY = newY; + } + fallingPuyo->setPuyoXY(fallingX, fallingY); + } + } + else + fallingCompanion = newCompanion; + + companionPuyo->setPuyoXY(getFallingCompanionX(), getFallingCompanionY()); + + if ((delegate != NULL) && (moved)) + delegate->companionDidTurn(companionPuyo, fallingCompanion, !left); + +} + +void PuyoGame::rotateLeft() +{ + rotate(true); +} + +void PuyoGame::rotateRight() +{ + rotate(false); +} + +PuyoState PuyoGame::getNextFalling() +{ + return attachedRandom->getPuyoForSequence(sequenceNr); +} + +PuyoState PuyoGame::getNextCompanion() +{ + return attachedRandom->getPuyoForSequence(sequenceNr+1); +} + +void PuyoGame::increaseNeutralPuyos(int incr) +{ + neutralPuyos += incr; +} + +int PuyoGame::getNeutralPuyos() const +{ + return neutralPuyos; +} + + + +// Set the state of the puyo at the indicated coordinates (not recommanded) +void PuyoGame::setPuyoCellAt(int X, int Y, PuyoState value) +{ + /*if ((X > PUYODIMX) || (Y > PUYODIMY)) + return;*/ + if (puyoCells[X + Y * PUYODIMX]) + puyoCells[X + Y * PUYODIMX]->setPuyoState(value); +}; + +// Set the puyo at the indicated coordinates +void PuyoGame::setPuyoAt(int X, int Y, PuyoPuyo *newPuyo) +{ + puyoCells[X + Y * PUYODIMX] = newPuyo; + if (newPuyo != NULL) + newPuyo->setPuyoXY(X, Y); +} + +void PuyoGame::dropNeutrals() +{ + if (neutralPuyos < 0) { + extern int gameLevel; + points -= gameLevel * neutralPuyos * 1000; + } + + int idNeutral = 0; + while (neutralPuyos > 0) + { + int cycleNeutral; + if (neutralPuyos >= PUYODIMX) + cycleNeutral = PUYODIMX; + else + cycleNeutral = neutralPuyos; + for (int i = 0 ; i < cycleNeutral ; i++) + { + int posX = fallingTable[(nbFalled++) % PUYODIMX]; + int posY = getFallY(posX, 2); + neutralPuyos -= 1; + if (getPuyoCellAt(posX, posY) != PUYO_EMPTY) + continue; + // Creating a new neutral puyo + PuyoPuyo *newNeutral = attachedFactory->createPuyo(PUYO_NEUTRAL); + puyoVector.addElement(newNeutral); + setPuyoAt(posX, posY, newNeutral); + if (delegate != NULL) + delegate->gameDidAddNeutral(newNeutral, idNeutral++); + } + } + neutralPuyos = 0; +} + +void PuyoGame::setFallingAtTop(bool gameConstruction) +{ + if (delegate != NULL) + delegate->gameDidEndCycle(); + + if (!gameConstruction) { + dropNeutrals(); + if (getPuyoCellAt((PUYODIMX-1)/2, 1) != PUYO_EMPTY) { + gameRunning = false; + if (delegate != NULL) + delegate->gameLost(); + return; + } + } + + // Creating the new falling puyo and its companion + fallingX = (PUYODIMX-1)/2; + fallingY = 1; + fallingCompanion = 2; + fallingPuyo = attachedFactory->createPuyo(attachedRandom->getPuyoForSequence(sequenceNr++)); + companionPuyo = attachedFactory->createPuyo(attachedRandom->getPuyoForSequence(sequenceNr++)); + fallingPuyo->setPuyoXY(fallingX, fallingY); + companionPuyo->setPuyoXY(getFallingCompanionX(), getFallingCompanionY()); + puyoVector.addElement(fallingPuyo); + puyoVector.addElement(companionPuyo); + + endOfCycle = false; + semiMove = 0; + phase = 0; +} + +int PuyoGame::getFallingCompanionX() const +{ + if (fallingCompanion == 1) + return fallingX - 1; + if (fallingCompanion == 3) + return fallingX + 1; + return fallingX; +} + +int PuyoGame::getFallingCompanionY() const +{ + if (fallingCompanion == 0) + return fallingY + 1; + if (fallingCompanion == 2) + return fallingY - 1; + return fallingY; +} + +int PuyoGame::getFallY(int X, int Y) const +{ + int result = Y + 1; + while (getPuyoCellAt(X, result) == PUYO_EMPTY) + result++; + return result - 1; +} + +int PuyoGame::getColumnHeigth(int colNum) const +{ + int result = 0; + for (int i = 0 ; i < PUYODIMY ; i++) { + if (getPuyoCellAt(colNum, i) > PUYO_EMPTY) + result++; + } + return result; +} + +int PuyoGame::getMaxColumnHeight() const +{ + int max = 0; + for (int i=0;imax) max=v; + } + return max; +} + +int PuyoGame::getSamePuyoAround(int X, int Y, PuyoState color) +{ + char marked[PUYODIMX][PUYODIMY]; + int mx[PUYODIMY*PUYODIMX]; + int my[PUYODIMX*PUYODIMY]; + int nFound = 1; + bool again = true; + mx[0] = X; + my[0] = Y; + + for (int x=0;x=0;--i) { + X = mx[i]; + Y = my[i]; + if ((Y+1=0) && !marked[X-1][Y] && (getPuyoCellAt(X-1,Y) == color)) { + again = true; + mx[nFound] = X-1; + my[nFound] = Y; + marked[X-1][Y] = 1; + nFound++; + } + if ((Y-1>=0) && !marked[X][Y-1] && (getPuyoCellAt(X,Y-1) == color)) { + again = true; + mx[nFound] = X; + my[nFound] = Y-1; + marked[X][Y-1] = 1; + nFound++; + } + } + } + return nFound; +} + +void PuyoGame::markPuyoAt(int X, int Y, PuyoState color, bool includeNeutral) +{ + PuyoState currentPuyo = getPuyoCellAt(X, Y); + setPuyoCellAt(X, Y, color); + if (getPuyoCellAt(X-1, Y) == currentPuyo) + markPuyoAt(X-1, Y, color, includeNeutral); + if (getPuyoCellAt(X+1, Y) == currentPuyo) + markPuyoAt(X+1, Y, color, includeNeutral); + if (getPuyoCellAt(X, Y-1) == currentPuyo) + markPuyoAt(X, Y-1, color, includeNeutral); + if (getPuyoCellAt(X, Y+1) == currentPuyo) + markPuyoAt(X, Y+1, color, includeNeutral); + if (includeNeutral) { + if (getPuyoCellAt(X-1, Y) == PUYO_NEUTRAL) + setPuyoCellAt(X-1, Y, color); + if (getPuyoCellAt(X+1, Y) == PUYO_NEUTRAL) + setPuyoCellAt(X+1, Y, color); + if (getPuyoCellAt(X, Y-1) == PUYO_NEUTRAL) + setPuyoCellAt(X, Y-1, color); + if (getPuyoCellAt(X, Y+1) == PUYO_NEUTRAL) + setPuyoCellAt(X, Y+1, color); + } +} + +// delete the marked puyos and the neutral next to them +void PuyoGame::deleteMarkedPuyosAt(int X, int Y) +{ + PuyoState currentPuyo = getPuyoCellAt(X, Y); + puyoVector.removeElement(getPuyoAt(X, Y)); + if (getPuyoAt(X,Y) == companionPuyo) { + attachedFactory->deletePuyo(getPuyoAt(X, Y)); + companionPuyo = NULL; + } + else if (getPuyoAt(X,Y) == fallingPuyo) { + attachedFactory->deletePuyo(getPuyoAt(X, Y)); + fallingPuyo = NULL; + } else { + attachedFactory->deletePuyo(getPuyoAt(X, Y)); + } + setPuyoAt(X, Y, NULL); + if (getPuyoCellAt(X-1, Y) == currentPuyo) + deleteMarkedPuyosAt(X-1, Y); + if (getPuyoCellAt(X+1, Y) == currentPuyo) + deleteMarkedPuyosAt(X+1, Y); + if (getPuyoCellAt(X, Y-1) == currentPuyo) + deleteMarkedPuyosAt(X, Y-1); + if (getPuyoCellAt(X, Y+1) == currentPuyo) + deleteMarkedPuyosAt(X, Y+1); + if (getPuyoCellAt(X-1, Y) == PUYO_NEUTRAL) { + puyoVector.removeElement(getPuyoAt(X-1, Y)); + attachedFactory->deletePuyo(getPuyoAt(X-1, Y)); + setPuyoAt(X-1, Y, NULL); + } + if (getPuyoCellAt(X+1, Y) == PUYO_NEUTRAL) { + puyoVector.removeElement(getPuyoAt(X+1, Y)); + attachedFactory->deletePuyo(getPuyoAt(X+1, Y)); + setPuyoAt(X+1, Y, NULL); + } + if (getPuyoCellAt(X, Y-1) == PUYO_NEUTRAL) { + puyoVector.removeElement(getPuyoAt(X, Y-1)); + attachedFactory->deletePuyo(getPuyoAt(X, Y-1)); + setPuyoAt(X, Y-1, NULL); + } + if (getPuyoCellAt(X, Y+1) == PUYO_NEUTRAL) { + puyoVector.removeElement(getPuyoAt(X, Y+1)); + attachedFactory->deletePuyo(getPuyoAt(X, Y+1)); + setPuyoAt(X, Y+1, NULL); + } +} + +int PuyoGame::removePuyos() +{ + int globalRemovedPuyos = 0; + /* First, we will mark all the puyos that need to be removed */ + for (int i = 0 ; i < PUYODIMX ; i++) { + for (int j = 0 ; j <= PUYODIMY ; j++) { + PuyoState currentPuyo = getPuyoCellAt(i, j); + if ((currentPuyo >= PUYO_BLUE) && (currentPuyo <= PUYO_YELLOW)) { + int removedPuyos = 0; + markPuyoAt(i, j, PUYO_MARKED, false); + for (int u = 0 ; u < PUYODIMX ; u++) { + for (int v = 0 ; v <= PUYODIMY ; v++) { + if (getPuyoCellAt(u, v) == PUYO_MARKED) { + removedPuyos++; + } + } + } + //printf("Removed for %d, %d : %d\n", i, j, removedPuyos); + if (removedPuyos >= 4) { + globalRemovedPuyos += removedPuyos; + deleteMarkedPuyosAt(i, j); + } + else + markPuyoAt(i, j, currentPuyo, false); + } + } + } + + /* Next we make the other puyos fall */ + for (int i = 0 ; i < PUYODIMX ; i++) { + for (int j = PUYODIMY - 1 ; j > 0 ; j--) { + PuyoState currentPuyoState = getPuyoCellAt(i, j); + if ((currentPuyoState >= PUYO_BLUE) && (currentPuyoState <= PUYO_NEUTRAL)) { + int newJ = getFallY(i, j); + if (newJ != j) { + PuyoPuyo *currentPuyo = getPuyoAt(i, j); + setPuyoAt(i, j, NULL); + setPuyoAt(i, newJ, currentPuyo); + if (delegate != NULL) { + delegate->puyoDidFall(currentPuyo, i, j); + } + } + } + } + } + + return globalRemovedPuyos; +} + + +void PuyoGame::notifyReductions() +{ + IosVector removedPuyos; + + // Clearing every puyo's flag + for (int i = 0, j = getPuyoCount() ; i < j ; i++) { + getPuyoAtIndex(i)->unsetFlag(); + } + // Search for groupped puyos + int puyoGroupNumber = 0; + for (int j = 0 ; j < PUYODIMY ; j++) { + for (int i = 0 ; i <= PUYODIMX ; i++) { + PuyoPuyo *puyoToMark = getPuyoAt(i, j); + // If the puyo exists and is not flagged, then + if ((puyoToMark != NULL) && (! puyoToMark->getFlag())) { + PuyoState initialPuyoState = puyoToMark->getPuyoState(); + // I really would have liked to skip this stupid test + if ((initialPuyoState >= PUYO_BLUE) && (initialPuyoState <= PUYO_YELLOW)) { + markPuyoAt(i, j, PUYO_MARKED, false); + + // Collecting every marked puyo in a vector + removedPuyos.removeAllElements(); + for (int u = 0 ; u < PUYODIMX ; u++) { + for (int v = 0 ; v <= PUYODIMY ; v++) { + PuyoPuyo *markedPuyo = getPuyoAt(u, v); + if (markedPuyo->getPuyoState() == PUYO_MARKED) { + // mark the puyo so we wont'do the job twice + markedPuyo->setFlag(); + removedPuyos.addElement(markedPuyo); + } + } + } + + + // If there is more than 4 puyo in the group, let's notify it + if (removedPuyos.getSize() >= 4) { + markPuyoAt(i, j, initialPuyoState, false); + if (delegate != NULL) + delegate->puyoWillVanish(removedPuyos, puyoGroupNumber++, phase); + } + else { + markPuyoAt(i, j, initialPuyoState, false); + } + } + } + } + } +} + +void PuyoGame::cycleEnding() +{ + static int cmpt = 0; + int score = removePuyos(); + + if (score >= 4) { + #ifdef DESACTIVE + audio_sound_play(sound_splash[phase>7?7:phase]); + #endif + score -= 3; + if (phase > 0) { + neutralPuyos -= PUYODIMX; + } + phase++; + } + + extern int gameLevel; + points += gameLevel * 100 + gameLevel * (phase>0?phase-1:0) * 5000; + + neutralPuyos -= score; + if (score == 0) + setFallingAtTop(); +} diff --git a/PuyoGame.h b/PuyoGame.h new file mode 100644 index 0000000..9fa207a --- /dev/null +++ b/PuyoGame.h @@ -0,0 +1,181 @@ +#include "IosVector.h" + +#ifdef _WIN32 +#define srandom srand +#define random rand +#endif + +#ifndef PUYOGAME_H +#define PUYOGAME_H + +#define PUYODIMX 6 +#define PUYODIMY 14 + +#define PUYO_STILL PUYO_BLUE-PUYO_FALLINGBLUE + +enum PuyoState { + PUYO_FALLINGBLUE = 0, + PUYO_FALLINGRED = 1, + PUYO_FALLINGGREEN = 2, + PUYO_FALLINGVIOLET = 3, + PUYO_FALLINGYELLOW = 4, + PUYO_EMPTY = 5, + PUYO_BLUE = 6, + PUYO_RED = 7, + PUYO_GREEN = 8, + PUYO_VIOLET = 9, + PUYO_YELLOW = 10, + PUYO_NEUTRAL = 11, + PUYO_UNMOVEABLE = 12, + PUYO_MARKED = 13, + PUYO_REMOVED = 14 +}; + +class PuyoRandomSystem { +public: + PuyoRandomSystem(); + PuyoState getPuyoForSequence(int sequence); +private: + IosVector sequenceItems; +}; + +// A PuyoPuyo is an entity of the game +class PuyoPuyo { +public: + PuyoPuyo(PuyoState state); + virtual ~PuyoPuyo() {}; + PuyoState getPuyoState(); + void setPuyoState(PuyoState state); + bool isFalling(); + int getPuyoX() const; + int getPuyoY() const; + void setPuyoXY(int X, int Y); + void setFlag() { flag = true; } + void unsetFlag() { flag = false; } + bool getFlag() const { return flag; } +private: + PuyoState state; + int X, Y; + bool flag; +}; + +// The puyos must be created by a factory to ensure custom puyo creation +class PuyoFactory { + public: + virtual PuyoPuyo *createPuyo(PuyoState state) = 0; + virtual void deletePuyo(PuyoPuyo *target) { delete target; } +}; + +class PuyoDefaultFactory : public PuyoFactory { + public: + PuyoPuyo *createPuyo(PuyoState state) { + return new PuyoPuyo(state); + } +}; + +class PuyoDelegate { +public: + virtual void gameDidAddNeutral(PuyoPuyo *neutralPuyo, int neutralIndex) = 0; + virtual void companionDidTurn(PuyoPuyo *companionPuyo, + int companionVector, + bool counterclockwise) = 0; + virtual void puyoDidFall(PuyoPuyo *puyo, int originX, int originY) = 0; + virtual void puyoWillVanish(IosVector &puyoGroup, int groupNum, int phase) = 0; + virtual void gameDidEndCycle() = 0; + virtual void gameLost() = 0; +}; + +class PuyoGame { +public: + PuyoGame(PuyoRandomSystem *attachedRandom, PuyoFactory *attachedFactory); + PuyoGame(PuyoRandomSystem *attachedRandom); + virtual ~PuyoGame(); + void setDelegate(PuyoDelegate *delegate); + void cycle(); + + // Get the state of the puyo at the indicated coordinates + PuyoState getPuyoCellAt(int X, int Y) const; + // Get the puyo at the indicated coordinates + PuyoPuyo *getPuyoAt(int X, int Y) const; + + // List access to the PuyoPuyo objects + int getPuyoCount() const; + PuyoPuyo *getPuyoAtIndex(int index) const; + + void moveLeft(); + void moveRight(); + void rotate(bool left); + void rotateLeft(); + void rotateRight(); + PuyoState getNextFalling(); + PuyoState getNextCompanion(); + PuyoState getCompanionState() const { return companionPuyo->getPuyoState(); } + PuyoState getFallingState() const { return fallingPuyo->getPuyoState(); } + + int getFallingX() const { return fallingPuyo->getPuyoX(); } + int getFallingY() const { return fallingPuyo->getPuyoY(); } + int getCompanionX() const { return companionPuyo->getPuyoX(); } + int getCompanionY() const { return companionPuyo->getPuyoY(); } + int getFallingCompanionX() const; + int getFallingCompanionY() const; + int getFallingCompanionDir() const { return fallingCompanion; } + PuyoPuyo *getFallingPuyo() const { return fallingPuyo; } + + void increaseNeutralPuyos(int incr); + int getNeutralPuyos() const; + void dropNeutrals(); + bool isGameRunning() const { return gameRunning; } + bool isEndOfCycle() const { return endOfCycle; } + int getColumnHeigth(int colNum) const; + int getMaxColumnHeight() const; + int getSamePuyoAround(int X, int Y, PuyoState color); + + int getSemiMove() const { return semiMove; } + int getPoints() const { return points; } + int points; + + private: + void InitGame(PuyoRandomSystem *attachedRandom); + // Set the state of the puyo at the indicated coordinates (not recommanded) + void setPuyoCellAt(int X, int Y, PuyoState value); + // Set the puyo at the indicated coordinates + void setPuyoAt(int X, int Y, PuyoPuyo *newPuyo); + + void setFallingAtTop(bool gameConstruction = false); + int getFallY(int X, int Y) const; + void cycleEnding(); + void markPuyoAt(int X, int Y, PuyoState color, bool includeNeutral); + void deleteMarkedPuyosAt(int X, int Y); + int removePuyos(); + void notifyReductions(); + + bool gameRunning; + bool endOfCycle; + + // The falling is the puyo you couldn't control, + // whereas you can make the companion turn around the falling puyo + PuyoPuyo *fallingPuyo, *companionPuyo; + int fallingX, fallingY; + + // Position of the companion is relative of the falling puyo + // 0 = up 1 = left 2 = down 3 = up + unsigned char fallingCompanion; + + PuyoPuyo *puyoCells[PUYODIMX * (PUYODIMY+1)]; + PuyoRandomSystem *attachedRandom; + int sequenceNr; + int neutralPuyos; + PuyoDelegate *delegate; + PuyoFactory *attachedFactory; + int phase; + int semiMove; + + // This is not really a puyo, it is instead an indicator for the edges of the game + PuyoPuyo *unmoveablePuyo; + + // We are keeping a list of current puyos + IosVector puyoVector; + int nbFalled; +}; + +#endif // PUYOGAME_H diff --git a/PuyoIA.cpp b/PuyoIA.cpp new file mode 100644 index 0000000..c403976 --- /dev/null +++ b/PuyoIA.cpp @@ -0,0 +1,338 @@ +#include +#include +#include +#include "PuyoIA.h" + +#define UP 2 +#define LEFT 1 +#define DOWN 0 +#define RIGHT 3 + + +struct PosEvaluator { + int c_direction; + int f_x; + int score; + bool keep; + + int update(PuyoGame *game, PuyoState f_color, PuyoState c_color, + int IAPuyoValue, + int IASecondLevelPuyoValue, + int IAConstructorLevel, + int IAHeightAgressivity, + int IAHeightFactor, int myRand) + { + int f_y = 0; + int c_x = 0; + int c_y = 0; + + if (f_color == PUYO_EMPTY) return 0; + if (c_color == PUYO_EMPTY) return 0; + + /* Calcul de la posisition finale des deux puyos */ + switch (c_direction) { + case UP: + f_y = game->getColumnHeigth(f_x); + c_x = f_x; + c_y = f_y + 1; + break; + case DOWN: + c_y = game->getColumnHeigth(f_x); + c_x = f_x; + f_y = c_y + 1; + break; + case RIGHT: + f_y = game->getColumnHeigth(f_x); + c_x = f_x + 1; + c_y = game->getColumnHeigth(f_x + 1); + break; + case LEFT: + f_y = game->getColumnHeigth(f_x); + c_x = f_x - 1; + c_y = game->getColumnHeigth(f_x - 1); + break; + } + f_y = PUYODIMY - f_y - 1; + c_y = PUYODIMY - c_y - 1; + + /* Si l'un des deux puyos est tout en haut => pire cas ! */ + if ((f_y < 0) || (c_y < 0)) + { + score = -10000; + return score; + } + + /* On favorise les cas ou on regroupe les couleurs ! */ + score = (game->getSamePuyoAround(f_x, f_y, f_color) + game->getSamePuyoAround(c_x, c_y, c_color) )*IAPuyoValue; + + /* On favorise les cas ou on ne separe pas deux puyos de couleurs identiques qui tombent ! */ + if ( (f_color == c_color) && ( (c_x==f_x) || (c_y==f_y) ) ) score += IAPuyoValue; + + /* Si l'un des deux puyos est vraiment tres haut => vraiment pas bien ! */ + if ((f_y < 2) || (c_y < 2)) score -= 1000; + + /* Si les deux puyos sont suffisament bas => mode attaque ! */ + if ((f_y >= IAHeightAgressivity) && (c_y >= IAHeightAgressivity)) + { + /* On va defavoriser les cas ou on fait des trops petites constructions : */ + PuyoState aboveStateF, aboveStateC; + bool F,C; + + /* Main puyo may only make a block of 4 */ + aboveStateF = (c_direction == UP) ? c_color : PUYO_EMPTY; + F = ((game->getSamePuyoAround(f_x, f_y, f_color) + ((aboveStateF == f_color)?1:0)) == 4); + + /* Companion puyo may only make a block of 4 */ + aboveStateC = (c_direction == DOWN) ? f_color : PUYO_EMPTY; + C = ((game->getSamePuyoAround(c_x, c_y, c_color) + ((aboveStateC == c_color)?1:0)) == 4); + + if ((F || C) && !(F && C)) + { + score -= IAConstructorLevel*IAPuyoValue; + /* fprintf(stderr,"--o--"); */ + } + } + + /* On favorise les cas ou les puyos sont envoyes le plus bas possible */ + score += (f_y+c_y)*IAHeightFactor; + + /* On favorise les regroupements verticaux apres demolition : */ + + /* xx2 */ + /* xx3 Favoriser le cas ou on pose un '3' au milieu pour que les '2' se groupent apres destruction du '3' */ + /* 152 */ + + /* xx2 */ + /* x43 Favoriser aussi un peu le cas ou on pose un '5' sur le '4' */ + /* 152 */ + + /* Puyo companion */ + if (c_y < PUYODIMY-1) /* Si on est pas completement en bas */ + { + if ((c_x<(PUYODIMX - 1)) && (c_color == (game->getPuyoAt(c_x+1,c_y))->getPuyoState())) /* Test du pattern a droite */ + { + PuyoState cColorA = (game->getPuyoAt(c_x+1,c_y+1))->getPuyoState(); + if ((c_color!=cColorA) && (cColorA>5) && (cColorA<11) && (cColorA == (game->getPuyoAt(c_x+1,c_y-1))->getPuyoState())) + { + score += game->getSamePuyoAround(c_x+1, c_y, cColorA)*IASecondLevelPuyoValue; + } + } + + if ((c_x>0) && (c_color == (game->getPuyoAt(c_x-1,c_y))->getPuyoState())) /* Test du pattern a gauche */ + { + PuyoState cColorB = (game->getPuyoAt(c_x-1,c_y+1))->getPuyoState(); + if ((c_color!=cColorB) && (cColorB>5) && (cColorB<11) && (cColorB == (game->getPuyoAt(c_x-1,c_y-1))->getPuyoState())) + { + score += game->getSamePuyoAround(c_x-1, c_y, cColorB)*IASecondLevelPuyoValue; + } + } + } + if (c_y < PUYODIMY-2) /* Si on est pas trop en bas */ + { + if (c_color == (game->getPuyoAt(c_x,c_y+2))->getPuyoState()) + { + score += game->getSamePuyoAround(c_x, c_y+2, c_color)*IASecondLevelPuyoValue; + } + } + + /* Puyo principal */ + if (f_y < PUYODIMY-1) /* Si on est pas completement en bas */ + { + if ((f_x<(PUYODIMX - 1)) && (f_color == (game->getPuyoAt(f_x+1,f_y))->getPuyoState())) /* Test du pattern a droite */ + { + PuyoState fColorA = (game->getPuyoAt(f_x+1,f_y+1))->getPuyoState(); + if ((f_color!=fColorA) && (fColorA>5) && (fColorA<11) && (fColorA == (game->getPuyoAt(f_x+1,f_y-1))->getPuyoState())) + { + score += game->getSamePuyoAround(f_x+1, f_y, fColorA)*IASecondLevelPuyoValue; + } + } + + if ((f_x>0) && (f_color == (game->getPuyoAt(f_x-1,f_y))->getPuyoState())) /* Test du pattern a gauche */ + { + PuyoState fColorB = (game->getPuyoAt(f_x-1,f_y+1))->getPuyoState(); + if ((f_color!=fColorB) && (fColorB>5) && (fColorB<11) && (fColorB == (game->getPuyoAt(f_x-1,f_y-1))->getPuyoState())) + { + score += game->getSamePuyoAround(f_x-1, f_y, fColorB)*IASecondLevelPuyoValue; + } + } + } + if (f_y < PUYODIMY-2) /* Si on est pas trop en bas */ + { + if (f_color == (game->getPuyoAt(f_x,f_y+2))->getPuyoState()) + { + score += game->getSamePuyoAround(f_x, f_y+2, f_color)*IASecondLevelPuyoValue; + } + } + + score += myRand; + return score; + } + + void init(int col, int dir) + { + f_x = col; + c_direction = dir; + } + + PosEvaluator() { f_x = c_direction = 0; } + +}; + +PuyoIA::PuyoIA(IA_Type type, int level, PuyoView *targetView) +: PuyoPlayer(targetView), type(type), level(level) +{ + attachedGame = targetView->getAttachedGame(); + evaluator = new PosEvaluator[PUYODIMX * 4 - 2]; + int iEval = 0; + for (int col=0; colrotateLeft(); + break; + case 1: + attachedView->moveLeft(); + break; + case 2: + attachedView->moveRight(); + break; + default: + break; + } + break; + + default: + { + if (!firstLine && (attachedGame->getFallingY() == 1) && (choosenMove != -1)) + choosenMove = -1; + firstLine = (attachedGame->getFallingY() == 1); + if ((choosenMove >= 0) && (random() % level < 10)) + { + if (attachedGame->getFallingCompanionDir() != evaluator[choosenMove].c_direction) + attachedView->rotateLeft(); + else if (attachedGame->getFallingX() < evaluator[choosenMove].f_x) + attachedView->moveRight(); + else if (attachedGame->getFallingX() > evaluator[choosenMove].f_x) + attachedView->moveLeft(); + else + attachedView->cycleGame(); + } + else if (choosenMove == -1) + { + PuyoState f_color = extractColor(attachedGame->getFallingState()); + PuyoState c_color = extractColor(attachedGame->getCompanionState()); + int max = -100000; + for (int i=PUYODIMX*4-3; i>=0; --i) + { + if ((f_color == PUYO_EMPTY)||(c_color==PUYO_EMPTY)) return; + int myRand = 0; + + if (type == FLOBO) { + myRand = (int) ((double)IAPuyoValue*2*(double)random()/(RAND_MAX+1.0)); + } + else if (type == JEKO) { + myRand = (int) ((double)IAPuyoValue*(double)random()/(RAND_MAX+1.0)/2.0); + } + + + /* IAPuyoValue / IASecondLevelPuyoValue / IAConstructorLevel / IAHeightAgressivity / IAHeightFactor */ + int val = evaluator[i].update(attachedGame, f_color, c_color, + IAPuyoValue, IASecondLevelPuyoValue, IAConstructorLevel, IAHeightAgressivity, IAHeightFactor, + myRand); + + if (val >= max) max = val; + } + + int nbKeep = 0; + for (int i=PUYODIMX*4-3; i>=0; --i) + { + if (evaluator[i].score == max) { + nbKeep ++; + evaluator[i].keep = true; + } + else evaluator[i].keep = false; + } + + if (nbKeep > 0) { + int tmp = 1 + ((random() / 7) % nbKeep); + + for (int i=PUYODIMX*4-3; i>=0; --i) + { + if (evaluator[i].keep) + { + if (tmp == 1) + { + /*fprintf(stderr,"\nchoice : %d/%d (%d)\n",i,nbKeep,evaluator[i].score);*/ + choosenMove = i; + break; + } + tmp--; + } + } + } + } + } + } +} diff --git a/PuyoIA.h b/PuyoIA.h new file mode 100644 index 0000000..cd82538 --- /dev/null +++ b/PuyoIA.h @@ -0,0 +1,28 @@ +#ifndef PUYOIA_H +#define PUYOIA_H + +#include "PuyoPlayer.h" + +enum IA_Type { + RANDOM, + FLOBO, + JEKO, + TANIA, + GYOM +}; + +class PuyoIA : public virtual PuyoPlayer { +public: + PuyoIA(IA_Type type, int level, PuyoView *targetView); + virtual void cycle(); + private: + PuyoState extractColor(PuyoState A) const; + struct PosEvaluator *evaluator; + int choosenMove; + bool firstLine; + IA_Type type; + int level; +}; + +#endif // PUYOIA_H + diff --git a/PuyoLock.cpp b/PuyoLock.cpp new file mode 100644 index 0000000..079b4d1 --- /dev/null +++ b/PuyoLock.cpp @@ -0,0 +1,56 @@ +/* FloboPuyo - PuyoLock Class +* Copyright (C) 2004 Guillaume Borios +* iOS Software +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* +*/ + +#include "PuyoLock.h" + +#include "stdio.h" + +PuyoLock::PuyoLock() +{ + lockLevel = 0; +} + +PuyoLock::~PuyoLock() +{ + // Nothing to free +} + +void PuyoLock::lock() +{ + lockLevel++; +} + +void PuyoLock::unlock() +{ + if (lockLevel > 0) lockLevel --; + else + { + fprintf(stderr,"Unlock not done (lock index already at 0)"); + // exit (1); + } +} + +bool PuyoLock::locked() +{ + return (lockLevel > 0); +} + + diff --git a/PuyoLock.h b/PuyoLock.h new file mode 100644 index 0000000..7cb69e2 --- /dev/null +++ b/PuyoLock.h @@ -0,0 +1,37 @@ +/* FloboPuyo - PuyoLock Class + * Copyright (C) 2004 Guillaume Borios + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#ifndef _PUYOLOCK +#define _PUYOLOCK + +class PuyoLock { + public: + PuyoLock(); + ~PuyoLock(); + void lock(); + void unlock(); + bool locked(); + private: + unsigned int lockLevel; +}; + +#endif // _PUYOLOCK + diff --git a/PuyoPlayer.h b/PuyoPlayer.h new file mode 100644 index 0000000..e365931 --- /dev/null +++ b/PuyoPlayer.h @@ -0,0 +1,22 @@ +#ifndef PUYOPLAYER_H +#define PUYOPLAYER_H + +#include "PuyoView.h" +#include "PuyoGame.h" + +class PuyoPlayer { + +protected: + PuyoView *attachedView; + PuyoGame *attachedGame; + +public: + PuyoPlayer(PuyoView *targetView) : attachedView(targetView) + { + attachedGame = targetView->getAttachedGame(); + } + virtual void cycle() = 0; + +}; + +#endif diff --git a/PuyoStarter.cpp b/PuyoStarter.cpp new file mode 100644 index 0000000..d51eb61 --- /dev/null +++ b/PuyoStarter.cpp @@ -0,0 +1,784 @@ +#include "PuyoStarter.h" +#include "PuyoView.h" + +#include "SDL_Painter.h" +#include "IosImgProcess.h" + +extern SDL_Painter painter; + +IIM_Surface *grid; +IIM_Surface *perso[2]; +int currentPerso; +IIM_Surface *live[4]; +IIM_Surface *speedImg; +IIM_Surface *speedBlackImg; +IIM_Surface *gameScreen; +IIM_Surface *shrinkingPuyo[5][5]; +IIM_Surface *explodingPuyo[5][5]; +extern IIM_Surface *bigNeutral; +IIM_Surface *puyoCircle[32]; +extern IIM_Surface *puyoShadow; +IIM_Surface *puyoEye[3]; +extern IIM_Surface *puyoEyes; +IIM_Surface *puyoEyesSwirl[4]; +extern IIM_Surface *puyoFaces[5][16]; + +int gameLevel; +int GAME_ACCEL = 1250; +static const int NB_PERSO_STATE = 2; + +const char *p1name; +const char *p2name; + +extern Menu *menu_pause; +static char *BACKGROUND[NB_MUSIC_THEME] = { "Background.jpg", "BackgroundDark.jpg" }; +extern IIM_Surface *background, *fallingBlue, *fallingRed, *fallingGreen, *fallingViolet, *fallingYellow, *neutral; + +static void loadShrinkXplode2(int i, float dec) +{ + for (int j=1;j<=4;++j) + { + shrinkingPuyo[j-1][i] = iim_surface_shift_hue(shrinkingPuyo[j-1][3],dec); + explodingPuyo[j-1][i] = iim_surface_shift_hue(explodingPuyo[j-1][3],dec); + } +} + +static void loadShrinkXplode(void) +{ + for (int j=1;j<=4;++j) + { + char f[20]; + sprintf(f,"Shrink%d.png", j); + shrinkingPuyo[j-1][3] = IIM_Load_DisplayFormatAlpha(f); + sprintf(f,"Explode%d.png", j); + explodingPuyo[j-1][3] = IIM_Load_DisplayFormatAlpha(f); + } + + loadShrinkXplode2(0,-65.0f); + loadShrinkXplode2(1,100.0f); + loadShrinkXplode2(2,-150.0f); + loadShrinkXplode2(4,140.0f); +} + + +void PuyoStarter::stopRender() +{ + this->stopRendering = true; + iim_surface_convert_to_gray(painter.gameScreen); +} + + +void PuyoStarter::restartRender() +{ + this->stopRendering = false; + painter.redrawAll(); +} + +void PuyoStarter::draw() +{ + if (stopRendering) { + SDL_BlitSurface(painter.gameScreen->surf,NULL,display,NULL); + } + else { + SDL_Rect drect; + + areaA->render(); + areaB->render(); + + drect.x = 21; + drect.y = -1; + drect.w = grid->w; + drect.h = grid->h; + painter.requestDraw(grid, &drect); + drect.x = 407; + drect.y = -1; + drect.w = grid->w; + drect.h = grid->h; + painter.requestDraw(grid, &drect); + + areaA->renderNeutral(); + areaB->renderNeutral(); + if ((randomPlayer)&&(currentPerso>=0)) + { + drect.x = 320 - perso[currentPerso]->w/2; + drect.y = 280 - perso[currentPerso]->h/2; + drect.w = perso[currentPerso]->w; + drect.h = perso[currentPerso]->h; + painter.requestDraw(perso[currentPerso], &drect); + } + + if ((randomPlayer)&&(lives>=0)&&(lives<=3)) + { + drect.x = painter.gameScreen->w / 2 - live[lives]->w / 2; + drect.y = 436; + drect.w = live[lives]->w; + drect.h = live[lives]->h; + painter.requestDraw(live[lives], &drect); + } + + painter.draw(painter.gameScreen->surf); + } + char text[256]; + if (!randomPlayer) + { + sprintf(text, "Win %d", score1); + SoFont_CenteredString_XY (commander->smallFont, display, + 50, 460, text, NULL); + sprintf(text, "Win %d", score2); + SoFont_CenteredString_XY (commander->smallFont, display, + 590, 460, text, NULL); + } + + SoFont *font = (stopRendering?commander->darkFont:commander->menuFont); + SoFont_CenteredString_XY (font, display, 510, 460, p1name, NULL); + SoFont_CenteredString_XY (font, display, 130, 460, p2name, NULL); + + int gameSpeedCpy = gameSpeed; + if (gameSpeed == 1) gameSpeedCpy = 0; + + SDL_Rect speedRect; + speedRect.x = 0; + speedRect.w = speedImg->w; + speedRect.h = (20 - gameSpeedCpy) * 6; + speedRect.y = speedImg->h - speedRect.h; + + SDL_Rect drect; + drect.x = 320 - speedRect.w / 2; + drect.y = 170 - speedRect.h; + drect.w = speedRect.w; + drect.h = speedRect.h; + + SDL_Rect speedBlackRect = speedRect; + SDL_Rect drectBlack = drect; + + speedBlackRect.h = speedImg->h - speedRect.h; + speedBlackRect.y = 0; + drectBlack.y = 50; + drectBlack.h = speedBlackRect.h; + + SDL_BlitSurface(speedBlackImg->surf,&speedBlackRect,display,&drectBlack); + if (stopRendering) + SDL_BlitSurface(speedBlackImg->surf,&speedRect,display,&drect); + else + SDL_BlitSurface(speedImg->surf,&speedRect,display,&drect); + + SoFont *fontBl = NULL; + if ((blinkingPointsA % 2) == 0) + fontBl = commander->smallFont; + else + fontBl = commander->menuFont; + + sprintf(text, "<< %d", attachedGameA->getPoints()); + SoFont_CenteredString_XY (fontBl, display, + 300, 380, text, NULL); + + if ((blinkingPointsB % 2) == 0) + fontBl = commander->smallFont; + else + fontBl = commander->menuFont; + + sprintf(text, "%d >>", attachedGameB->getPoints()); + SoFont_CenteredString_XY (fontBl, display, + 340, 395, text, NULL); +} + + +PuyoStarter::PuyoStarter(PuyoCommander *commander, bool aiLeft, int aiLevel, IA_Type aiType, int theme) +{ + this->stopRendering = false; + this->paused = false; + tickCounts = 0; + this->commander = commander; + + blinkingPointsA = 0; + blinkingPointsB = 0; + savePointsA = 0; + savePointsB = 0; + + background = IIM_Load_DisplayFormat(BACKGROUND[theme]); + + painter.backGround = background; + if (painter.gameScreen == NULL) + { + SDL_PixelFormat *fmt = background->surf->format; + SDL_Surface *tmp = SDL_CreateRGBSurface(background->surf->flags, + background->w, background->h, 32, + fmt->Rmask, fmt->Gmask, + fmt->Bmask, fmt->Amask); + gameScreen = painter.gameScreen = IIM_RegisterImg(SDL_DisplayFormat(tmp), false); + SDL_FreeSurface(tmp); + } + painter.redrawAll(painter.gameScreen->surf); + + static bool firstTime = true; + if (firstTime) { + fallingViolet = IIM_Load_DisplayFormatAlpha("v0.png"); + fallingRed = iim_surface_shift_hue(fallingViolet, 100.0f); + fallingBlue = iim_surface_shift_hue(fallingViolet, -65.0f); + fallingGreen = iim_surface_shift_hue(fallingViolet, -150.0f); + fallingYellow = iim_surface_shift_hue(fallingViolet, 140.0f); + neutral = IIM_Load_DisplayFormatAlpha("Neutral.png"); + bigNeutral = IIM_Load_DisplayFormatAlpha("BigNeutral.png"); + speedImg = IIM_Load_DisplayFormatAlpha("speed.png"); + speedBlackImg = IIM_Load_DisplayFormatAlpha("speed_black.png"); + + IIM_Surface * tmpsurf = IIM_Load_DisplayFormatAlpha("circle.png"); + for (int i = 0 ; i < 32 ; i++) + puyoCircle[i] = iim_surface_set_value(tmpsurf,sin(3.14f/2.0f+i*3.14f/64.0f)*0.6f+0.2f); + IIM_Free(tmpsurf); + loadShrinkXplode(); + + puyoShadow = IIM_Load_DisplayFormatAlpha("Shadow.png"); + puyoEye[0] = IIM_Load_DisplayFormatAlpha("eye0.png"); + puyoEye[1] = IIM_Load_DisplayFormatAlpha("eye1.png"); + puyoEye[2] = IIM_Load_DisplayFormatAlpha("eye2.png"); + puyoEyes = puyoEye[0]; + puyoEyesSwirl[0] = IIM_Load_DisplayFormatAlpha("twirleye0.png"); + puyoEyesSwirl[1] = IIM_Load_DisplayFormatAlpha("twirleye1.png"); + puyoEyesSwirl[2] = IIM_Load_DisplayFormatAlpha("twirleye2.png"); + puyoEyesSwirl[3] = IIM_Load_DisplayFormatAlpha("twirleye3.png"); + + puyoFaces[0][0] = IIM_Load_DisplayFormatAlpha("v0.png"); + puyoFaces[0][1] = IIM_Load_DisplayFormatAlpha("v1a.png"); + puyoFaces[0][2] = IIM_Load_DisplayFormatAlpha("v1b.png"); + puyoFaces[0][3] = IIM_Load_DisplayFormatAlpha("v1c.png"); + puyoFaces[0][4] = IIM_Load_DisplayFormatAlpha("v1d.png"); + puyoFaces[0][5] = IIM_Load_DisplayFormatAlpha("v2ab.png"); + puyoFaces[0][6] = IIM_Load_DisplayFormatAlpha("v2ac.png"); + puyoFaces[0][7] = IIM_Load_DisplayFormatAlpha("v2ad.png"); + puyoFaces[0][8] = IIM_Load_DisplayFormatAlpha("v2bc.png"); + puyoFaces[0][9] = IIM_Load_DisplayFormatAlpha("v2bd.png"); + puyoFaces[0][10] = IIM_Load_DisplayFormatAlpha("v2cd.png"); + puyoFaces[0][11] = IIM_Load_DisplayFormatAlpha("v3abc.png"); + puyoFaces[0][12] = IIM_Load_DisplayFormatAlpha("v3abd.png"); + puyoFaces[0][13] = IIM_Load_DisplayFormatAlpha("v3acd.png"); + puyoFaces[0][14] = IIM_Load_DisplayFormatAlpha("v3bcd.png"); + puyoFaces[0][15] = IIM_Load_DisplayFormatAlpha("v4abcd.png"); + + live[0] = IIM_Load_DisplayFormatAlpha("0live.png"); + live[1] = IIM_Load_DisplayFormatAlpha("1live.png"); + live[2] = IIM_Load_DisplayFormatAlpha("2live.png"); + live[3] = IIM_Load_DisplayFormatAlpha("3live.png"); + + for (int i = 0 ; i < 16 ; i++) { + puyoFaces[1][i] = iim_surface_shift_hue(puyoFaces[0][i], 100); + } + for (int i = 0 ; i < 16 ; i++) { + puyoFaces[2][i] = iim_surface_shift_hue(puyoFaces[0][i], -65); + } + for (int i = 0 ; i < 16 ; i++) { + puyoFaces[3][i] = iim_surface_shift_hue(puyoFaces[0][i], -150); + } + for (int i = 0 ; i < 16 ; i++) { + puyoFaces[4][i] = iim_surface_shift_hue(puyoFaces[0][i], 140); + } + + grid = IIM_Load_DisplayFormatAlpha("grid.png"); + firstTime = false; + } + + if (fallingBlue == NULL) + { + fprintf(stderr, "IMG_Load error:%s\n", SDL_GetError()); + exit(-1); + } + + areaA = new PuyoView(&attachedRandom, 1 + CSIZE, BSIZE-TSIZE, CSIZE + PUYODIMX*TSIZE + FSIZE, BSIZE+ESIZE); + areaB = new PuyoView(&attachedRandom, 1 + CSIZE + PUYODIMX*TSIZE + DSIZE, BSIZE-TSIZE, CSIZE + PUYODIMX*TSIZE + DSIZE - FSIZE - TSIZE, BSIZE+ESIZE); + + attachedGameA = areaA->getAttachedGame(); + attachedGameB = areaB->getAttachedGame(); + + if (aiLeft) { + randomPlayer = new PuyoIA(aiType, aiLevel, areaA); + perso[0] = IIM_Load_DisplayFormatAlpha("perso1_1.png"); + perso[1] = IIM_Load_DisplayFormatAlpha("perso1_2.png"); + } + else { + randomPlayer = 0; + perso[0] = NULL; + } + + areaA->setEnemyGame(attachedGameB); + areaB->setEnemyGame(attachedGameA); +} + +PuyoStarter::~PuyoStarter() +{ + delete areaA; + delete areaB; + delete attachedGameA; + delete attachedGameB; + // SDL_FreeSurface(fallingBlue); +} + + + + +#define FPKEY_REPEAT 7 +#define FPKEY_DELAY 5 + +#define FPKEY_keyNumber 10 + +#define FPKEY_P1_Down 0 +#define FPKEY_P1_Left 1 +#define FPKEY_P1_Right 2 +#define FPKEY_P1_TurnLeft 3 +#define FPKEY_P1_TurnRight 4 +#define FPKEY_P2_Down 5 +#define FPKEY_P2_Left 6 +#define FPKEY_P2_Right 7 +#define FPKEY_P2_TurnLeft 8 +#define FPKEY_P2_TurnRight 9 + +#define repeatCondition(A) keysDown[A]++; if (((keysDown[A]-FPKEY_DELAY)>0) && ((keysDown[A]-FPKEY_DELAY)%FPKEY_REPEAT == 0)) + + +void PuyoStarter::run(int _score1, int _score2, int lives, int point1, int point2) +{ + this->lives = lives; + this->score1 = _score1; + this->score2 = _score2; + SDL_Rect drect; + SDL_Event event; + int quit = 0; + SDL_EnableUNICODE(1); + int keysDown[FPKEY_keyNumber] = {0,0,0,0,0,0,0,0,0,0}; + + gameSpeed = 20; + attachedGameB->points = point1; + attachedGameA->points = point2; + + if (!randomPlayer) { + int sc1=score1,sc2=score2; + while ((sc1>0)&&(sc2>0)) { sc1--; sc2--; } + attachedGameA->increaseNeutralPuyos(sc1 * PUYODIMX); + attachedGameB->increaseNeutralPuyos(sc2 * PUYODIMX); + attachedGameA->dropNeutrals(); + attachedGameB->dropNeutrals(); + attachedGameA->cycle(); + attachedGameB->cycle(); + } + + while (!quit) { + bool left_danger = (attachedGameA->getMaxColumnHeight() > PUYODIMY - 5); + bool right_danger = (attachedGameB->getMaxColumnHeight() > PUYODIMY - 5); + bool danger = left_danger || right_danger; + bool gameover = (!attachedGameA->isGameRunning() || !attachedGameB->isGameRunning()); + + /*if (paused) + audio_music_start(0); + else */if (gameover) + audio_music_start(3); + else if (danger) + audio_music_start(2); + else + audio_music_start(1); + + if (left_danger) + currentPerso = 0; + else + currentPerso = 1; + + while (SDL_PollEvent(&event) == 1) + { + if (attachedGameA->isGameRunning() && + attachedGameB->isGameRunning()) { + if (!paused) { + + /* Check for usual events */ + GameControlEvent controlEvent; + getControlEvent(event, &controlEvent); + + if (controlEvent.isUp) + { + switch (controlEvent.gameEvent) { + case GameControlEvent::kPlayer1Down: + if (randomPlayer == 0) keysDown[FPKEY_P1_Down] = 0; + break; + case GameControlEvent::kPlayer1Left: + if (randomPlayer == 0) keysDown[FPKEY_P1_Left] = 0; + break; + case GameControlEvent::kPlayer1Right: + if (randomPlayer == 0) keysDown[FPKEY_P1_Right] = 0; + break; + case GameControlEvent::kPlayer1TurnLeft: + if (randomPlayer == 0) keysDown[FPKEY_P1_TurnLeft] = 0; + break; + case GameControlEvent::kPlayer1TurnRight: + if (randomPlayer == 0) keysDown[FPKEY_P1_TurnRight] = 0; + break; + + case GameControlEvent::kPlayer2Down: + keysDown[FPKEY_P2_Down] = 0; + break; + case GameControlEvent::kPlayer2Left: + keysDown[FPKEY_P2_Left] = 0; + break; + case GameControlEvent::kPlayer2Right: + keysDown[FPKEY_P2_Right] = 0; + break; + case GameControlEvent::kPlayer2TurnLeft: + keysDown[FPKEY_P2_TurnLeft] = 0; + break; + case GameControlEvent::kPlayer2TurnRight: + keysDown[FPKEY_P2_TurnRight] = 0; + break; + } + } + else { + switch (controlEvent.gameEvent) { + case GameControlEvent::kPlayer1Left: + if (randomPlayer == 0) { + areaA->moveLeft(); + if (attachedGameA->isEndOfCycle()) keysDown[FPKEY_P1_Left] = 0; + else keysDown[FPKEY_P1_Left]++; + } + break; + case GameControlEvent::kPlayer1Right: + if (randomPlayer == 0) { + areaA->moveRight(); + if (attachedGameA->isEndOfCycle()) keysDown[FPKEY_P1_Right] = 0; + else keysDown[FPKEY_P1_Right]++; + } + break; + case GameControlEvent::kPlayer1TurnLeft: + if (randomPlayer == 0) { + areaA->rotateLeft(); + if (attachedGameA->isEndOfCycle()) keysDown[FPKEY_P1_TurnLeft] = 0; + else keysDown[FPKEY_P1_TurnLeft]++; + } + break; + case GameControlEvent::kPlayer1TurnRight: + if (randomPlayer == 0) { + areaA->rotateRight(); + if (attachedGameA->isEndOfCycle()) keysDown[FPKEY_P1_TurnRight] = 0; + else keysDown[FPKEY_P1_TurnRight]++; + } + break; + case GameControlEvent::kPlayer1Down: + if (randomPlayer == 0) { + //attachedGameA->cycle(); desact flobo + if (attachedGameA->isEndOfCycle()) keysDown[FPKEY_P1_Down] = 0; + else keysDown[FPKEY_P1_Down]++; + } + break; + + case GameControlEvent::kPlayer2Left: + areaB->moveLeft(); + if (attachedGameB->isEndOfCycle()) keysDown[FPKEY_P2_Left] = 0; + else keysDown[FPKEY_P2_Left]++; + break; + case GameControlEvent::kPlayer2Right: + areaB->moveRight(); + if (attachedGameB->isEndOfCycle()) keysDown[FPKEY_P2_Right] = 0; + else keysDown[FPKEY_P2_Right]++; + break; + case GameControlEvent::kPlayer2TurnLeft: + areaB->rotateLeft(); + if (attachedGameB->isEndOfCycle()) keysDown[FPKEY_P2_TurnLeft] = 0; + else keysDown[FPKEY_P2_TurnLeft]++; + break; + case GameControlEvent::kPlayer2TurnRight: + areaB->rotateRight(); + if (attachedGameB->isEndOfCycle()) keysDown[FPKEY_P2_TurnRight] = 0; + else keysDown[FPKEY_P2_TurnRight]++; + break; + case GameControlEvent::kPlayer2Down: + //attachedGameB->cycle(); desact flobo + if (attachedGameB->isEndOfCycle()) keysDown[FPKEY_P2_Down] = 0; + else keysDown[FPKEY_P2_Down]++; + break; + default: + break; + } + } + + switch (event.type) { + case SDL_USEREVENT: + if (randomPlayer) + randomPlayer->cycle(); + if (event.user.code == 1) { + drect.x = 0; + drect.y = 0; + drect.w = 640; + drect.h = 480; + + if (attachedGameA->isEndOfCycle()) { + keysDown[FPKEY_P1_Down] = 0; + keysDown[FPKEY_P1_TurnLeft] = 0; + keysDown[FPKEY_P1_TurnRight] = 0; + } + + areaA->cycleGame(); // a voir + + if (attachedGameB->isEndOfCycle()) { + keysDown[FPKEY_P2_Down] = 0; + keysDown[FPKEY_P2_TurnLeft] = 0; + keysDown[FPKEY_P2_TurnRight] = 0; + } + areaB->cycleGame(); // a voir + + switch (gameLevel) + { + case 1: + attachedGameB->points += 1; + attachedGameA->points += 1; + break; + case 2: + attachedGameB->points += 5; + attachedGameA->points += 5; + break; + case 3: + attachedGameB->points += 10; + attachedGameA->points += 10; + break; + } + + if (attachedGameA->getPoints()/50000 > savePointsA/50000) + blinkingPointsA = 10; + if (attachedGameB->getPoints()/50000 > savePointsB/50000) + blinkingPointsB = 10; + + if (blinkingPointsA > 0) + blinkingPointsA--; + if (blinkingPointsB > 0) + blinkingPointsB--; + + savePointsB = attachedGameB->getPoints(); + savePointsA = attachedGameA->getPoints(); + + if (savePointsA < 50000) blinkingPointsA=0; + if (savePointsB < 50000) blinkingPointsB=0; + + } else { + if (keysDown[FPKEY_P2_Down]) { + if (attachedGameB->isEndOfCycle()) + keysDown[FPKEY_P2_Down] = 0; + else + areaB->cycleGame(); // a voir + } + if (keysDown[FPKEY_P2_Left]) { + repeatCondition(FPKEY_P2_Left) areaB->moveLeft(); + } + if (keysDown[FPKEY_P2_Right]) { + repeatCondition(FPKEY_P2_Right) areaB->moveRight(); + } + if (keysDown[FPKEY_P2_TurnLeft]) { + repeatCondition(FPKEY_P2_TurnLeft) areaB->rotateLeft(); + if (attachedGameB->isEndOfCycle()) + keysDown[FPKEY_P2_TurnLeft] = 0; + } + if (keysDown[FPKEY_P2_TurnRight]) { + repeatCondition(FPKEY_P2_TurnRight) areaB->rotateRight(); + if (attachedGameB->isEndOfCycle()) + keysDown[FPKEY_P2_TurnRight] = 0; + } + + if (keysDown[FPKEY_P1_Down]) { + if (attachedGameA->isEndOfCycle()) + keysDown[FPKEY_P1_Down] = 0; + else + areaA->cycleGame(); // a voir + } + if (keysDown[FPKEY_P1_Left]) { + repeatCondition(FPKEY_P1_Left) areaA->moveLeft(); + if (attachedGameA->isEndOfCycle()) + keysDown[FPKEY_P1_Left] = 0; + } + if (keysDown[FPKEY_P1_Right]) { + repeatCondition(FPKEY_P1_Right) areaA->moveRight(); + if (attachedGameA->isEndOfCycle()) + keysDown[FPKEY_P1_Right] = 0; + } + if (keysDown[FPKEY_P1_TurnLeft]) { + repeatCondition(FPKEY_P1_TurnLeft) areaA->rotateLeft(); + if (attachedGameA->isEndOfCycle()) + keysDown[FPKEY_P1_TurnLeft] = 0; + } + if (keysDown[FPKEY_P1_TurnRight]) { + repeatCondition(FPKEY_P1_TurnRight) areaA->rotateRight(); + if (attachedGameA->isEndOfCycle()) + keysDown[FPKEY_P1_TurnRight] = 0; + } + } + break; + case SDL_KEYDOWN: + /* check for cheat-codes */ + static int cheatcode = 0; + if (event.key.keysym.sym == SDLK_k) cheatcode = 0; + if (event.key.keysym.sym == SDLK_i) cheatcode += 1; + if (event.key.keysym.sym == SDLK_e) cheatcode += 10; + if (event.key.keysym.sym == SDLK_f) cheatcode += 100; + if (event.key.keysym.sym == SDLK_t) cheatcode += 1000; + if (event.key.keysym.sym == SDLK_l) cheatcode += 10000; + if (cheatcode == 31111) { + attachedGameA->increaseNeutralPuyos(PUYODIMX * 12); + attachedGameA->dropNeutrals(); + attachedGameA->increaseNeutralPuyos(PUYODIMX * 12); + } + default: + break; + } + } // !Paused + else { + GameControlEvent controlEvent; + getControlEvent(event, &controlEvent); + switch (controlEvent.cursorEvent) { + case GameControlEvent::kUp: + menu_prev_item(menu_pause); + break; + case GameControlEvent::kDown: + menu_next_item(menu_pause); + break; + case GameControlEvent::kStart: + if (menu_active_is(menu_pause, kContinueGame)) { + paused = false; + restartRender(); + menu_hide(menu_pause); + } + if (menu_active_is(menu_pause, kAbortGame)) { + if (menu_active_is(commander->gameOverMenu, "YES")) + menu_next_item(commander->gameOverMenu); + quit = 1; + menu_hide(menu_pause); + } + if (menu_active_is(menu_pause, kOptions)) { + menu_hide (menu_pause); + commander->optionMenuLoop(this); + menu_show (menu_pause); + } + break; + } + } + } else // Not GameIsRunning + { + if (randomPlayer) { + if (rightPlayerWin()) { + if ((_score2 == 7) && (rightPlayerWin())) + commander->gameOverMenu = commander->finishedMenu; + else + commander->gameOverMenu = commander->nextLevelMenu; + } + else { + if (lives == 0) { + commander->gameOverMenu = commander->gameOver1PMenu; + } + else { + commander->gameOverMenu = commander->looserMenu; + } + } + } + else { + commander->gameOverMenu = commander->gameOver2PMenu; + } + + if (!menu_visible(commander->gameOverMenu)) { + + if (leftPlayerWin()) score1 = _score1 + 1; + else if (rightPlayerWin()) score2 = _score2 + 1; + + if (commander->gameOverMenu == commander->gameOver2PMenu) { + char winner[256]; + char score[256]; + sprintf(winner,"%d Wins!!!",(leftPlayerWin()?1:2)); + sprintf(score, "%d - %d", score1, score2); + menu_set_value(commander->gameOverMenu, kPlayer, winner); + menu_set_value(commander->gameOverMenu, kScore, score); + } + else if (commander->gameOverMenu == commander->nextLevelMenu) { + char level[256]; + extern char *AI_NAMES[]; + sprintf(level, "Stage %d... Vs %s", score2+1, AI_NAMES[score2]); + menu_set_value(commander->gameOverMenu, kNextLevel, level); + } + else if (commander->gameOverMenu == commander->looserMenu) { + char level[256]; + char cont[256]; + sprintf(level, "Stage %d... Vs %s", score2+1, p2name); + sprintf(cont, "%d Left", lives); + menu_set_value(commander->gameOverMenu, kCurrentLevel, level); + menu_set_value(commander->gameOverMenu, kContinueLeft, cont); + } + else if (commander->gameOverMenu == commander->gameOver1PMenu) { + char level[256]; + sprintf(level, "Stage %d... Vs %s", score2+1, p2name); + menu_set_value(commander->gameOverMenu, kYouGotToLevel, level); + } + commander->showGameOver(); + stopRender(); + } // GameOver Visible + } + GameControlEvent controlEvent2; + getControlEvent(event, &controlEvent2); + switch (controlEvent2.cursorEvent) { + case GameControlEvent::kStart: + if (gameover) + { + if (menu_active_is(commander->gameOverMenu, "NO")) + menu_next_item(commander->gameOverMenu); + quit = 1; + } + break; + case GameControlEvent::kBack: + if (!gameover) { + if (!paused) { + menu_show(menu_pause); + paused = true; + stopRender(); + } + else { + paused = false; + restartRender(); + menu_hide(menu_pause); + } + } + else { + if (menu_active_is(commander->gameOverMenu, "NO")) + menu_next_item(commander->gameOverMenu); + quit = 1; + } + break; + default: + break; + } + if(event.type == SDL_QUIT) { + if (menu_active_is(commander->gameOverMenu, "YES")) + menu_next_item(commander->gameOverMenu); + quit = 1; + exit(0); + } + } + commander->updateAll(this); + + if (!paused) { + areaA->cycleAnimation(); + areaB->cycleAnimation(); + + tickCounts++; + event.user.type = SDL_USEREVENT; + event.user.code = 0; + SDL_PushEvent(&event); + // Vitesse du jeu + if (tickCounts % (gameSpeed + 5 * (3 - gameLevel)) == 0) + { + event.user.type = SDL_USEREVENT; + event.user.code = 1; + SDL_PushEvent(&event); + if (!paused) { + if ((tickCounts > GAME_ACCEL) && (gameSpeed > 1)) { + tickCounts = 0; + gameSpeed--; + } + } + } + } + + } + commander->hideGameOver(); + if (randomPlayer) { + for (int i=0; iisGameRunning(); } + bool rightPlayerWin() const { return attachedGameB->isGameRunning(); } + + int leftPlayerPoints() const { return attachedGameA->getPoints(); } + int rightPlayerPoints() const { return attachedGameB->getPoints(); } + +private: + PuyoCommander *commander; + PuyoView *areaA, *areaB; + PuyoGame *attachedGameA, *attachedGameB; + PuyoIA *randomPlayer; + PuyoRandomSystem attachedRandom; + int tickCounts; + int lives; + int score1; + int score2; + bool stopRendering; + bool paused; + int gameSpeed; + + void stopRender(); + void restartRender(); + + int blinkingPointsA, blinkingPointsB, savePointsA, savePointsB; +}; + +#endif // _PUYOSTARTER + diff --git a/PuyoStory.cpp b/PuyoStory.cpp new file mode 100644 index 0000000..983c192 --- /dev/null +++ b/PuyoStory.cpp @@ -0,0 +1,39 @@ +#include "PuyoStory.h" +#include "PuyoCommander.h" + +#ifndef DATADIR +extern char *DATADIR; +#endif + +PuyoStory *theStory; + +int NB_STORIES = 6; + +extern void launch_scenar(const char *f); +extern void draw_scenar(); + +PuyoStory::PuyoStory(PuyoCommander *com, int num) : num(num), commander(com) +{} + +PuyoStory::~PuyoStory() +{} + +void PuyoStory::loop() +{ + theStory = this; + if (num==0) { + char temp[1024]; + sprintf(temp, "%s/story/intro.txt", DATADIR); + launch_scenar(temp); + } + else { + char stories[1024]; + sprintf(stories, "%s/story%d.txt", DATADIR, num); + launch_scenar(stories); + } +} + +void PuyoStory::draw() +{ + draw_scenar(); +} diff --git a/PuyoStory.h b/PuyoStory.h new file mode 100644 index 0000000..57a5a06 --- /dev/null +++ b/PuyoStory.h @@ -0,0 +1,20 @@ +#ifndef PUYO_STORY_H +#define PUYO_STORY_H + +#include "PuyoCommander.h" +extern int NB_STORIES; + +class PuyoStory : public PuyoDrawable { + public: + PuyoStory(PuyoCommander *com, int num); + virtual ~PuyoStory(); + void loop(); + void draw(); + + PuyoCommander *commander; + + private: + int num; +}; + +#endif diff --git a/PuyoVersion.c b/PuyoVersion.c new file mode 100644 index 0000000..b2c0fde --- /dev/null +++ b/PuyoVersion.c @@ -0,0 +1,28 @@ +/* FloboPuyo + * Copyright (C) 2004 + * Florent Boudet , + * Jean-Christophe Hoelt , + * Guillaume Borios + * + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#define FLOBOPUYOVERSION "0.20" + +char *kVersion = "Version " FLOBOPUYOVERSION; diff --git a/PuyoVersion.h b/PuyoVersion.h new file mode 100644 index 0000000..c4f9820 --- /dev/null +++ b/PuyoVersion.h @@ -0,0 +1 @@ +extern char *kVersion; diff --git a/PuyoView.cpp b/PuyoView.cpp new file mode 100644 index 0000000..012ba2c --- /dev/null +++ b/PuyoView.cpp @@ -0,0 +1,427 @@ +/* FloboPuyo + * Copyright (C) 2004 + * Florent Boudet , + * Jean-Christophe Hoelt , + * Guillaume Borios + * + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#include +#include +#include +#include "PuyoView.h" +#include "PuyoAnimations.h" +#include "AnimatedPuyo.h" +#include "PuyoGame.h" +#include "audio.h" +#include "IosImgProcess.h" +#include "HiScores.h" +#include "PuyoDoomMelt.h" + +#include "SDL_Painter.h" +SDL_Painter painter; + +SDL_Surface *display; +IIM_Surface *background, *fallingBlue, *fallingRed, *fallingGreen, *fallingViolet, *fallingYellow, *neutral; +IIM_Surface *bigNeutral; +IIM_Surface *puyoEyes; +IIM_Surface *puyoFaces[5][16]; +IIM_Surface *puyoShadow; + + + +static char PuyoGroupImageIndex[2][2][2][2] = +{ { // empty bottom +{ // empty right +{ // empty top + 0, // empty left + 4, // full left +}, +{ // full top + 3, // empty left + 10, // full left +} +}, +{ // full right + { // empty top + 2, // empty left + 9, // full left + }, + { // full top + 8, // empty left + 14, // full left + } +} +}, +{ // full bottom + { // empty right + { // empty top + 1, // empty left + 7, // full left + }, + { // full top + 6, // empty left + 13, // full left + } + }, + { // full right + { // empty top + 5, // empty left + 12, // full left + }, + { // full top + 11, // empty left + 15, // full left + } + } +} +}; + + + +PuyoView::PuyoView(PuyoRandomSystem *attachedRandom, int xOffset, int yOffset, int nXOffset, int nYOffset) +:attachedPuyoFactory(this), attachedPainter(painter) +{ + attachedGame = new PuyoGame(attachedRandom, &attachedPuyoFactory); + attachedGame->setDelegate(this); + + this->xOffset = xOffset; + this->yOffset = yOffset - TSIZE; + this->nXOffset = nXOffset; + this->nYOffset = nYOffset; + gameRunning = true; + enemyGame = NULL; + skippedCycle = false; + cycleAllowance = 0; +} + +void PuyoView::setEnemyGame(PuyoGame *enemyGame) +{ + this->enemyGame = enemyGame; +} + +IIM_Surface *PuyoView::getSurfaceForState(PuyoState state) +{ + switch (state) { + case PUYO_BLUE: + return fallingBlue; + break; + case PUYO_RED: + return fallingRed; + break; + case PUYO_GREEN: + return fallingGreen; + break; + case PUYO_VIOLET: + return fallingViolet; + break; + case PUYO_YELLOW: + return fallingYellow; + break; + case PUYO_FALLINGBLUE: + return fallingBlue; + break; + case PUYO_FALLINGRED: + return fallingRed; + break; + case PUYO_FALLINGGREEN: + return fallingGreen; + break; + case PUYO_FALLINGVIOLET: + return fallingViolet; + break; + case PUYO_FALLINGYELLOW: + return fallingYellow; + break; + case PUYO_NEUTRAL: + return neutral; + break; + default: + return NULL; + break; + } +} + +IIM_Surface *PuyoView::getSurfaceForPuyo(PuyoPuyo *puyo) +{ + int i = puyo->getPuyoX(); + int j = puyo->getPuyoY(); + PuyoState currentPuyoState = puyo->getPuyoState(); + AnimatedPuyo *down = (AnimatedPuyo *)(attachedGame->getPuyoAt(i, j+1)); + AnimatedPuyo *right = (AnimatedPuyo *)(attachedGame->getPuyoAt(i+1, j)); + AnimatedPuyo *up = (AnimatedPuyo *)(attachedGame->getPuyoAt(i, j-1)); + AnimatedPuyo *left = (AnimatedPuyo *)(attachedGame->getPuyoAt(i-1, j)); + + PuyoState downState = (down == NULL) || (down->isRenderingAnimation()) ? PUYO_EMPTY : down->getPuyoState(); + PuyoState rightState = (right == NULL) || (right->isRenderingAnimation()) ? PUYO_EMPTY : right->getPuyoState(); + PuyoState upState = (up == NULL) || (up->isRenderingAnimation()) ? PUYO_EMPTY : up->getPuyoState(); + PuyoState leftState = (left == NULL) || (left->isRenderingAnimation()) ? PUYO_EMPTY : left->getPuyoState(); + + int currentIndex = PuyoGroupImageIndex + [downState == currentPuyoState ? 1 : 0] + [rightState == currentPuyoState ? 1 : 0] + [upState == currentPuyoState ? 1 : 0] + [leftState == currentPuyoState ? 1 : 0]; + switch (currentPuyoState) { + case PUYO_VIOLET: + return puyoFaces[0][currentIndex]; + case PUYO_RED: + return puyoFaces[1][currentIndex]; + case PUYO_BLUE: + return puyoFaces[2][currentIndex]; + case PUYO_GREEN: + return puyoFaces[3][currentIndex]; + case PUYO_YELLOW: + return puyoFaces[4][currentIndex]; + default: + return getSurfaceForState(currentPuyoState); + } +} + +void PuyoView::cycleAnimation() +{ + // Cycling every puyo's animation + for (int i = 0, j = attachedGame->getPuyoCount() ; i < j ; i++) { + AnimatedPuyo *currentPuyo = + (AnimatedPuyo *)(attachedGame->getPuyoAtIndex(i)); + currentPuyo->cycleAnimation(); + } + // Cycling dead puyo's animations + attachedPuyoFactory.cycleWalhalla(); + + // Cycling view's animations + if (viewAnimations.getSize() > 0) { + PuyoAnimation *currentAnimation = (PuyoAnimation *)(viewAnimations.getElementAt(0)); + if (currentAnimation->isFinished()) { + viewAnimations.removeElementAt(0); + delete currentAnimation; + } + else { + currentAnimation->cycle(); + } + } + + // If there is a skipped cycle to do, do it + if ((skippedCycle || attachedGame->isEndOfCycle()) && attachedGame->isGameRunning() && cycleAllowed()) { + attachedGame->cycle(); + skippedCycle = false; + } +} + +void PuyoView::cycleGame() +{ + // If we are not allowed to cycle the game, mark it + if (cycleAllowed()) { + skippedCycle = false; + attachedGame->cycle(); + } + else { + skippedCycle = true; + } +} + +void PuyoView::moveLeft() +{ + if (cycleAllowed()) attachedGame->moveLeft(); +} + +void PuyoView::moveRight() +{ + if (cycleAllowed()) attachedGame->moveRight(); +} + +void PuyoView::rotateLeft() +{ + if (cycleAllowed()) attachedGame->rotate(true); +} + +void PuyoView::rotateRight() +{ + if (cycleAllowed()) attachedGame->rotate(false); +} + + +void PuyoView::render() +{ + + SDL_Rect drect; + SDL_Rect vrect; + vrect.x = xOffset; + vrect.y = yOffset; + vrect.w = TSIZE * PUYODIMX; + vrect.h = TSIZE * PUYODIMY; + + bool displayFallings = this->cycleAllowed(); + + for (int i = 0 ; i < PUYODIMX ; i++) { + for (int j = 0 ; j < PUYODIMY ; j++) { + AnimatedPuyo *currentPuyo = (AnimatedPuyo *)(attachedGame->getPuyoAt(i, j)); + if ((currentPuyo != NULL) && (displayFallings || !currentPuyo->isFalling()) && (getSurfaceForPuyo(currentPuyo) != neutral) + && (currentPuyo->getVisible()) && (currentPuyo->isRenderingAnimation() == false)) { + drect.x = xOffset + i * TSIZE; + drect.y = yOffset + j * TSIZE; + if (currentPuyo->getPuyoState() < PUYO_EMPTY) + drect.y -= attachedGame->getSemiMove() * TSIZE / 2; + drect.w = puyoShadow->w; + drect.h = puyoShadow->h; + if (drect.y + drect.h > vrect.y + vrect.h) + drect.h -= (drect.y + drect.h - vrect.y - vrect.h); + if (drect.x + drect.w > vrect.x + vrect.w) + drect.w -= (drect.x + drect.w - vrect.x - vrect.w); + attachedPainter.requestDraw(puyoShadow, &drect); + } + } + } + for (int i = 0, j = attachedGame->getPuyoCount() ; i < j ; i++) { + AnimatedPuyo *currentPuyo = (AnimatedPuyo *)(attachedGame->getPuyoAtIndex(i)); + if (displayFallings || !currentPuyo->isFalling()) currentPuyo->render(); + } + // drawing the walhalla + attachedPuyoFactory.renderWalhalla(); + + drect.x = nXOffset; + drect.y = nYOffset; + drect.w = TSIZE; + drect.h = TSIZE * 2; + // Drawing next puyos + IIM_Surface *currentSurface = getSurfaceForState(attachedGame->getNextFalling()); + if (currentSurface != NULL) { + drect.x = nXOffset; + drect.y = nYOffset + TSIZE; + drect.w = currentSurface->w; + drect.h = currentSurface->h; + attachedPainter.requestDraw(currentSurface, &drect); + if (currentSurface != neutral) attachedPainter.requestDraw(puyoEyes, &drect); + } + currentSurface = getSurfaceForState(attachedGame->getNextCompanion()); + if (currentSurface != NULL) { + drect.x = nXOffset; + drect.y = nYOffset; + drect.w = currentSurface->w; + drect.h = currentSurface->h; + attachedPainter.requestDraw(currentSurface, &drect); + if (currentSurface != neutral) attachedPainter.requestDraw(puyoEyes, &drect); + } + + // Drawing the view animation + if (viewAnimations.getSize() > 0) { + PuyoAnimation *currentAnimation = (PuyoAnimation *)(viewAnimations.getElementAt(0)); + if (!currentAnimation->isFinished()) { + currentAnimation->draw(0); + } + } +} + +void PuyoView::renderNeutral() +{ + SDL_Rect drect; + + // Drawing neutral puyos + int numBigNeutral = attachedGame->getNeutralPuyos() / PUYODIMX; + int numNeutral = attachedGame->getNeutralPuyos() % PUYODIMX; + int drect_x = xOffset; + int compressor = 0; + + int width = numBigNeutral * bigNeutral->w + numNeutral * neutral->w; + if (width > TSIZE * PUYODIMX) { + compressor = (width - TSIZE * PUYODIMX) / (numNeutral + numBigNeutral); + } + + for (int cpt = 0 ; cpt < numBigNeutral ; cpt++) { + drect.x = drect_x; + drect.y = yOffset + 3 + TSIZE; + drect.w = bigNeutral->w; + drect.h = bigNeutral->h; + attachedPainter.requestDraw(bigNeutral, &drect); + drect_x += bigNeutral->w - compressor; + } + for (int cpt = 0 ; cpt < numNeutral ; cpt++) { + drect.x = drect_x; + drect.y = yOffset + 3 + TSIZE; + drect.w = neutral->w; + drect.h = neutral->h; + attachedPainter.requestDraw(neutral, &drect); + drect_x += neutral->w - compressor; + } +} + +void PuyoView::gameDidAddNeutral(PuyoPuyo *neutralPuyo, int neutralIndex) { + int x = neutralPuyo->getPuyoX(); + int y = neutralPuyo->getPuyoY(); + ((AnimatedPuyo *)neutralPuyo)->addAnimation(new NeutralAnimation(*((AnimatedPuyo *)neutralPuyo), neutralIndex * 4)); +} + +void PuyoView::companionDidTurn(PuyoPuyo *companionPuyo, int companionVector, bool counterclockwise) +{ + ((AnimatedPuyo *)companionPuyo)->addAnimation(new TurningAnimation(*(AnimatedPuyo *)companionPuyo, + companionVector, counterclockwise)); +} + +void PuyoView::puyoDidFall(PuyoPuyo *puyo, int originX, int originY) +{ + ((AnimatedPuyo *)puyo)->addAnimation(new FallingAnimation(*(AnimatedPuyo *)puyo, originY, xOffset, yOffset, 16)); +} + +void PuyoView::puyoWillVanish(IosVector &puyoGroup, int groupNum, int phase) +{ + AnimationSynchronizer *synchronizer = new AnimationSynchronizer(); + viewAnimations.addElement(new VanishSoundAnimation(phase, synchronizer)); + for (int i = 0, j = puyoGroup.getSize() ; i < j ; i++) { + AnimatedPuyo *currentPuyo = (AnimatedPuyo *)(puyoGroup.getElementAt(i)); + currentPuyo->addAnimation(new VanishAnimation(*currentPuyo, i*2 , xOffset, yOffset, synchronizer)); + } + // A revoir + if (groupNum == 0) { + if (phase>=2) { + audio_sound_play(sound_yahoohoo3[(int)((float)NB_YAHOOHOO3 * random()/(RAND_MAX+1.0))]); + } + if (phase==1) { + audio_sound_play(sound_yahoohoo2[(int)((float)NB_YAHOOHOO2 * random()/(RAND_MAX+1.0))]); + } + else { + audio_sound_play(sound_yahoohoo1[(int)((float)NB_YAHOOHOO1 * random()/(RAND_MAX+1.0))]); + } + } +} + +void PuyoView::gameDidEndCycle() +{ + if (enemyGame != NULL) { + if (attachedGame->getNeutralPuyos() < 0) + enemyGame->increaseNeutralPuyos(- attachedGame->getNeutralPuyos()); + } +} + +bool PuyoView::cycleAllowed() +{ + if (cycleAllowance < 0) + return false; + return true; +} + + +void PuyoView::gameLost() +{ + gameRunning = false; +} + + + + diff --git a/PuyoView.h b/PuyoView.h new file mode 100644 index 0000000..fbee693 --- /dev/null +++ b/PuyoView.h @@ -0,0 +1,93 @@ +/* FloboPuyo + * Copyright (C) 2004 + * Florent Boudet , + * Jean-Christophe Hoelt , + * Guillaume Borios + * + * iOS Software + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + */ + +#ifndef _PUYOVIEW +#define _PUYOVIEW + +#include "glSDL.h" +#include "PuyoGame.h" +#include "IosImgProcess.h" +#include "PuyoAnimations.h" +#include "AnimatedPuyo.h" + +#define TSIZE 32 +#define ASIZE 32 +#define BSIZE 32 +#define CSIZE 32 +#define DSIZE 192 +#define ESIZE 32 +#define FSIZE 16 + +extern SDL_Surface *display; +extern IIM_Surface *image; + + + +class PuyoView : public virtual PuyoDelegate { + public: + PuyoView(PuyoRandomSystem *attachedRandom, int xOffset, int yOffset, int nXOffset, int nYOffset); + void setEnemyGame(PuyoGame *enemyGame); + void render(); + void renderNeutral(); + void cycleAnimation(); + void cycleGame(); + void allowCycle() { cycleAllowance++; } + void disallowCycle() { cycleAllowance--; } + + void moveLeft(); + void moveRight(); + void rotateLeft(); + void rotateRight(); + + static IIM_Surface *getSurfaceForState(PuyoState state); + IIM_Surface *getSurfaceForPuyo(PuyoPuyo *puyo); + PuyoGame *getAttachedGame() const { return attachedGame; } + SDL_Painter & getPainter() const { return attachedPainter; } + + int getScreenCoordinateX(int X) const { return X * TSIZE + xOffset; } + int getScreenCoordinateY(int Y) const { return Y * TSIZE + yOffset; } + + // PuyoDelegate methods + void gameDidAddNeutral(PuyoPuyo *neutralPuyo, int neutralIndex); + void gameDidEndCycle(); + void companionDidTurn(PuyoPuyo *companionPuyo, int companionVector, bool counterclockwise); + void puyoDidFall(PuyoPuyo *puyo, int originX, int originY); + void puyoWillVanish(IosVector &puyoGroup, int groupNum, int phase); + void gameLost(); + + private: + bool cycleAllowed(); + bool skippedCycle; + bool gameRunning; + int xOffset, yOffset; + int nXOffset, nYOffset; + AnimatedPuyoFactory attachedPuyoFactory; + PuyoGame *attachedGame, *enemyGame; + IosVector viewAnimations; + int cycleAllowance; + SDL_Painter &attachedPainter; +}; + +#endif // _PUYOVIEW diff --git a/SDL_Painter.cpp b/SDL_Painter.cpp new file mode 100644 index 0000000..b26baac --- /dev/null +++ b/SDL_Painter.cpp @@ -0,0 +1,205 @@ +#include "SDL_Painter.h" +#include + +// #define DEBUG + +extern SDL_Surface *display; + +SDL_Painter::SDL_Painter(IIM_Surface *gameScreen, IIM_Surface *bg) + : gameScreen(gameScreen), backGround(bg), nbElts(0), nbPrev(0) {} + +void SDL_Painter::requestDraw(IIM_Surface *surf, SDL_Rect *where) +{ +#ifdef DEBUG + if (nbElts >= MAX_PAINT_ELTS) { + fprintf(stderr, "To much elements given to SDL_Painter...\n"); + exit(1); + } +#endif + DrawElt elt; + elt.surf = surf; + elt.rect = *where; + onScreenElts[nbElts++] = elt; +} + +static inline bool isEqual(DrawElt &d1, DrawElt &d2) +{ + return (d1.surf == d2.surf) + && (d1.rect.x == d2.rect.x) + && (d1.rect.y == d2.rect.y) + && (d1.rect.w == d2.rect.w) + && (d1.rect.h == d2.rect.h); +} + +static inline bool isEqual(const SDL_Rect &r1, const SDL_Rect &r2) +{ + return (r1.x == r2.x) + && (r1.y == r2.y) + && (r1.w == r2.w) + && (r1.h == r2.h); +} + +static inline bool isInside(const SDL_Rect &r1, const SDL_Rect &r2) +{ + return (r1.x >= r2.x) && (r1.x + r1.w <= r2.x + r2.w) + && (r1.y >= r2.y) && (r1.y + r1.h <= r2.y + r2.h); +} + +static inline int addRectToList(SDL_Rect rectList[MAX_PAINT_ELTS], int nbRect, const SDL_Rect &rect) +{ + if ((rect.w <= 0) || (rect.h <= 0)) return nbRect; + for (int r=0; r= rectList[r].x) && (rect.x <= rectList[r].x + rectList[r].w)) { + SDL_Rect newRect = rectList[r]; + newRect.w = rect.w + rect.x - rectList[r].x; + rectList[r] = rectList[nbRect-1]; + return addRectToList(rectList, nbRect-1, newRect); + } + if ((rectList[r].x >= rect.x) && (rectList[r].x <= rect.x + rect.w)) { + SDL_Rect newRect = rect; + newRect.w = rectList[r].w + rectList[r].x - rect.x; + rectList[r] = rectList[nbRect-1]; + return addRectToList(rectList, nbRect-1, newRect); + } + } + // voisin vertical + if ((rect.x == rectList[r].x) && (rect.w == rectList[r].w)) { + if ((rect.y >= rectList[r].y) && (rect.y <= rectList[r].y + rectList[r].h)) { + SDL_Rect newRect = rectList[r]; + newRect.h = rect.h + rect.y - rectList[r].y; + rectList[r] = rectList[nbRect-1]; + return addRectToList(rectList, nbRect-1, newRect); + } + if ((rectList[r].y >= rect.y) && (rectList[r].y <= rect.y + rect.h)) { + SDL_Rect newRect = rect; + newRect.h = rectList[r].h + rectList[r].y - rect.y; + rectList[r] = rectList[nbRect-1]; + return addRectToList(rectList, nbRect-1, newRect); + } + } + } + rectList[nbRect] = rect; + return nbRect + 1; +} + +void SDL_Painter::draw(SDL_Surface *surf) +{ + SDL_Rect rectToUpdate[MAX_PAINT_ELTS]; // liste des zones a reafficher. + int nbRects = 0; + + bool findMatchPrev[MAX_PAINT_ELTS]; + + for (int j=0; jsurf, &rectToUpdate[r], surf, &rectToUpdate[r]); + for (int i=0; isurf, NULL, + surf, &rect); + } + } +#endif + + // Draw what is necessary... + storeScreenContent(surf); +} + +void SDL_Painter::storeScreenContent(SDL_Surface *surf) +{ + if (surf != display) { + SDL_SetClipRect(display,NULL); + SDL_BlitSurface(surf,NULL,display,NULL); + } + nbPrev = nbElts; + while(nbElts > 0) { + nbElts --; + onScreenPrev[nbElts] = onScreenElts[nbElts]; + } + nbElts = 0; +} + +void SDL_Painter::redrawAll(SDL_Surface *surf) +{ + SDL_SetClipRect(surf, NULL); + + // Draw everything. + SDL_BlitSurface(backGround->surf, NULL, surf, NULL); + for (int i=0; isurf, NULL, + surf, &onScreenElts[i].rect); + } + + // Remember what is on screen... + storeScreenContent(surf); +} diff --git a/SDL_Painter.h b/SDL_Painter.h new file mode 100644 index 0000000..286fe98 --- /dev/null +++ b/SDL_Painter.h @@ -0,0 +1,36 @@ +#ifndef _SDL_PAINTER_H +#define _SDL_PAINTER_H + +#define MAX_PAINT_ELTS 0x800 + +#include "IosImgProcess.h" + +struct DrawElt { + IIM_Surface *surf; + SDL_Rect rect; +}; + +class SDL_Painter +{ + public: + IIM_Surface *backGround; + IIM_Surface *gameScreen; +/* SDL_Surface *display; */ + + SDL_Painter(IIM_Surface *gameScreen = NULL, IIM_Surface *bg = NULL); + void requestDraw(IIM_Surface *surf, SDL_Rect *where); + void draw(SDL_Surface *surf); + void redrawAll(SDL_Surface *surf); + void draw() { draw(gameScreen->surf); } + void redrawAll() { redrawAll(gameScreen->surf); } + + private: + + int nbElts; + int nbPrev; + DrawElt onScreenElts[MAX_PAINT_ELTS]; + DrawElt onScreenPrev[MAX_PAINT_ELTS]; + void storeScreenContent(SDL_Surface *surf); +}; + +#endif diff --git a/SDL_prim.c b/SDL_prim.c new file mode 100644 index 0000000..f404d99 --- /dev/null +++ b/SDL_prim.c @@ -0,0 +1,690 @@ +/* + * SDL_prim.c + * + * Copyright (C) 2003 Ryan McGuigan + * + * Licensed under the Academic Free License version 1.2 + * + * Simple SDL Primitive C functions. My goal while writing this + * was to keep everything as simple as possible, both in the design + * of these functions and in the usage of these functions. + * + * I hope someone finds this useful. + * + */ + + +#include "SDL_prim.h" + + +/* + * simple pixel drawing function. + * + * tests to be sure pixel is within bounds of the surface, so you + * can draw off the surface to your hearts content + * + * it's probably not good practice to be drawing off the surface though... + * + */ +inline +void SDL_putPixel(SDL_Surface *surf, int x, int y, Uint32 clr) +{ + int Bpp = surf->format->BytesPerPixel; + Uint8 *p; + + if (! (x < 0 || x >= surf->w || y < 0 || y >= surf->h) ) + { + p = (Uint8*)surf->pixels + y * surf->pitch + x * Bpp; + + switch(Bpp) { + case 1: + *p = clr; + break; + + case 2: + *(Uint16*)p = clr; + break; + + case 3: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (clr >> 16) & 0xff; + p[1] = (clr >> 8) & 0xff; + p[2] = clr & 0xff; + } else { + p[0] = clr & 0xff; + p[1] = (clr >> 8) & 0xff; + p[2] = (clr >> 16) & 0xff; + } + break; + + case 4: + *(Uint32*)p = clr; + break; + } + } +} + + +/* + * returns pointer to pixel at x, y + */ +inline +Uint8* SDL_getPixel(SDL_Surface *surf, int x, int y) +{ + if (! (x < 0 || x >= surf->w || y < 0 || y >= surf->h) ) + return (Uint8*)surf->pixels + y * surf->pitch + x * surf->format->BytesPerPixel; + else + return 0; +} + + +/* + * slow pixel blending function + * + * called from SDL_blendPixel for 8 bit surfaces. + * may be useful for something else, I dunno. + * + */ +inline +void __slow_SDL_blendPixel( SDL_Surface *surf, int x, int y, Uint32 clr, + Uint8 alpha) +{ + Uint8 *p; + Uint8 r, g, b, r2, g2, b2; + + if ( (p = SDL_getPixel(surf, x, y)) ) { + SDL_GetRGB(clr, surf->format, &r, &g, &b); + SDL_GetRGB(*p, surf->format, &r2, &g2, &b2); + + r = r2 + ( r - r2 ) * ( alpha / 255.0 ); + g = g2 + ( g - g2 ) * ( alpha / 255.0 ); + b = b2 + ( b - b2 ) * ( alpha / 255.0 ); + + clr = SDL_MapRGB(surf->format, r, g, b); + + SDL_putPixel(surf, x, y, clr); + } +} + + +/* + * blends pixel onto surface according to alpha + */ +inline +void SDL_blendPixel( SDL_Surface *surf, int x, int y, Uint32 clr, + Uint8 alpha) +{ + Uint8 *p; + Uint32 R, G, B; + + if ( (p = SDL_getPixel(surf, x, y)) ) { + switch(surf->format->BytesPerPixel) { + case 1: /* 8 bpp */ + __slow_SDL_blendPixel(surf, x, y, clr, alpha); + break; + + case 2: /* 16 bpp */ + R = ( (*(Uint16*)p & surf->format->Rmask) + ( ((clr & surf->format->Rmask) - ((*(Uint16*)p) & surf->format->Rmask)) * alpha >> 8) ) & surf->format->Rmask; + G = ( (*(Uint16*)p & surf->format->Gmask) + ( ((clr & surf->format->Gmask) - ((*(Uint16*)p) & surf->format->Gmask)) * alpha >> 8) ) & surf->format->Gmask; + B = ( (*(Uint16*)p & surf->format->Bmask) + ( ((clr & surf->format->Bmask) - ((*(Uint16*)p) & surf->format->Bmask)) * alpha >> 8) ) & surf->format->Bmask; + *(Uint16*)p = R | G | B; + break; + + case 3: /* 24 bpp */ + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = ( p[0] + ((((clr >> 16) & 0xff) - p[0]) * alpha >> 8) ) & 0xff; + p[1] = ( p[1] + ((((clr >> 8) & 0xff) - p[1]) * alpha >> 8) ) & 0xff; + p[2] = ( p[2] + (((clr & 0xff) - p[2]) * alpha >> 8) ) & 0xff; + } else { + p[0] = ( p[0] + (((clr & 0xff) - p[0]) * alpha >> 8) ) & 0xff; + p[1] = ( p[1] + ((((clr >> 8) & 0xff) - p[1]) * alpha >> 8) ) & 0xff; + p[2] = ( p[2] + ((((clr >> 16) & 0xff) - p[2]) * alpha >> 8) ) & 0xff; + } + break; + + case 4: /* 32 bpp */ + R = ( (*(Uint32*)p & surf->format->Rmask) + ( ((clr & surf->format->Rmask) - (*(Uint32*)p & surf->format->Rmask)) * alpha >> 8) ) & surf->format->Rmask; + G = ( (*(Uint32*)p & surf->format->Gmask) + ( ((clr & surf->format->Gmask) - (*(Uint32*)p & surf->format->Gmask)) * alpha >> 8) ) & surf->format->Gmask; + B = ( (*(Uint32*)p & surf->format->Bmask) + ( ((clr & surf->format->Bmask) - (*(Uint32*)p & surf->format->Bmask)) * alpha >> 8) ) & surf->format->Bmask; + *(Uint32*)p = R | G | B; + break; + } + } +} + + +/* + * Self explanatory. draws lines, supports anti-aliasing and + * alpha blending + * + */ +void SDL_drawLine_TG( SDL_Surface *surf, int x, int y, int x2, int y2, + Uint32 clr, Uint8 alpha, Uint8 flags ) +{ + int xaa, yaa, *a, *b, *a2, *b2, da, xd, yd; + float aa, db; + float realb; + + /* + * for the sake of my sanity when i look at this again... + * + * xaa: the x aa offset value, either 1 or 0 + * yaa: the y aa offset value, either 1 or 0 + * *a: reference to either x or y, depending on which way we are + * drawing. + * *b: reference to either x or y, depending on which way we are + * drawing. + * *a2: reference to either x2 or y2, depending on which way we are + * drawing. + * *b2: reference to either x2 or y2, depending on which way we are + * drawing. + * x: starting x value, current x value + * y: starting y value, current y value + * x2: ending x value + * y2: ending y value + * xd: distance from x to x2 + * yd: distance from y to y2 + * da: change in a for each iteration, either 1 or -1 + * db: change in b with respect to a + * realb: real b value, as b is an int, and db is going to be a + * fraction + * aa: anti-aliasing value, fraction part of realb + * + */ + + /* find x and y distances */ + xd = x2 - x; + yd = y2 - y; + + if (abs(xd) >= abs(yd)) { + /* draw left/right to left/right */ + xaa = 0; + yaa = 1; + a = &x; + b = &y; + a2 = &x2; + b2 = &y2; + db = (float)yd / xd; + } else { + /* draw top/bottom to top/bottom */ + xaa = 1; + yaa = 0; + a = &y; + b = &x; + a2 = &y2; + b2 = &x2; + db = (float)xd / yd; + } + + if (!(alpha < 255 && (flags & SDL_TG_ALPHA))) + alpha = 255; + + /* left or right? */ + da = (*a <= *a2) ? 1 : -1; + /* up or down? */ + db *= da; + /* init realb */ + realb = (float)*b; + /* we're stopping when we hit *a2, so we have to add +/-1 to it */ + *a2 += da; + + if (flags & SDL_TG_LOCK) + __SDL_PRIM_LOCKSURFACE(surf) + + for (; *a != *a2; *a += da) { + *b = floor(realb + 0.5); + + if (flags & SDL_TG_ANTIALIAS) { + aa = (realb + 0.5 - *b) * alpha; + SDL_blendPixel(surf, x + xaa, y + yaa, clr, (int)aa); + SDL_blendPixel(surf, x - xaa, y - yaa, clr, alpha - (int)aa); + } + + if (alpha < 255) + SDL_blendPixel(surf, x, y, clr, alpha); + else + SDL_putPixel(surf, x, y, clr); + + realb += db; + } + + if (flags & SDL_TG_LOCK) + __SDL_PRIM_UNLOCKSURFACE(surf) +} + + + +/* + * Circle drawing/filling function. Uses a more optimized algorithm + * when drawing non-anti-aliased circles, uses a mathematically correct algo + * when drawing anti-aliased circles. After much trial and error, I have + * removed 100% of pixel overlapping, no more spots in anti-aliased circles. + * + */ +void SDL_drawCircle_TG( SDL_Surface *surf, int x1, int y1, int r, Uint32 clr, + Uint8 alpha, Uint8 flags ) +{ + int x = 0, tmp, dy = 0, y = r, r2, aa = 0, fy = 0; + int r2p = r * r; + float aatmp; + +# define __CIRCLE_OUTAA \ + { \ + if (x) { \ + SDL_blendPixel(surf, x1 + x, y1 + y + 1, clr, aa); \ + SDL_blendPixel(surf, x1 + x, y1 - y - 1, clr, aa); \ + } \ + SDL_blendPixel(surf, x1 - x, y1 + y + 1, clr, aa); \ + SDL_blendPixel(surf, x1 - x, y1 - y - 1, clr, aa); \ + } + +# define __CIRCLE_INAA \ + { \ + if (x) { \ + SDL_blendPixel(surf, x1 + x, y1 + y - 1, clr, alpha - aa); \ + SDL_blendPixel(surf, x1 + x, y1 - y + 1, clr, alpha - aa); \ + } \ + SDL_blendPixel(surf, x1 - x, y1 + y - 1, clr, alpha - aa); \ + SDL_blendPixel(surf, x1 - x, y1 - y + 1, clr, alpha - aa); \ + } + +# define __CIRCLE_OUTAA_ROT90 \ + { \ + if (x) { \ + SDL_blendPixel(surf, x1 + y + 1, y1 + x, clr, aa); \ + SDL_blendPixel(surf, x1 - y - 1, y1 - x, clr, aa); \ + } \ + SDL_blendPixel(surf, x1 - y - 1, y1 + x, clr, aa); \ + SDL_blendPixel(surf, x1 + y + 1, y1 - x, clr, aa); \ + } + +# define __CIRCLE_INAA_ROT90 \ + { \ + if (x) { \ + SDL_blendPixel(surf, x1 + y - 1, y1 + x, clr, alpha - aa); \ + SDL_blendPixel(surf, x1 - y + 1, y1 - x, clr, alpha - aa); \ + } \ + SDL_blendPixel(surf, x1 - y + 1, y1 + x, clr, alpha - aa); \ + SDL_blendPixel(surf, x1 + y - 1, y1 - x, clr, alpha - aa); \ + } + +# define __CIRCLE_BLEND \ + { \ + if (x) { \ + SDL_blendPixel(surf, x1 + x, y1 + y, clr, alpha); \ + SDL_blendPixel(surf, x1 + x, y1 - y, clr, alpha); \ + } \ + SDL_blendPixel(surf, x1 - x, y1 + y, clr, alpha); \ + SDL_blendPixel(surf, x1 - x, y1 - y, clr, alpha); \ + } + +# define __CIRCLE_BLEND_ROT90 \ + { \ + if (x) { \ + SDL_blendPixel(surf, x1 - y, y1 + x, clr, alpha); \ + SDL_blendPixel(surf, x1 + y, y1 + x, clr, alpha); \ + } \ + SDL_blendPixel(surf, x1 + y, y1 - x, clr, alpha); \ + SDL_blendPixel(surf, x1 - y, y1 - x, clr, alpha); \ + } + +# define __CIRCLE_DRAW \ + { \ + if (x) { \ + SDL_putPixel(surf, x1 + x, y1 + y, clr); \ + SDL_putPixel(surf, x1 + x, y1 - y, clr); \ + } \ + SDL_putPixel(surf, x1 - x, y1 + y, clr); \ + SDL_putPixel(surf, x1 - x, y1 - y, clr); \ + } + +# define __CIRCLE_DRAW_ROT90 \ + { \ + if (x) { \ + SDL_putPixel(surf, x1 - y, y1 + x, clr); \ + SDL_putPixel(surf, x1 + y, y1 + x, clr); \ + } \ + SDL_putPixel(surf, x1 + y, y1 - x, clr); \ + SDL_putPixel(surf, x1 - y, y1 - x, clr); \ + } + + if (!(alpha < 255 && (flags & SDL_TG_ALPHA))) + alpha = 255; + + if (r < 6) + r2 = 0; + else + r2 = r / 2; + + if (flags & SDL_TG_LOCK) + __SDL_PRIM_LOCKSURFACE(surf) + + if (flags & SDL_TG_FILL) { + if (alpha < 255) + SDL_blendPixel(surf, x1, y1, clr, alpha); + else + SDL_putPixel(surf, x1, y1, clr); + } + + do { + if (alpha < 255) { + __CIRCLE_BLEND + __CIRCLE_BLEND_ROT90 + } else { + __CIRCLE_DRAW + __CIRCLE_DRAW_ROT90 + } + + if (flags & SDL_TG_FILL) { + /* Fill the circle... */ + if (alpha < 255) { + for (fy = y - 1; fy >= (x ? x : 1); fy--) { + if (x) { + SDL_blendPixel(surf, x1 - x, y1 + fy, clr, alpha); + SDL_blendPixel(surf, x1 - x, y1 - fy, clr, alpha); + } + SDL_blendPixel(surf, x1 + x, y1 + fy, clr, alpha); + SDL_blendPixel(surf, x1 + x, y1 - fy, clr, alpha); + + if (fy > x) { + if (x) { + SDL_blendPixel(surf, x1 - fy, y1 + x, clr, alpha); + SDL_blendPixel(surf, x1 + fy, y1 + x, clr, alpha); + } + SDL_blendPixel(surf, x1 - fy, y1 - x, clr, alpha); + SDL_blendPixel(surf, x1 + fy, y1 - x, clr, alpha); + } + } + } else { + for (fy = y - 1; fy >= (x ? x : 1); fy--) { + if (x) { + SDL_putPixel(surf, x1 - x, y1 + fy, clr); + SDL_putPixel(surf, x1 - x, y1 - fy, clr); + } + SDL_putPixel(surf, x1 + x, y1 + fy, clr); + SDL_putPixel(surf, x1 + x, y1 - fy, clr); + + if (fy > x) { + if (x) { + SDL_putPixel(surf, x1 - fy, y1 + x, clr); + SDL_putPixel(surf, x1 + fy, y1 + x, clr); + } + SDL_putPixel(surf, x1 - fy, y1 - x, clr); + SDL_putPixel(surf, x1 + fy, y1 - x, clr); + } + } + } + } + + if (flags & SDL_TG_ANTIALIAS) { + /* + * anti-aliased circle algorithm. this is + * mathematically correct, but fairly expensive. + * looks extremely good though. + * + * this is also very simple, we simply find the y + * value with respect to each x value, using a circle + * equation. the anti-alias values are based on the + * real y coordinates. + */ + __CIRCLE_OUTAA + __CIRCLE_OUTAA_ROT90 + if (!(flags & SDL_TG_FILL)) { + __CIRCLE_INAA + if (x < y-1) + __CIRCLE_INAA_ROT90 + } + + aatmp = sqrt((float)r2p - x*x); + y = aatmp; + aatmp = aatmp - (int)aatmp; + aa = aatmp * alpha; + x++; + } else { + /* + * i came up with this circle drawing algorithm on my + * own. i don't know how it compares to other circle + * drawing algos as far as optimization, but this seems + * to work pretty well. it's also pretty damn + * simple, which is what i'm all about. + */ + + /* move along x axis */ + x++; + + /* + * adjust y if we're moving too far out of the circle. + * the r2 is simply the radius over 2, unless the + * radius is less than a particular value(above). + * this simply makes the circle look more accurate, + * as we're working with integers so it's never exact, + * and we don't want to change the y value unless we're + * going off by a particular amount. this also more + * closely approximates the above, mathematically + * correct, algo. + */ + if (dy) + y += dy; + tmp = (r2p - x*x - y*y + r2); + if (tmp < 0) + dy = -1; + else if (dy) + dy = 0; + } + } while (x < y); + + if (x == y) { + if (alpha < 255) + __CIRCLE_BLEND + else + __CIRCLE_DRAW + + if (flags & SDL_TG_ANTIALIAS) { + __CIRCLE_OUTAA + __CIRCLE_OUTAA_ROT90 + } + } + else if (flags & SDL_TG_ANTIALIAS) + __CIRCLE_OUTAA + + if (flags & SDL_TG_LOCK) + __SDL_PRIM_UNLOCKSURFACE(surf) +} + + +/* + * draws triangles + * + * this isn't very good, there is a good amount of pixel overlapping, + * and there are sometimes pixels dangling off the corners... + * + * bleh, fuck it, i'll fix it later + * + */ +void SDL_drawTriangle_TG(SDL_Surface *surf, int x1, int y1, int x2, int y2, + int x3, int y3, Uint32 clr, Uint8 alpha, Uint8 flags) +{ + int x, y, c, xlen, ylen, i, aaoffset; + float a1, b1, a2, b2, slopea, slopeb, aa, aastep, pb1, pb2; + float slope1, slope2, slope3; + + /* avoid compiler warnings... */ + aa = 0.0; + aastep = 0.0; + aaoffset = 0; + +# define __TRI_DRAW(PB, B, SLOPE) \ + { \ + if (fabs(SLOPE) > 1.0) { \ + c = (PB) > (B) ? -1 : 1; \ + if (flags & SDL_TG_ANTIALIAS) { \ + aastep = alpha / fabs(SLOPE); \ + aa = alpha; \ + } \ + for (y = (PB); ( c > 0 ? y < floor(B) : y > (B) ); y += c) { \ + if (alpha < 255) \ + SDL_blendPixel(surf, x, y, clr, alpha); \ + else \ + SDL_putPixel(surf, x, y, clr); \ + if (flags & SDL_TG_ANTIALIAS) { \ + SDL_blendPixel(surf, x - 1, y, clr, (int)aa); \ + SDL_blendPixel(surf, x + 1, y, clr, (int)(alpha - aa)); \ + aa -= aastep; \ + } \ + } \ + } else { \ + if (alpha < 255) \ + SDL_blendPixel(surf, x, (int)(B), clr, (int)alpha); \ + else \ + SDL_putPixel(surf, x, (int)(B), clr); \ + if (flags & SDL_TG_ANTIALIAS) { \ + aa = (B) - (int)(B); \ + aa *= alpha; \ + SDL_blendPixel(surf, x, (int)(B) + 1, clr, (int)aa); \ + SDL_blendPixel(surf, x, (int)(B) - 1, clr, (int)(alpha - aa)); \ + } \ + } \ + } + +# define __TRI_FILL_AA(PB, B, B2, SLOPE) \ + { \ + if (fabs(SLOPE) > 2.0) { \ + c = (SLOPE > 0) ? 1 : -1; \ + if (SLOPE > 0) { \ + if (SLOPE == slope3) { \ + aaoffset = 0; \ + aa = 0; \ + aastep = alpha / SLOPE; \ + } \ + else { \ + aaoffset = -1; \ + aa = alpha; \ + aastep = alpha / SLOPE * -1; \ + } \ + } \ + else { \ + if (SLOPE == slope3) { \ + aaoffset = 0; \ + aa = 0; \ + aastep = alpha / SLOPE * -1; \ + } \ + else { \ + aaoffset = -1; \ + aa = alpha; \ + aastep = alpha / SLOPE; \ + } \ + } \ + for (y = (PB) + c; ( c > 0 ? y < floor(B) : y > (B) ); y += c) { \ + SDL_blendPixel(surf, x + aaoffset, y, clr, (int)aa); \ + aa += aastep; \ + } \ + } else { \ + if (B > B2) \ + c = 1; \ + else \ + c = -1; \ + aa = fabs( (B) - (int)(B) ); \ + if (c < 0) \ + aa = alpha - aa * alpha; \ + else \ + aa *= alpha; \ + SDL_blendPixel(surf, x, (B) + c, clr, (int)aa); \ + } \ + } + + if ( !(flags & SDL_TG_ALPHA && alpha < 255) ) + alpha = 255; + + /* + * sort points by X coordinate so we can split the drawing + * at a slope change + */ + while ( !(x1 <= x2 && x1 <= x3) ) { + x = x1; + y = y1; + x1 = x2; + y1 = y2; + x2 = x3; + y2 = y3; + x3 = x; + y3 = y; + } + while ( !(x2 <= x3) ) { + x = x2; + y = y2; + x2 = x3; + y2 = y3; + x3 = x; + y3 = y; + } + + xlen = x3 - x1; + ylen = y3 - y1; + slope1 = (float)ylen / xlen; + + xlen = x1 - x2; + ylen = y1 - y2; + slope2 = (float)ylen / xlen; + + xlen = x2 - x3; + ylen = y2 - y3; + slope3 = (float)ylen / xlen; + + a1 = x1; + a2 = x2; + b1 = y1; + b2 = y1; + slopea = slope1; + slopeb = slope2; + + pb1 = b1; + pb2 = b2; + + if (flags & SDL_TG_LOCK) + __SDL_PRIM_LOCKSURFACE(surf) + + for (i = 0; i <= 1; i++) { + if ((a1 == x1 && a1 != a2) || (a1 != x1 && a1 - 1 != a2)) { + for (x = a1; x <= a2; x++) { + if (flags & SDL_TG_FILL) { + c = ((int)b1 < (int)b2) ? 1 : -1; + for (y = (int)b1; (c > 0 ? y <= (int)b2 : y >= (int)b2); y += c) { + if (alpha < 255) + SDL_blendPixel(surf, x, y, clr, alpha); + else + SDL_putPixel(surf, x, y, clr); + } + if (flags & SDL_TG_ANTIALIAS) { + __TRI_FILL_AA(pb1, b1, b2, slopea) + __TRI_FILL_AA(pb2, b2, b1, slopeb) + } + } + else { + __TRI_DRAW(pb1, b1, slopea) + __TRI_DRAW(pb2, b2, slopeb) + } + pb1 = b1; + pb2 = b2; + + b1 += slopea; + b2 += slopeb; + } + } else if (!(flags & SDL_TG_FILL)) { + if (a1 == x1) + SDL_drawLine_TG(surf, x1,y1, x2,y2, clr, alpha, flags); + else + SDL_drawLine_TG(surf, x2,y2, x3,y3, clr, alpha, flags); + } + + a1 = x2 + 1; + a2 = x3; + b2 = y2; + slopeb = slope3; + b2 += slopeb; + } + + if (flags & SDL_TG_LOCK) + __SDL_PRIM_UNLOCKSURFACE(surf) +} + + diff --git a/SDL_prim.h b/SDL_prim.h new file mode 100644 index 0000000..06a374d --- /dev/null +++ b/SDL_prim.h @@ -0,0 +1,133 @@ + /* + * Copyright (C) 2003 Ryan McGuigan + * + * Licensed under the Academic Free License version 1.2 + * + */ + +#ifndef __SDL_PRIM_H +#define __SDL_PRIM_H 1 + +#include +#include +#include + +#include "glSDL.h" + +/* + * SDL_prim drawing flags + */ +#define SDL_TG_ALPHA 0x01 +#define SDL_TG_ANTIALIAS 0x02 +#define SDL_TG_FILL 0x04 +#define SDL_TG_LOCK 0x08 + +#define __SDL_PRIM_LOCKSURFACE(screen) \ +{ \ + if ( SDL_MUSTLOCK((screen)) ) { \ + if ( SDL_LockSurface((screen)) < 0 ) { \ + fprintf(stderr, "Can't lock video surface: %s\n", SDL_GetError()); \ + } \ + } \ +} + +#define __SDL_PRIM_UNLOCKSURFACE(screen) \ +{ \ + if ( SDL_MUSTLOCK((screen)) ) { \ + SDL_UnlockSurface((screen)); \ + } \ +} + +inline void SDL_putPixel(SDL_Surface*, int x, int y, Uint32 clr); +inline void SDL_blendPixel(SDL_Surface*, int x, int y, Uint32 clr, Uint8 alpha); +inline Uint8* SDL_getPixel(SDL_Surface*, int x, int y); +inline void __slow_SDL_blendPixel(SDL_Surface*, int, int, Uint32, Uint8); + +void SDL_drawLine_TG( SDL_Surface*, int x, int y, int x2, int y2, Uint32 clr, + Uint8 alpha, Uint8 flags ); +/* +void SDL_drawBrokenLine_TG( SDL_Surface*, int x, int y, int x2, int y2, + int seglen, int breaklen, Uint32 clr, Uint8 alpha, + Uint32 flags ); +*/ +void SDL_drawCircle_TG( SDL_Surface*, int x, int y, int r, Uint32 clr, + Uint8 alpha, Uint8 flags ); +void SDL_drawTriangle_TG( SDL_Surface*, int x, int y, int x2, int y2, int x3, + int y3, Uint32 clr, Uint8 alpha, Uint8 flags ); + +#define SDL_drawLine(surf, x1, y1, x2, y2, clr) \ + SDL_drawLine_TG((surf), (x1), (y1), (x2), (y2), (clr), 0, 0) + +#define SDL_drawLine_Alpha(surf, x1, y1, x2, y2, clr, alpha) \ + SDL_drawLine_TG( (surf), (x1), (y1), (x2), (y2), (clr),\ + (alpha), SDL_TG_ALPHA ) + +#define SDL_drawLine_AA(surf, x1, y1, x2, y2, clr) \ + SDL_drawLine_TG( (surf), (x1), (y1), (x2), (y2), (clr), 0,\ + SDL_TG_ANTIALIAS ) + +#define SDL_drawLine_Alpha_AA(surf, x1, y1, x2, y2, clr, alpha) \ + SDL_drawLine_TG( (surf), (x1), (y1), (x2), (y2), (clr),\ + (alpha), SDL_TG_ALPHA | SDL_TG_ANTIALIAS ) + +#define SDL_drawCircle(surf, x, y, r, clr) \ + SDL_drawCircle_TG((surf), (x), (y), (r), (clr), 0, 0) + +#define SDL_drawCircle_AA(surf, x, y, r, clr) \ + SDL_drawCircle_TG((surf), (x), (y), (r), (clr), 0, SDL_TG_ANTIALIAS) + +#define SDL_drawCircle_Alpha(surf, x, y, r, clr, alpha) \ + SDL_drawCircle_TG((surf), (x), (y), (r), (clr), (alpha), SDL_TG_ALPHA) + +#define SDL_drawCircle_Alpha_AA(surf, x, y, r, clr, alpha) \ + SDL_drawCircle_TG((surf), (x), (y), (r), (clr), (alpha),\ + SDL_TG_ALPHA | SDL_TG_ANTIALIAS) + +#define SDL_fillCircle(surf, x, y, r, clr) \ + SDL_drawCircle_TG((surf), (x), (y), (r), (clr), 0, SDL_TG_FILL) + +#define SDL_fillCircle_AA(surf, x, y, r, clr) \ + SDL_drawCircle_TG((surf), (x), (y), (r), (clr), 0,\ + SDL_TG_ANTIALIAS | SDL_TG_FILL) + +#define SDL_fillCircle_Alpha(surf, x, y, r, clr, alpha) \ + SDL_drawCircle_TG((surf), (x), (y), (r), (clr), (alpha),\ + SDL_TG_ALPHA | SDL_TG_FILL) + +#define SDL_fillCircle_Alpha_AA(surf, x, y, r, clr, alpha) \ + SDL_drawCircle_TG((surf), (x), (y), (r), (clr), (alpha),\ + SDL_TG_ALPHA | SDL_TG_ANTIALIAS | SDL_TG_FILL) + +#define SDL_drawTriangle(surf, x1,y1, x2,y2, x3,y3, clr) \ + SDL_drawTriangle_TG((surf), (x1),(y1), (x2),(y2), (x3),(y3), (clr), 0, \ + 0) + +#define SDL_drawTriangle_AA(surf, x1,y1, x2,y2, x3,y3, clr) \ + SDL_drawTriangle_TG((surf), (x1),(y1), (x2),(y2), (x3),(y3), (clr), 0, \ + SDL_TG_ANTIALIAS) + +#define SDL_drawTriangle_Alpha(surf, x1,y1, x2,y2, x3,y3, clr, alpha) \ + SDL_drawTriangle_TG((surf), (x1),(y1), (x2),(y2), (x3),(y3), (clr), \ + (alpha), SDL_TG_ALPHA) + +#define SDL_drawTriangle_Alpha_AA(surf, x1,y1, x2,y2, x3,y3, clr, alpha) \ + SDL_drawTriangle_TG((surf), (x1),(y1), (x2),(y2), (x3),(y3), (clr), \ + (alpha), SDL_TG_ALPHA | SDL_TG_ANTIALIAS) + +#define SDL_fillTriangle(surf, x1,y1, x2,y2, x3,y3, clr) \ + SDL_drawTriangle_TG((surf), (x1),(y1), (x2),(y2), (x3),(y3), (clr), 0, \ + SDL_TG_FILL) + +#define SDL_fillTriangle_AA(surf, x1,y1, x2,y2, x3,y3, clr) \ + SDL_drawTriangle_TG((surf), (x1),(y1), (x2),(y2), (x3),(y3), (clr), 0, \ + SDL_TG_ANTIALIAS|SDL_TG_FILL) + +#define SDL_fillTriangle_Alpha(surf, x1,y1, x2,y2, x3,y3, clr, alpha) \ + SDL_drawTriangle_TG((surf), (x1),(y1), (x2),(y2), (x3),(y3), (clr), \ + (alpha), SDL_TG_ALPHA|SDL_TG_FILL) + +#define SDL_fillTriangle_Alpha_AA(surf, x1,y1, x2,y2, x3,y3, clr, alpha) \ + SDL_drawTriangle_TG((surf), (x1),(y1), (x2),(y2), (x3),(y3), (clr), \ + (alpha), SDL_TG_ALPHA|SDL_TG_ANTIALIAS|SDL_TG_FILL) + +#endif diff --git a/TODO b/TODO new file mode 100644 index 0000000..c99fb82 --- /dev/null +++ b/TODO @@ -0,0 +1,18 @@ +#In game menu background wrong when changing keys while playing +#Correct the joystick 1P/2P fonctionality +#Make puyos fall from 3rd column instead of 4th +#Should not be able to play if the "falling row" is full +#Invert th puyos orientation when they fall +#Eye blinking pb when the puyos rotate +#When puyos are going up because of rotation, semi-move pb +#Key repetition +#Bug when rotating in a single column +#Make the "main puyo" visible compared to the companion +#Version in about +#Score board +#Alignement in menus +#Support d'OpenGL +Net Play +#Explosions trop rapides qd on accelere les cycles (fleche du bas) +#Ameliorer ce putain de makefile (cible statique pour mac, et surtout ne pas compiler glsdl quand opengl est desactive, c'est une horreur a compiler sur les becanes qui n'ont pas opengl installe, et eventuellement gerer mieux les dependances) +#Faire un systeme d'installation decent pour Linux diff --git a/audio.c b/audio.c new file mode 100644 index 0000000..b03d136 --- /dev/null +++ b/audio.c @@ -0,0 +1,229 @@ +#include +#include "audio.h" + +extern char *dataFolder; + +Sound *sound_pop; +Sound *sound_slide; +Sound *sound_bam1; +Sound *sound_fff; +Sound *sound_yahoohoo1[NB_YAHOOHOO1]; +Sound *sound_yahoohoo2[NB_YAHOOHOO2]; +Sound *sound_yahoohoo3[NB_YAHOOHOO3]; +Sound *sound_splash[8]; +Sound *sound_bim[2]; + + +#ifdef USE_AUDIO +#include "glSDL.h" +static Mix_Music *music[4]; + +static char *MUSIC_THEME[2][4] = { + { "flobopuyo_menu.xm", "flobopuyo_game1.xm", "flobopuyo_game2.xm", "flobopuyo_gameover.xm" }, + { "flobopuyo_menu.xm", "strange_fear.xm", "strange_fear2.xm", "strange_gameover.xm" } +}; + +static int sound_supported; +static int volume; +static int sound_on; +static int music_on; +static int currentMus = -1; + +static Sound *CustomMix_LoadWAV(char *path, char *fileName, int volume) +{ + Sound *result; + char temp[1024]; + if (!sound_supported) return NULL; + sprintf(temp, "%s/sfx/%s", dataFolder, fileName); + result = Mix_LoadWAV(temp); + if (result) + Mix_VolumeChunk (result, volume); + return result; +} + +static Mix_Music *CustomMix_LoadMUS(char *path, char *fileName) +{ + Mix_Music *result; + char temp[1024]; + if (!sound_supported) return NULL; + sprintf(temp, "%s/sfx/%s", dataFolder, fileName); + result = Mix_LoadMUS(temp); + return result; +} +#endif + +void +audio_init () +{ +#ifdef USE_AUDIO + int audio_rate; + Uint16 audio_format; + int audio_channels; + int i; + + sound_supported = + (Mix_OpenAudio (MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024) == 0); + + if (!sound_supported) return; + + sound_pop = CustomMix_LoadWAV (dataFolder, "pop.wav", 30); + sound_bam1 = CustomMix_LoadWAV (dataFolder, "bam1.wav", 12); + sound_fff = CustomMix_LoadWAV (dataFolder, "fff.wav", 48); + sound_slide = CustomMix_LoadWAV (dataFolder, "slide.wav", 128); + + sound_yahoohoo1[0] = NULL; + sound_yahoohoo1[1] = CustomMix_LoadWAV (dataFolder, "yahoohoo.wav", 50); + sound_yahoohoo1[2] = CustomMix_LoadWAV (dataFolder, "woho.wav", 32); + sound_yahoohoo1[3] = CustomMix_LoadWAV (dataFolder, "pastaga.wav", 70); + + sound_yahoohoo2[0] = sound_yahoohoo1[2]; + sound_yahoohoo2[1] = CustomMix_LoadWAV (dataFolder, "woo.wav", 45); + + sound_yahoohoo3[0] = CustomMix_LoadWAV (dataFolder, "applose.wav", 96); + + sound_splash[0] = CustomMix_LoadWAV (dataFolder, "splash1.wav",72); + sound_splash[1] = CustomMix_LoadWAV (dataFolder, "splash2.wav",72); + sound_splash[2] = CustomMix_LoadWAV (dataFolder, "splash3.wav",72); + sound_splash[3] = CustomMix_LoadWAV (dataFolder, "splash4.wav",72); + sound_splash[4] = CustomMix_LoadWAV (dataFolder, "splash5.wav",72); + sound_splash[5] = CustomMix_LoadWAV (dataFolder, "splash6.wav",72); + sound_splash[6] = CustomMix_LoadWAV (dataFolder, "splash7.wav",72); + sound_splash[7] = CustomMix_LoadWAV (dataFolder, "splash8.wav",72); + + sound_bim[0] = CustomMix_LoadWAV (dataFolder, "bim1.wav",72); + sound_bim[1] = CustomMix_LoadWAV (dataFolder, "bim2.wav",72); + + music[0] = CustomMix_LoadMUS (dataFolder, "flobopuyo_menu.xm"); + music[1] = CustomMix_LoadMUS (dataFolder, "flobopuyo_game1.xm"); + music[2] = CustomMix_LoadMUS (dataFolder, "flobopuyo_game2.xm"); + music[3] = CustomMix_LoadMUS (dataFolder, "flobopuyo_gameover.xm"); + + Mix_QuerySpec (&audio_rate, &audio_format, &audio_channels); + + sound_on = 1; + music_on = 1; + audio_music_set_volume (100); + audio_set_volume (50); +#endif +} + +void +audio_music_start (int num) +{ +#ifdef USE_AUDIO + if (!sound_supported) return; + if ((currentMus != num) && (music_on==true)) { + currentMus = num; + Mix_HaltMusic (); + if (music[num]) + Mix_PlayMusic (music[num], -1); + } +// audio_music_set_volume (50); +#endif +} + +void +audio_sound_play (Sound * s) +{ +#ifdef USE_AUDIO + if (!sound_supported) return; + if (s) + Mix_PlayChannel (-1, s, 0); +#endif +} + +void +audio_close () +{ +#ifdef USE_AUDIO + if (!sound_supported) return; + Mix_CloseAudio (); +#endif +} + +// vol compris entre 0 et 100; +void +audio_music_set_volume (int vol) +{ +#ifdef USE_AUDIO + if (!sound_supported) return; + Mix_VolumeMusic ((int) (128.0 * (double) vol / 100.0)); +#endif +} + +// vol compris entre 0 et 100; +void +audio_set_volume (int vol) +{ +#ifdef USE_AUDIO + if (!sound_supported) return; + if (sound_on) { + volume = Mix_Volume (-1, (int) (128.0 * (double) vol / 100.0)); + } + else { + volume = (int) (128.0 * (double) vol / 100.0); + } +#endif +} + +void +audio_set_music_on_off (int on) +{ +#ifdef USE_AUDIO + if (!sound_supported) return; + if (on) { + if (Mix_PausedMusic()) Mix_ResumeMusic(); + else + { + while (Mix_FadingMusic() == MIX_FADING_OUT) SDL_Delay(100); + } + if (music[currentMus]) + Mix_FadeInMusic (music[currentMus], -1, 1000); + } + else { + while (Mix_FadingMusic() == MIX_FADING_IN) SDL_Delay(100); + Mix_FadeOutMusic (1000); + } + music_on = on; +#endif +} + +void +audio_set_sound_on_off (int on) +{ +#ifdef USE_AUDIO + if (!sound_supported) return; + if (on) { + Mix_Volume (-1, volume); + } + else { + volume = Mix_Volume (-1, -1); + Mix_Volume (-1, 0); + } + sound_on = on; +#endif +} + +void audio_music_switch_theme(int theme_number) +{ +#ifdef USE_AUDIO + int i; + if (!sound_supported) return; + + Mix_Music * themusic[4]; + + for (i=0; i<4; ++i) { + themusic[i] = music[i]; + music[i] = CustomMix_LoadMUS (dataFolder, MUSIC_THEME[theme_number][i]); + } + + Mix_HaltMusic(); + + for (i=0; i<4; ++i) { + Mix_FreeMusic(themusic[i]); + } + + // Mix_PlayMusic(); + +#endif +} diff --git a/audio.h b/audio.h new file mode 100644 index 0000000..6e3caf5 --- /dev/null +++ b/audio.h @@ -0,0 +1,45 @@ +#ifndef _AUDIO_H +#define _AUDIO_H + +/// AUDIO + +#ifdef USE_AUDIO +#include +typedef Mix_Chunk Sound; +#else +#define Sound void +#endif + +/// les differents sons utilisables +extern Sound *sound_pop; +extern Sound *sound_bam1; +extern Sound *sound_fff; +extern Sound *sound_slide; +#define NB_YAHOOHOO1 4 +#define NB_YAHOOHOO2 2 +#define NB_YAHOOHOO3 1 +extern Sound *sound_yahoohoo1[NB_YAHOOHOO1]; +extern Sound *sound_yahoohoo2[NB_YAHOOHOO2]; +extern Sound *sound_yahoohoo3[NB_YAHOOHOO3]; +extern Sound *sound_splash[8]; +extern Sound *sound_bim[2]; + +void audio_init (); +void audio_music_start (int num); +void audio_sound_play (Sound * s); +void audio_close (); + +/// volume compris entre 0 et 100; +void audio_music_set_volume (int vol); + +/// volume compris entre 0 et 100; +void audio_set_volume (int vol); + +void audio_set_music_on_off (int on); +void audio_set_sound_on_off (int on); + + +#define NB_MUSIC_THEME 2 +void audio_music_switch_theme(int theme_number); + +#endif diff --git a/corona.cpp b/corona.cpp new file mode 100644 index 0000000..25bbeb4 --- /dev/null +++ b/corona.cpp @@ -0,0 +1,452 @@ +/* + * This file is released under the GNU General Public Licence + * + * authors: + * Richard Ashbury + * Jean-Christophe Hoelt + */ + +///////////////////////////////////////////////////////////////////////////// +// +// Corona.cpp : Implementation of CCorona +// +///////////////////////////////////////////////////////////////////////////// + +#include "corona.h" +#include +#include +using namespace std; + + +///////////////////////////////////////////////////////////////////////////// +// Corona::Corona +// Constructor + +Corona::Corona() +{ + m_clrForeground = 0x0000FF; + m_swirltime = 0; + m_testing = false; + m_silent = false; + m_avg = 1; + m_oldval = 0; + m_pos = 0; + + m_image = 0; + m_real_image = 0; + m_deltafield = 0; + m_width = -1; + m_height = -1; + m_real_height = -1; + nbParticules = 1000; + m_reflArray = 0; + m_waveloop = 0.0; + m_nPreset = PRESET_CORONA; + + m_particles = (Particle*)calloc (nbParticules, sizeof(Particle)); + // Create particles in random positions + for (int i = nbParticules - 1; i >= 0; --i) + { + Particle *it = m_particles + i; + it->x = random(0, 1); + it->y = random(0, 1); + it->xvel = it->yvel = 0; + } + // Set up the background swirling effect + chooseRandomSwirl(); +} + +///////////////////////////////////////////////////////////////////////////// +// Corona::~Corona +// Destructor + +Corona::~Corona() +{ + if (m_real_image) free(m_real_image); + if (m_deltafield) free(m_deltafield); +} + +double Corona::random(double min, double max) const { + return rand() * (max - min) / RAND_MAX + min; +} + +bool Corona::setUpSurface(int width, int height) { + // Delete any image that might have previously been allocated + if (m_real_image) free(m_real_image); + if (m_deltafield) free(m_deltafield); + if (m_reflArray) free(m_reflArray); + + // Fill in the size details in the BitmapInfo structure + m_width = width; + m_height = (height*4) / 5; + m_real_height = height; + + // Allocate the image data + m_real_image = (unsigned char *)calloc(1,width*height+1); + if (m_real_image == 0) return false; + m_image = m_real_image + m_width * (m_real_height - m_height); + m_reflArray = (int*)calloc(1,m_real_height - m_height + 1); + + // Allocate the delta-field memory, and initialise it + m_deltafield = (unsigned char**)malloc(m_width * m_height * sizeof(unsigned char*)); + + for (int x = 0; x < m_width; ++x) { + for (int y = 0; y < m_height; ++y) { + setPointDelta(x, y); + } + } + + // Change the number of particles + int newsize = (int) (::sqrt(m_width * m_height) * 3.0); + if (newsize < 2000) newsize = 2000; + int oldsize = (int) nbParticules; + nbParticules = newsize; + m_particles = (Particle*)realloc (m_particles, sizeof(Particle) * newsize); + for (int i = oldsize; i < newsize; ++i) { + m_particles[i].x = random(0, 1); + m_particles[i].y = random(0, 1); + m_particles[i].xvel = m_particles[i].yvel = 0; + } + + return true; +} + +void Corona::drawLine(int x0, int y0, int x1, int y1, unsigned char col) +{ + int incx = (x1 > x0) ? 1 : -1; + int incy = (y1 > y0) ? m_width : -m_width; + int dincx = 2 * abs(y1 - y0); + int dincy = 2 * abs(x1 - x0); + unsigned char* p = &(m_image[x0 + y0 * m_width]); + unsigned char* const end = &(m_image[m_width + (m_height - 1) * m_width]); + unsigned char* const start = m_image; + int n, d; // n is the "pixel counter" + + // Always draw at least one pixel + if (start <= p && p < end) *p = col; + + if (abs(x1 - x0) > abs(y1 - y0)) { + d = x0 - x1; + for (n = abs(x1 - x0); n > 0; --n, p += incx) { + if (start <= p && p < end) *p = col; + d += dincx; + if (d > 0) { + p += incy; + d -= dincy; + } + } + } + else { + d = y0 - y1; + for (n = abs(y1 - y0); n > 0; --n, p += incy) { + if (start <= p && p < end) *p = col; + d += dincy; + if (d > 0) { + p += incx; + d -= dincx; + } + } + } +} + + +void Corona::chooseRandomSwirl() +{ + m_swirl.x = random(0.2, 0.8); + m_swirl.y = random(0.2, 0.8); + m_swirl.tightness = random(-0.01, 0.01); + m_swirl.pull = random(1.0, 1.04); +} + +void Corona::setPointDelta(int x, int y) +{ + double tx = ((double) x / m_width) - m_swirl.x; + double ty = ((double) y / m_height) - m_swirl.y; + double d = tx * tx + ty * ty; + double ds = ::sqrt(d); + double ang = atan2(ty, tx) + m_swirl.tightness / (d + 0.01); + int dx = (int) ((ds * m_swirl.pull * cos(ang) - tx) * m_width) + rand() % 5 - 2; + int dy = (int) ((ds * m_swirl.pull * sin(ang) - ty) * m_height) + rand() % 5 - 2; + if (x + dx < 0) dx = -dx - x; + if (x + dx >= m_width) dx = 2 * m_width - 2 * x - dx - 1; + if (y + dy < 0) dy = -dy - y; + if (y + dy >= m_height) dy = 2 * m_height - 2 * y - dy - 1; + m_deltafield[x + y * m_width] = &(m_image[x + dx + (y + dy) * m_width]); +} + +void Corona::applyDeltaField(bool heavy) +{ + if (heavy) { + for (int y = 0; y < m_height; ++y) { + unsigned char *s = &(m_image[y * m_width]); + unsigned char **p = &(m_deltafield[y * m_width]); + for (int x = 0; x < m_width; ++x, ++s, ++p) { + *s = (*s + *(*p)) >> 1; + if (*s >= 2) *s -= 2; + } + } + } + else { + for (int y = 0; y < m_height; ++y) { + unsigned char *s = &(m_image[y * m_width]); + unsigned char **p = &(m_deltafield[y * m_width]); + for (int x = 0; x < m_width; ++x, ++s, ++p) { + *s = (*s + **p) >> 1; + if (*s >= 1) *s -= 1; + } + } + } +} + +int Corona::getBeatVal(TimedLevel *tl) +{ + int total = 0; + for (int i = 50; i < 250; ++i) { + int n = tl->frequency[0][i]; + total += n; + } + total /= 3; + + m_avg = 0.9 * m_avg + 0.1 * total; + if (m_avg < 1000) m_avg = 1000; + + if (total > m_avg * 1.2 && tl->timeStamp - tl->lastbeat > 750000) { + m_avg = total; + tl->lastbeat = tl->timeStamp; + if (total > 2500) return 2500; + else return total; + } + else return 0; +} + +void Corona::drawParticules() +{ + int p; + for (p = 0; p < nbParticules; ++p) { + Particle *it = m_particles + p; + int x = (int) (it->x * m_width); + int y = (int) (it->y * m_height); + int xv = (int) (it->xvel * m_width); + int yv = (int) (it->yvel * m_height); + drawLine(x, y, x - xv, y - yv, 255); + } +} + +void Corona::drawParticulesWithShift() +{ + int p; + for (p = 0; p < nbParticules; ++p) { + Particle *it = m_particles + p; + int x = (int) (it->x * m_width); + int y = (int) (it->y * m_height); + int xv = (int) (it->xvel * m_width); + int yv = (int) (it->yvel * m_height); + double l = (xv * xv + yv * yv); + if (l > 10.0 * 10.0) { + l = ::sqrt(l); + double dl = 10 / (l + 0.01); + xv = (int) (xv * dl); + yv = (int) (yv * dl); + } + drawLine(x, y, x - xv, y - yv, 255); + } +} + +void Corona::getAvgParticlePos(double& x, double& y) const +{ + x = y = 0; + for (int i = 0; i < 10; ++i) { + int r = rand() % nbParticules; + x += m_particles[r].x; + y += m_particles[r].y; + } + x /= 10; + y /= 10; +} + +#define REFL_MIN_WIDTH 3.0 +#define REFL_INC_WIDTH 0.08 + +void Corona::genReflectedWaves(double loop) +{ + double fdec = 0.0; + double floop = 0.0; + double fwidth = (m_real_height - m_height) * REFL_INC_WIDTH + REFL_MIN_WIDTH; + double REFL_MAX_WIDTH = fwidth; + + m_reflArray = new int[m_real_height - m_height + 1]; + + for (int i = 0; i < (m_real_height - m_height); ++i) + { + double fincr = (3.1415 / 2.0) * (1.0 - (fwidth - REFL_MIN_WIDTH) / REFL_MAX_WIDTH); + floop += fincr; + + fwidth -= REFL_INC_WIDTH; + fdec = fwidth * sin(floop + loop); + m_reflArray[i] = (int)fdec; + } +} + +void Corona::drawReflected() +{ + genReflectedWaves(m_waveloop); + int offsetDest = (m_real_height - m_height - 1) * m_width; + int offsetSrc = (m_real_height - m_height) * m_width; + + for (int i = m_real_height - m_height; i--;) + { + int idec = m_reflArray[i]; + + for (int x = m_width; x--;) + { + int out = m_real_image[(offsetSrc++) + idec]; + m_real_image[offsetDest++] = out; + } + + offsetDest -= m_width * 2; + offsetSrc += m_width; + } +} + +void Corona::blurImage() +{ + for (int y = 1; y < m_real_height - 1; ++y) { + m_real_image[y * m_width] = 0; + for (int x = 1; x < m_width - 1; ++x) { + int n = x + y * m_width; + int val = m_real_image[n + 1]; + val += m_real_image[n - 1]; + val += m_real_image[n - m_width]; + val += m_real_image[n + m_width]; + val >>= 2; + m_real_image[n] = val & 0xff; + } + } +} + +void Corona::update(TimedLevel *pLevels) +{ + // Check for a beat + int beatval = getBeatVal(pLevels); + if (beatval > 1000) + { + int total = 0; + for (int i = 0; i < 512; ++i) + total += 2 * pLevels->frequency[0][i]; + double currval = 1.0 - exp(-total / 40000.0); + m_oldval = (m_oldval + currval) / 2.0; + + double tx, ty; + getAvgParticlePos(tx, ty); + // If most of the particles are low down, use a launch + if (ty < 0.2 && rand() % 4 != 0) { + int p; + double bv = m_oldval * 5.0; + for (p = 0; p < nbParticules; ++p) + { + Particle *it = m_particles + p; + if (it->y < 0.1) { + double x = (it->x - tx) / bv; + it->yvel += 0.01 * bv * exp(-1000.0 * x * x); + } + } + } + else + { // Otherwise use a swirl + tx += random(-0.1, 0.1); + ty += random(-0.1, 0.1); + double bv = 0.009 * m_oldval; + double bv2 = 0.0036 * m_oldval; + if (rand() % 2 == 0) bv = -bv; + m_movement.x = tx; + m_movement.y = ty; + m_movement.tightness = random(0.8 * bv, bv); + m_movement.pull = random(1 - bv2, 1 - 0.2 * bv2); + m_swirltime = 1; + } + + pLevels->lastbeat = pLevels->currentTimeMs; + } + + // Deal with the particles + int p; + for (p = 0; p < nbParticules; ++p) + { + Particle *it = m_particles + p; + // Apply gravity + it->yvel -= 0.0006; // the gravity value + + // If there's an active swirl, swirl around it + if (m_swirltime > 0) { + double dx = it->x - m_movement.x; + double dy = it->y - m_movement.y; + double d = dx * dx + dy * dy; + double ds = ::sqrt(d); + double ang = atan2(dy, dx) + m_movement.tightness / (d + 0.01); + it->xvel += (ds * m_movement.pull * cos(ang) - dx); + it->yvel += (ds * m_movement.pull * sin(ang) - dy); + } + + // Gitter + it->xvel += random(-0.0002, 0.0002); + it->yvel += random(-0.0002, 0.0002); + + // Clamp the velocity + if (it->xvel < -0.1 ) it->xvel = -0.1; + if (it->xvel > 0.1 ) it->xvel = 0.1; + if (it->yvel < -0.1 ) it->yvel = -0.1; + if (it->yvel > 0.1 ) it->yvel = 0.1; + + // Randomly move the particle once in a while + if (rand() % (nbParticules / 5) == 0) + { + it->x = random(0, 1); + it->y = random(0, 1); + it->xvel = it->yvel = 0; + } + + // Move and bounce the particle + it->x += it->xvel; + it->y += it->yvel; + if (it->x < 0) { it->x = -it->x; it->xvel *= -0.25; it->yvel *= 0.25; } + if (it->y < 0) { it->y = -it->y; it->xvel *= 0.25; it->yvel *= -0.25; } + if (it->x > 1) { it->x = 2.0 - it->x; it->xvel *= -0.25; it->yvel *= 0.25; } + if (it->y > 1) { it->y = 2.0 - it->y; it->xvel *= 0.25; it->yvel = 0; } + } + + if (m_swirltime > 0) --m_swirltime; + + // Randomly change the delta field + if (rand() % 200 == 0) chooseRandomSwirl(); + + // Animate the waves + m_waveloop += 0.6; + + // drawing. + if (m_image != 0) + { + // Draw the particles on the bitmap + drawParticules(); + + // Apply the deltafield and update a few of its points + applyDeltaField((m_nPreset == PRESET_BLAZE) && m_width * m_height < 150000); + + int n = (m_width * m_height) / 100; + for (int i = 0; i < n; ++i) + setPointDelta(rand() % m_width, rand() % m_height); + + // If on the blaze preset, draw the particles again + if (m_nPreset == PRESET_BLAZE) + drawParticules(); + + drawReflected(); + + // Blur the bitmap + blurImage(); + + // If on the blaze preset, draw the particles one last time + if (m_nPreset == PRESET_BLAZE) + drawParticulesWithShift(); + } +} + diff --git a/corona.h b/corona.h new file mode 100644 index 0000000..1777b40 --- /dev/null +++ b/corona.h @@ -0,0 +1,114 @@ +/* + * This file is released under the GNU General Public Licence + * + * authors: + * Richard Ashbury + * Jean-Christophe Hoelt + */ + +///////////////////////////////////////////////////////////////////////////// +// +// corona.h : Declaration of the Corona class +// +///////////////////////////////////////////////////////////////////////////// + +#ifndef __CORONA_H_ +#define __CORONA_H_ + +#include "corona_types.h" + +// presets +enum { + PRESET_CORONA = 0, + PRESET_BLAZE, + PRESET_COUNT +}; + +// The particles +struct Particle +{ + double x, y, xvel, yvel; +}; + +// The background swirl +struct Swirl +{ + double x; + double y; + double tightness; + double pull; +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// Core Class of the Corona Visual FX +// +///////////////////////////////////////////////////////////////////////////// + +class Corona +{ + private: + int m_clrForeground; // foreground color + int m_nPreset; + + Particle *m_particles; + int nbParticules; + + // The off-screen buffer + unsigned char* m_image; + unsigned char* m_real_image; + + int m_width; + int m_height; + int m_real_height; + + Swirl m_swirl; + unsigned char** m_deltafield; + + // Particle movement info + int m_swirltime; + Swirl m_movement; + + bool m_testing; + bool m_silent; + + // Beat detection + double m_avg; + double m_oldval; + int m_pos; + + // Waves + double m_waveloop; + int *m_reflArray; + + // Implementation functions + double random(double min, double max) const; + // void setUpPalette(); + void drawLine(int x0, int y0, int x1, int y1, unsigned char col); + void drawParticules(); + void drawParticulesWithShift(); + void blurImage(); + void drawReflected(); + + void chooseRandomSwirl(); + void setPointDelta(int x, int y); + void applyDeltaField(bool heavy); + int getBeatVal(TimedLevel *tl); + void getAvgParticlePos(double& x, double& y) const; + void genReflectedWaves(double loop); + + + public: + Corona(); + ~Corona(); + + bool setUpSurface(int width, int height); + void update(TimedLevel *pLevels); + unsigned char *getSurface() const { return m_real_image; } + + int getWidth() const { return m_width; } + int getHeight() const { return m_real_height; } +}; + +#endif //__CORONA_H_ diff --git a/corona32.cpp b/corona32.cpp new file mode 100644 index 0000000..72aabed --- /dev/null +++ b/corona32.cpp @@ -0,0 +1,98 @@ +/* + * This file is released under the GNU General Public Licence + * + * authors: + * Richard Ashbury + * Jean-Christophe Hoelt + */ + +#include "corona32.h" +#include "corona.h" +#include "corona_palette.h" + +#include +#include +#include + +#define WIDTH 400 +#define HEIGHT 300 + +static const int PALETTEDATA[][NB_PALETTES] = { +#include "corona_pal.h" +}; + +/* Private members */ + +struct _Corona32 +{ + Corona *corona; + PaletteCycler *pal; + TimedLevel tl; + int width; + int height; +}; + +/* Private methods */ + +void corona32_init_internals(Corona32 *_this) +{ + _this->corona = new Corona(); + _this->pal = new PaletteCycler(PALETTEDATA, NB_PALETTES); + _this->tl.timeStamp = 0; + _this->tl.lastbeat = 0; + _this->tl.state = normal_state; + _this->width = 0; + _this->height = 0; +} + +void corona32_free_internals(Corona32 *_this) +{ + delete _this->corona; + delete _this->pal; +} + +/* Public methods */ + +Corona32 *corona32_new(void) +{ + Corona32 *_this = (Corona32*)calloc(1, sizeof(Corona32)); + corona32_init_internals(_this); + return _this; +} + +void corona32_delete(Corona32 *_this) +{ + corona32_free_internals(_this); + free(_this); +} + +void corona32_resize(Corona32 *_this, int width, int height) +{ + if (_this->width != 0) { + corona32_free_internals(_this); + corona32_init_internals(_this); + } + _this->corona->setUpSurface(width, height); + _this->width = width; + _this->height = height; +} + +void corona32_update(Corona32 *_this, int timeInMilli, short frequency[2][512]) +{ + _this->tl.timeStamp ++; + for (int i=0; i<512; ++i) + { + _this->tl.frequency[0][i] = frequency[0][i]; + _this->tl.frequency[1][i] = frequency[1][i]; + } + _this->corona->update(&_this->tl); // Update Corona + _this->pal->update(&_this->tl); // Update Palette Cycler +} + +void corona32_displayRGBA(Corona32 *_this, int *screen) +{ + int hwPal[256]; + paletteToRGBA(hwPal, _this->pal->getPalette()); + blitSurface8To32(_this->corona->getSurface(), screen, hwPal, _this->width * _this->height); +} + diff --git a/corona32.h b/corona32.h new file mode 100644 index 0000000..e8efd75 --- /dev/null +++ b/corona32.h @@ -0,0 +1,29 @@ +#ifndef _CORONA_32 +#define _CORONA_32 + +/* + * This file is released under the GNU General Public Licence + * + * authors: + * Richard Ashbury + * Jean-Christophe Hoelt + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _Corona32 Corona32; + +Corona32 *corona32_new(void); +void corona32_delete(Corona32 *_this); + +void corona32_resize(Corona32 *_this, int widht, int height); +void corona32_update(Corona32 *_this, int timeInMilli, short frequency[2][512]); +void corona32_displayRGBA(Corona32 *_this, int *screen); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/corona_pal.h b/corona_pal.h new file mode 100644 index 0000000..8d5015e --- /dev/null +++ b/corona_pal.h @@ -0,0 +1,30 @@ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff }, /* 1 */ + { 3, 85, 0xff0000, 170, 0xffff00, 255, 0xffffff } /* 1 */ diff --git a/corona_palette.cpp b/corona_palette.cpp new file mode 100644 index 0000000..59ce8a3 --- /dev/null +++ b/corona_palette.cpp @@ -0,0 +1,183 @@ +/* + * This file is released under the GNU General Public Licence + * + * authors: + * Richard Ashbury + * Jean-Christophe Hoelt + */ + +#include "corona_palette.h" + +#include +#include +using namespace std; + +/////////////////////////////////////////////////////////////////////////////// + + +// Palette stored in compressed format (just checkpoints) +class CompressedPalette +{ + public: + ColorRGB m_col[16]; + int m_ind[16]; + int m_nb; + + CompressedPalette() : m_nb(0) {} + void push_color(int i, ColorRGB col) { m_col[m_nb] = col; m_ind[m_nb++] = i; } + + void expand(Palette pal) const; +}; + +/////////////////////////////////////////////////////////////////////////////// + +PaletteCycler::PaletteCycler(const int palettes[][NB_PALETTES], int nbPalettes) + : m_palettes(palettes, nbPalettes) +{ + for (int i=0;i<256;++i) { + m_curpal[i].rgbBlue = 0; + m_curpal[i].rgbGreen = 0; + m_curpal[i].rgbRed = 0; + } + startPaletteTransition(); + affectPaletteTransition(1); + m_transferring = false; + m_srcnum = m_destnum; +} + +void PaletteCycler::startPaletteTransition() +{ + if (m_palettes.size() > 0) + { + // Copy the current palette to the "source palette" + memcpy(m_srcpal, m_curpal, sizeof(m_srcpal)); + + // Create a new palette as the "destination palette" + m_srcnum = m_destnum; + m_destnum = rand() % (int) m_palettes.size(); + m_palettes.expandPalette(m_destnum, m_destpal); + + // Begin the transition + m_transferring = true; + m_progress = 0; + } +} + +void PaletteCycler::update(TimedLevel *pLevels) +{ + // Randomly change the destination palette + if (pLevels->timeStamp - pLevels->lastbeat > 10000000) { + if (rand() % 100 == 0) startPaletteTransition(); + } + else { + if (rand() % 400 == 0) startPaletteTransition(); + } + + // Continue any current palette transtion + if (m_transferring) { + if (pLevels->timeStamp - pLevels->lastbeat > 10000000) m_progress += 0.01; + else m_progress += 0.005; + if (m_progress >= 1) { + m_transferring = false; + m_progress = 1; + m_srcnum = m_destnum; + } + // Use an inverse sigmoid transition to emphasise the midway palette + double x; + if (m_progress < 0.5) x = 2 * m_progress * (1 - m_progress); + else x = 2 * m_progress * (m_progress - 1) + 1; + affectPaletteTransition(x); + } +} + +void PaletteCycler::affectPaletteTransition(double p) +{ + for (int i = 0; i < 256; ++i) + { + ColorRGB c1 = m_srcpal[i]; + ColorRGB c2 = m_destpal[i]; + m_curpal[i].rgbRed = (unsigned char) ((1 - p) * c1.rgbRed + p * c2.rgbRed); + m_curpal[i].rgbGreen = (unsigned char) ((1 - p) * c1.rgbGreen + p * c2.rgbGreen); + m_curpal[i].rgbBlue = (unsigned char) ((1 - p) * c1.rgbBlue + p * c2.rgbBlue); + } +} + + + +/////////////////////////////////////////////////////////////////////////////// + +void CompressedPalette::expand(Palette dest) const +{ + ColorRGB col; + int entry = 0; + col.rgbBlue = col.rgbGreen = col.rgbRed = 0; + + int i = 0; + for (; i < m_nb; ++i) + { + int j = entry; + for (; j < m_ind[i]; ++j) + { + double t = (double) (j - entry) / (m_ind[i] - entry); + dest[j].rgbRed = (unsigned char) ((1 - t) * col.rgbRed + t * m_col[i].rgbRed); + dest[j].rgbGreen = (unsigned char) ((1 - t) * col.rgbGreen + t * m_col[i].rgbGreen); + dest[j].rgbBlue = (unsigned char) ((1 - t) * col.rgbBlue + t * m_col[i].rgbBlue); + } + entry = j; + col = m_col[i]; + } + for (; entry < 256; ++entry) dest[entry] = col; +} + +/////////////////////////////////////////////////////////////////////////////// + +PaletteCollection::PaletteCollection(const int palettes[][NB_PALETTES], int nbPalettes) +{ + m_cpal = new CompressedPalette[nbPalettes]; + m_nbPalettes = nbPalettes; + + // Set up the palettes from the array + for (int i = 0; i < nbPalettes; ++i) + { + CompressedPalette newpal; + const int* pal = palettes[i]; + for (int j = 1; j < pal[0] * 2; j += 2) + { + ColorRGB rgb; + rgb.rgbRed = (pal[j + 1] & 0xff0000) >> 16; + rgb.rgbGreen = (pal[j + 1] & 0xff00) >> 8; + rgb.rgbBlue = pal[j + 1] & 0xff; + newpal.push_color(pal[j], rgb); + } + m_cpal[i] = newpal; + } +} + +PaletteCollection::~PaletteCollection() +{ + delete[] m_cpal; +} + +void PaletteCollection::expandPalette(int i, Palette dest) const +{ + m_cpal[i].expand(dest); +} + +/////////////////////////////////////////////////////////////////////////////// + +void blitSurface8To32(unsigned char *byteSurf, int *colorSurf, int palette[256], int size) +{ + int i = 0; + while(size--) { + colorSurf[i++] = palette[byteSurf[size]]; + } +} + +void paletteToRGBA(int dest[256], const Palette src) +{ + for (int i=0; i<256; ++i) { + int alpha = (i<128?i*2:255); + dest[i] = (alpha << 24) | ((int)src[i].rgbRed << 16) | ((int)src[i].rgbGreen << 8) | (int)src[i].rgbBlue; +/* dest[i] = ((int)i << 24) | ((int)src[i].rgbRed << 0) | ((int)src[i].rgbGreen << 8) | ((int)src[i].rgbBlue << 16); */ + } +} diff --git a/corona_palette.h b/corona_palette.h new file mode 100644 index 0000000..1389252 --- /dev/null +++ b/corona_palette.h @@ -0,0 +1,73 @@ +/* + * This file is released under the GNU General Public Licence + * + * authors: + * Richard Ashbury + * Jean-Christophe Hoelt + */ + +#ifndef _PALETTE_H +#define _PALETTE_H + +#define NB_PALETTES 30 + +#include "corona_types.h" + +// PALETTE +// +typedef ColorRGB Palette[256]; +class CompressedPalette; + +// COLLECTION OF PALETTES +// +// Definition of some palettes stored in compressed format. +// +class PaletteCollection +{ + public: + PaletteCollection(const int palettes[][NB_PALETTES], int nbPalettes); + ~PaletteCollection(); + + int size() const { return m_nbPalettes; } + void expandPalette(int i, Palette dest) const; + + private: + CompressedPalette *m_cpal; + int m_nbPalettes; +}; + +// PALETTE CYCLER +// +// A class to create cycling palettes. +// +class PaletteCycler +{ + private: + Palette m_srcpal; + Palette m_destpal; + Palette m_curpal; + + PaletteCollection m_palettes; + int m_srcnum, m_destnum; + bool m_transferring; + double m_progress; + + void startPaletteTransition(); + void affectPaletteTransition(double p); + + public: + PaletteCycler(const int palettes[][NB_PALETTES], int nbPalettes); + void update(TimedLevel *pLevels); + const Palette &getPalette() const { return m_curpal; } +}; + +// PALETTE CONVERSIOn +// +// Display/Architecture specific routines to convert Palette to int[] (hardware color) +// +void paletteToRGBA(int dest[256], const Palette src); + +// BLIT A PALETTIZED SURFACE using converted palette. +void blitSurface8To32(unsigned char *byteSurf, int *colorSurf, int palette[256], int size); + +#endif diff --git a/corona_types.h b/corona_types.h new file mode 100644 index 0000000..db9a764 --- /dev/null +++ b/corona_types.h @@ -0,0 +1,37 @@ +/* + * This file is released under the GNU General Public Licence + * + * authors: + * Richard Ashbury + * Jean-Christophe Hoelt + */ + +#ifndef _CORONA_TYPES_H +#define _CORONA_TYPES_H + +#define pause_state 10 +#define normal_state 9 + +// TIMED LEVEL +// +struct TimedLevel +{ + unsigned char frequency[2][512]; + int state; + int timeStamp; + int currentTimeMs; + + int lastbeat; // filled by corona +}; + +// COLOR +// +struct ColorRGB +{ + unsigned char rgbRed; + unsigned char rgbGreen; + unsigned char rgbBlue; +}; + + +#endif diff --git a/data/gfx/0live.png b/data/gfx/0live.png new file mode 100644 index 0000000..af95c5d Binary files /dev/null and b/data/gfx/0live.png differ diff --git a/data/gfx/1live.png b/data/gfx/1live.png new file mode 100644 index 0000000..67df423 Binary files /dev/null and b/data/gfx/1live.png differ diff --git a/data/gfx/2live.png b/data/gfx/2live.png new file mode 100644 index 0000000..7e39071 Binary files /dev/null and b/data/gfx/2live.png differ diff --git a/data/gfx/3live.png b/data/gfx/3live.png new file mode 100644 index 0000000..3911346 Binary files /dev/null and b/data/gfx/3live.png differ diff --git a/data/gfx/Background.jpg b/data/gfx/Background.jpg new file mode 100644 index 0000000..0a786c5 Binary files /dev/null and b/data/gfx/Background.jpg differ diff --git a/data/gfx/BackgroundDark.jpg b/data/gfx/BackgroundDark.jpg new file mode 100644 index 0000000..684e3d1 Binary files /dev/null and b/data/gfx/BackgroundDark.jpg differ diff --git a/data/gfx/BigNeutral.png b/data/gfx/BigNeutral.png new file mode 100644 index 0000000..efc31b3 Binary files /dev/null and b/data/gfx/BigNeutral.png differ diff --git a/data/gfx/Explode1.png b/data/gfx/Explode1.png new file mode 100644 index 0000000..2d4820b Binary files /dev/null and b/data/gfx/Explode1.png differ diff --git a/data/gfx/Explode2.png b/data/gfx/Explode2.png new file mode 100644 index 0000000..87e6154 Binary files /dev/null and b/data/gfx/Explode2.png differ diff --git a/data/gfx/Explode3.png b/data/gfx/Explode3.png new file mode 100644 index 0000000..a4edbce Binary files /dev/null and b/data/gfx/Explode3.png differ diff --git a/data/gfx/Explode4.png b/data/gfx/Explode4.png new file mode 100644 index 0000000..bd48f76 Binary files /dev/null and b/data/gfx/Explode4.png differ diff --git a/data/gfx/MenuBackground.jpg b/data/gfx/MenuBackground.jpg new file mode 100644 index 0000000..5be0028 Binary files /dev/null and b/data/gfx/MenuBackground.jpg differ diff --git a/data/gfx/Neutral.png b/data/gfx/Neutral.png new file mode 100644 index 0000000..ff57b35 Binary files /dev/null and b/data/gfx/Neutral.png differ diff --git a/data/gfx/Shadow.png b/data/gfx/Shadow.png new file mode 100644 index 0000000..44d6f0c Binary files /dev/null and b/data/gfx/Shadow.png differ diff --git a/data/gfx/Shrink1.png b/data/gfx/Shrink1.png new file mode 100644 index 0000000..5d52fed Binary files /dev/null and b/data/gfx/Shrink1.png differ diff --git a/data/gfx/Shrink2.png b/data/gfx/Shrink2.png new file mode 100644 index 0000000..7053d12 Binary files /dev/null and b/data/gfx/Shrink2.png differ diff --git a/data/gfx/Shrink3.png b/data/gfx/Shrink3.png new file mode 100644 index 0000000..692394b Binary files /dev/null and b/data/gfx/Shrink3.png differ diff --git a/data/gfx/Shrink4.png b/data/gfx/Shrink4.png new file mode 100644 index 0000000..a296ab8 Binary files /dev/null and b/data/gfx/Shrink4.png differ diff --git a/data/gfx/circle.png b/data/gfx/circle.png new file mode 100644 index 0000000..010debb Binary files /dev/null and b/data/gfx/circle.png differ diff --git a/data/gfx/crosseye.png b/data/gfx/crosseye.png new file mode 100644 index 0000000..e6b0ab8 Binary files /dev/null and b/data/gfx/crosseye.png differ diff --git a/data/gfx/eye0.png b/data/gfx/eye0.png new file mode 100644 index 0000000..6d18518 Binary files /dev/null and b/data/gfx/eye0.png differ diff --git a/data/gfx/eye1.png b/data/gfx/eye1.png new file mode 100644 index 0000000..b499f1b Binary files /dev/null and b/data/gfx/eye1.png differ diff --git a/data/gfx/eye2.png b/data/gfx/eye2.png new file mode 100644 index 0000000..000a114 Binary files /dev/null and b/data/gfx/eye2.png differ diff --git a/data/gfx/font3b.png b/data/gfx/font3b.png new file mode 100644 index 0000000..8739719 Binary files /dev/null and b/data/gfx/font3b.png differ diff --git a/data/gfx/font4b.png b/data/gfx/font4b.png new file mode 100644 index 0000000..fa3867d Binary files /dev/null and b/data/gfx/font4b.png differ diff --git a/data/gfx/fontdark.png b/data/gfx/fontdark.png new file mode 100644 index 0000000..f0485da Binary files /dev/null and b/data/gfx/fontdark.png differ diff --git a/data/gfx/grid.png b/data/gfx/grid.png new file mode 100644 index 0000000..b5568a1 Binary files /dev/null and b/data/gfx/grid.png differ diff --git a/data/gfx/menusel.png b/data/gfx/menusel.png new file mode 100644 index 0000000..790769f Binary files /dev/null and b/data/gfx/menusel.png differ diff --git a/data/gfx/perso1_1.png b/data/gfx/perso1_1.png new file mode 100644 index 0000000..de8f979 Binary files /dev/null and b/data/gfx/perso1_1.png differ diff --git a/data/gfx/perso1_2.png b/data/gfx/perso1_2.png new file mode 100644 index 0000000..e07b595 Binary files /dev/null and b/data/gfx/perso1_2.png differ diff --git a/data/gfx/speed.png b/data/gfx/speed.png new file mode 100644 index 0000000..7ca71bb Binary files /dev/null and b/data/gfx/speed.png differ diff --git a/data/gfx/speed_black.png b/data/gfx/speed_black.png new file mode 100644 index 0000000..2b7d897 Binary files /dev/null and b/data/gfx/speed_black.png differ diff --git a/data/gfx/twirleye0.png b/data/gfx/twirleye0.png new file mode 100644 index 0000000..1d92e75 Binary files /dev/null and b/data/gfx/twirleye0.png differ diff --git a/data/gfx/twirleye1.png b/data/gfx/twirleye1.png new file mode 100644 index 0000000..76932a4 Binary files /dev/null and b/data/gfx/twirleye1.png differ diff --git a/data/gfx/twirleye2.png b/data/gfx/twirleye2.png new file mode 100644 index 0000000..680fc08 Binary files /dev/null and b/data/gfx/twirleye2.png differ diff --git a/data/gfx/twirleye3.png b/data/gfx/twirleye3.png new file mode 100644 index 0000000..5b406bc Binary files /dev/null and b/data/gfx/twirleye3.png differ diff --git a/data/gfx/v0.png b/data/gfx/v0.png new file mode 100644 index 0000000..b963d60 Binary files /dev/null and b/data/gfx/v0.png differ diff --git a/data/gfx/v1a.png b/data/gfx/v1a.png new file mode 100644 index 0000000..65be0d8 Binary files /dev/null and b/data/gfx/v1a.png differ diff --git a/data/gfx/v1b.png b/data/gfx/v1b.png new file mode 100644 index 0000000..867038a Binary files /dev/null and b/data/gfx/v1b.png differ diff --git a/data/gfx/v1c.png b/data/gfx/v1c.png new file mode 100644 index 0000000..5f38857 Binary files /dev/null and b/data/gfx/v1c.png differ diff --git a/data/gfx/v1d.png b/data/gfx/v1d.png new file mode 100644 index 0000000..58612a1 Binary files /dev/null and b/data/gfx/v1d.png differ diff --git a/data/gfx/v2ab.png b/data/gfx/v2ab.png new file mode 100644 index 0000000..90b622f Binary files /dev/null and b/data/gfx/v2ab.png differ diff --git a/data/gfx/v2ac.png b/data/gfx/v2ac.png new file mode 100644 index 0000000..aec0878 Binary files /dev/null and b/data/gfx/v2ac.png differ diff --git a/data/gfx/v2ad.png b/data/gfx/v2ad.png new file mode 100644 index 0000000..6e49f16 Binary files /dev/null and b/data/gfx/v2ad.png differ diff --git a/data/gfx/v2bc.png b/data/gfx/v2bc.png new file mode 100644 index 0000000..a35042d Binary files /dev/null and b/data/gfx/v2bc.png differ diff --git a/data/gfx/v2bd.png b/data/gfx/v2bd.png new file mode 100644 index 0000000..4b05f89 Binary files /dev/null and b/data/gfx/v2bd.png differ diff --git a/data/gfx/v2cd.png b/data/gfx/v2cd.png new file mode 100644 index 0000000..4c3b36e Binary files /dev/null and b/data/gfx/v2cd.png differ diff --git a/data/gfx/v3abc.png b/data/gfx/v3abc.png new file mode 100644 index 0000000..37a3384 Binary files /dev/null and b/data/gfx/v3abc.png differ diff --git a/data/gfx/v3abd.png b/data/gfx/v3abd.png new file mode 100644 index 0000000..6ab6949 Binary files /dev/null and b/data/gfx/v3abd.png differ diff --git a/data/gfx/v3acd.png b/data/gfx/v3acd.png new file mode 100644 index 0000000..749ecb6 Binary files /dev/null and b/data/gfx/v3acd.png differ diff --git a/data/gfx/v3bcd.png b/data/gfx/v3bcd.png new file mode 100644 index 0000000..8466bec Binary files /dev/null and b/data/gfx/v3bcd.png differ diff --git a/data/gfx/v4abcd.png b/data/gfx/v4abcd.png new file mode 100644 index 0000000..2cb2e93 Binary files /dev/null and b/data/gfx/v4abcd.png differ diff --git a/data/sfx/._bi b/data/sfx/._bi new file mode 100644 index 0000000..087061a Binary files /dev/null and b/data/sfx/._bi differ diff --git a/data/sfx/applose.wav b/data/sfx/applose.wav new file mode 100644 index 0000000..990b4d5 Binary files /dev/null and b/data/sfx/applose.wav differ diff --git a/data/sfx/bam1.wav b/data/sfx/bam1.wav new file mode 100644 index 0000000..06785df Binary files /dev/null and b/data/sfx/bam1.wav differ diff --git a/data/sfx/bim1.wav b/data/sfx/bim1.wav new file mode 100644 index 0000000..f5dbc13 Binary files /dev/null and b/data/sfx/bim1.wav differ diff --git a/data/sfx/bim2.wav b/data/sfx/bim2.wav new file mode 100644 index 0000000..f6fb6df Binary files /dev/null and b/data/sfx/bim2.wav differ diff --git a/data/sfx/fff.wav b/data/sfx/fff.wav new file mode 100644 index 0000000..5cbb793 Binary files /dev/null and b/data/sfx/fff.wav differ diff --git a/data/sfx/flobopuyo_game1.xm b/data/sfx/flobopuyo_game1.xm new file mode 100644 index 0000000..e51d0cf Binary files /dev/null and b/data/sfx/flobopuyo_game1.xm differ diff --git a/data/sfx/flobopuyo_game2.xm b/data/sfx/flobopuyo_game2.xm new file mode 100644 index 0000000..256e37e Binary files /dev/null and b/data/sfx/flobopuyo_game2.xm differ diff --git a/data/sfx/flobopuyo_gameover.xm b/data/sfx/flobopuyo_gameover.xm new file mode 100644 index 0000000..4a0e211 Binary files /dev/null and b/data/sfx/flobopuyo_gameover.xm differ diff --git a/data/sfx/flobopuyo_menu.xm b/data/sfx/flobopuyo_menu.xm new file mode 100644 index 0000000..1ad0e83 Binary files /dev/null and b/data/sfx/flobopuyo_menu.xm differ diff --git a/data/sfx/pastaga.wav b/data/sfx/pastaga.wav new file mode 100644 index 0000000..b4af100 Binary files /dev/null and b/data/sfx/pastaga.wav differ diff --git a/data/sfx/pop.wav b/data/sfx/pop.wav new file mode 100644 index 0000000..ab6d5d1 Binary files /dev/null and b/data/sfx/pop.wav differ diff --git a/data/sfx/remove.wav b/data/sfx/remove.wav new file mode 100644 index 0000000..971a4b0 Binary files /dev/null and b/data/sfx/remove.wav differ diff --git a/data/sfx/slide.wav b/data/sfx/slide.wav new file mode 100644 index 0000000..253d568 Binary files /dev/null and b/data/sfx/slide.wav differ diff --git a/data/sfx/splash1.wav b/data/sfx/splash1.wav new file mode 100644 index 0000000..936bbb8 Binary files /dev/null and b/data/sfx/splash1.wav differ diff --git a/data/sfx/splash2.wav b/data/sfx/splash2.wav new file mode 100644 index 0000000..7eea2c2 Binary files /dev/null and b/data/sfx/splash2.wav differ diff --git a/data/sfx/splash3.wav b/data/sfx/splash3.wav new file mode 100644 index 0000000..2ce7c94 Binary files /dev/null and b/data/sfx/splash3.wav differ diff --git a/data/sfx/splash4.wav b/data/sfx/splash4.wav new file mode 100644 index 0000000..54070ed Binary files /dev/null and b/data/sfx/splash4.wav differ diff --git a/data/sfx/splash5.wav b/data/sfx/splash5.wav new file mode 100644 index 0000000..38c88cb Binary files /dev/null and b/data/sfx/splash5.wav differ diff --git a/data/sfx/splash6.wav b/data/sfx/splash6.wav new file mode 100644 index 0000000..d540905 Binary files /dev/null and b/data/sfx/splash6.wav differ diff --git a/data/sfx/splash7.wav b/data/sfx/splash7.wav new file mode 100644 index 0000000..9cd4732 Binary files /dev/null and b/data/sfx/splash7.wav differ diff --git a/data/sfx/splash8.wav b/data/sfx/splash8.wav new file mode 100644 index 0000000..401f2fb Binary files /dev/null and b/data/sfx/splash8.wav differ diff --git a/data/sfx/strange_fear.xm b/data/sfx/strange_fear.xm new file mode 100644 index 0000000..6431420 Binary files /dev/null and b/data/sfx/strange_fear.xm differ diff --git a/data/sfx/strange_fear2.xm b/data/sfx/strange_fear2.xm new file mode 100644 index 0000000..15fe45a Binary files /dev/null and b/data/sfx/strange_fear2.xm differ diff --git a/data/sfx/strange_gameover.xm b/data/sfx/strange_gameover.xm new file mode 100644 index 0000000..ca56d3e Binary files /dev/null and b/data/sfx/strange_gameover.xm differ diff --git a/data/sfx/woho.wav b/data/sfx/woho.wav new file mode 100644 index 0000000..f895914 Binary files /dev/null and b/data/sfx/woho.wav differ diff --git a/data/sfx/woo.wav b/data/sfx/woo.wav new file mode 100644 index 0000000..61c3f66 Binary files /dev/null and b/data/sfx/woo.wav differ diff --git a/data/sfx/yahoohoo.wav b/data/sfx/yahoohoo.wav new file mode 100644 index 0000000..d2d69ac Binary files /dev/null and b/data/sfx/yahoohoo.wav differ diff --git a/data/story/intro.txt b/data/story/intro.txt new file mode 100644 index 0000000..4d02409 --- /dev/null +++ b/data/story/intro.txt @@ -0,0 +1,16 @@ +<-- Game Introduction --> + +foreground "earth.png" +background "stars.jpg" +vector -50,-50 +textarea 35,35,387,94,30 + +say "Earth!!\nHere we are!\n..." +wait 5 sec + +textarea 35,35,387,64,30 +say "This will be a nice invasion!\nFor sure..." + +<-- say "I'm a bad guy...\nDefeat me.... OR DIE !!!\n\nAH AH AH!!!" wait 5 sec say "Do you wanna die or what ?\nCome on Come on!!" wait 10 sec background "nuages1.png" textarea 325,389,280,74,30 say "Oh, I love a nice cloudy sky.\nThat's nice isn't it??\n\nReady to play ?" --> + +loop diff --git a/glSDL.c b/glSDL.c new file mode 100644 index 0000000..71ba39d --- /dev/null +++ b/glSDL.c @@ -0,0 +1,1858 @@ +/*(LGPL) +------------------------------------------------------------ + glSDL 0.6 - SDL 2D API on top of OpenGL +------------------------------------------------------------ + * (c) David Olofson, 2001-2003 + * This code is released under the terms of the GNU LGPL. + */ + +#define _GLSDL_NO_REDEFINES_ +#include "glSDL.h" + +#ifdef HAVE_OPENGL + +/* + * Note that this will result in whining about + * TexInfo 0 being leaked, as the checking is + * done before the screen is closed. Ignore. :-) + */ +/* #define LEAK_TRACKING */ + +#define DBG(x) /*error messages, warnings*/ +#define DBG2(x) /*texture allocation*/ +#define DBG3(x) /*chopping/tiling*/ +#define DBG4(x) /*texture uploading*/ + +/*#define CKSTATS*/ /*colorkey statistics*/ +/*#define FAKE_MAXTEXSIZE 256*/ + +#include +#include +#include + +#define HAS_SDL_OPENGL_H 1 + +#if HAS_SDL_OPENGL_H +#include "SDL_opengl.h" +#else +#ifdef WIN32 +#include +#endif +#if defined(__APPLE__) && defined(__MACH__) +#include +#include +#else +#include +#include +#endif +#endif + +#define USING_GLSDL (IS_GLSDL_SURFACE(SDL_GetVideoSurface())) + +#define MAX_TEXINFOS 16384 + +static glSDL_TexInfo **texinfotab = NULL; +static GLint maxtexsize = 256; +static SDL_PixelFormat _RGBfmt, _RGBAfmt; +static int screen_was_locked = 0; + +static void _UnloadTexture(glSDL_TexInfo *txi); + +static int scale = 1; + +static SDL_Surface *fake_screen = NULL; + + +static int _glSDL_BlitGL(SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect); + + +DBG(static void _print_glerror(int point) +{ + const char *err = ""; + switch(glGetError()) + { + case GL_NO_ERROR: + return; + case GL_INVALID_ENUM: + err = "GL_INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + err = "GL_INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + err = "GL_INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + err = "GL_STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + err = "GL_STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + err = "GL_OUT_OF_MEMORY"; + break; + default: + err = ""; + break; + } + fprintf(stderr,"OpenGL error \"%s\" at point %d.\n", err, point); +}) + + +/* Get texinfo for a surface. */ +glSDL_TexInfo *glSDL_GetTexInfo(SDL_Surface *surface) +{ + if(!surface) + return NULL; + if(!texinfotab) + return NULL; + if(!surface->unused1) + return NULL; + if(surface->unused1 > MAX_TEXINFOS) + return NULL; + + return texinfotab[surface->unused1 - 1]; +} + + +/* Allocate a "blank" texinfo for a suface. */ +glSDL_TexInfo *glSDL_AllocTexInfo(SDL_Surface *surface) +{ + int handle, i; + glSDL_TexInfo *txi; + if(!surface) + return NULL; + + if(!texinfotab) + { + texinfotab = (glSDL_TexInfo**)calloc(MAX_TEXINFOS, sizeof(glSDL_TexInfo *)); + if(!texinfotab) + return NULL; + } + + txi = glSDL_GetTexInfo(surface); + if(txi) + return txi; /* There already is one! --> */ + + /* Find a free handle... */ + handle = -1; + for(i = 0; i < MAX_TEXINFOS; ++i) + if(NULL == texinfotab[i]) + { + handle = i; + break; + } + + if(handle < 0) + { + DBG(fprintf(stderr, "glSDL: Out of handles!\n")); + return NULL; + } + + /* ...and hook a new texinfo struct up to it. */ + texinfotab[handle] = (glSDL_TexInfo*)calloc(1, sizeof(glSDL_TexInfo)); + if(!texinfotab[handle]) + return NULL; + + /* Connect the surface to the new TexInfo. */ + surface->unused1 = (Uint32)handle + 1; + + DBG2(fprintf(stderr, "glSDL: Allocated TexInfo %d.\n", handle)); + + return texinfotab[handle]; +} + + +static void _FreeTexInfo(Uint32 handle) +{ + if(handle >= MAX_TEXINFOS) + return; + if(!texinfotab[handle]) + return; + + _UnloadTexture(texinfotab[handle]); + texinfotab[handle]->textures = 0; + free(texinfotab[handle]->texture); + texinfotab[handle]->texture = NULL; + free(texinfotab[handle]); + texinfotab[handle] = NULL; + DBG2(fprintf(stderr, "glSDL: Freed TexInfo %d.\n", handle)); +} + + +/* Detach and free the texinfo of a surface. */ +void glSDL_FreeTexInfo(SDL_Surface *surface) +{ + if(!glSDL_GetTexInfo(surface)) + return; + + _FreeTexInfo(surface->unused1 - 1); + GLSDL_FIX_SURFACE(surface); +} + + +/* + * Calculate chopping/tiling of a surface to + * fit it into the smallest possible OpenGL + * texture. + */ +static int _CalcChop(glSDL_TexInfo *txi) +{ + int rows, vw, vh; + int vertical = 0; + int texsize; + int lastw, lasth, minsize; + + vw = txi->virt.w; + vh = txi->virt.h; + + DBG3(fprintf(stderr, "w=%d, h=%d ", vw, vh)); + if(vh > vw) + { + int t = vw; + vw = vh; + vh = t; + vertical = 1; + DBG3(fprintf(stderr, "(vertical) \t")); + } + + /* + * Check whether this is a "huge" surface - at least one dimension + * must be <= than the maximum texture size, or we'll have to chop + * in both directions. + */ + if(vh > maxtexsize) + { + fprintf(stderr, "glSDL: \"Huge\" surfaces not yet supported!\n"); + return 0; + } + + /* Calculate minimum size */ + rows = 1; + lastw = vw; + lasth = vh; + minsize = lastw > lasth ? lastw : lasth; + while(1) + { + int w, h, size; + ++rows; + w = vw / rows; + h = rows * vh; + size = w > h ? w : h; + if(size >= minsize) + { + --rows; + break; + } + lastw = w; + lasth = h; + minsize = size; + } + if(minsize > maxtexsize) + { + /* Handle multiple textures for very wide/tall surfaces. */ + minsize = maxtexsize; + rows = (vw + minsize-1) / minsize; + } + DBG3(fprintf(stderr, "==> minsize=%d ", minsize)); + DBG3(fprintf(stderr, "(rows=%d) \t", rows)); + + /* Recalculate with nearest higher power-of-2 width. */ + for(texsize = 1; texsize < minsize; texsize <<= 1) + ; + txi->texsize = texsize; + rows = (vw + texsize-1) / texsize; + DBG3(fprintf(stderr, "==> texsize=%d (rows=%d) \t", texsize, rows)); + + /* Calculate number of tiles per texture */ + txi->tilespertex = txi->texsize / vh; + DBG3(fprintf(stderr, "tilespertex=%d \t", txi->tilespertex)); + + /* Calculate number of textures needed */ + txi->textures = (rows + txi->tilespertex-1) / txi->tilespertex; + txi->texture = (int*)malloc(txi->textures * sizeof(int)); + memset(txi->texture, -1, txi->textures * sizeof(int)); + DBG3(fprintf(stderr, "textures=%d, ", txi->textures)); + if(!txi->texture) + { + fprintf(stderr, "glSDL: INTERNAL ERROR: Failed to allocate" + " texture name table!\n"); + return -2; + } + + /* Set up tile size. (Only one axis supported here!) */ + if(1 == rows) + { + txi->tilemode = GLSDL_TM_SINGLE; + if(vertical) + { + txi->tilew = vh; + txi->tileh = vw; + } + else + { + txi->tilew = vw; + txi->tileh = vh; + } + } + else if(vertical) + { + txi->tilemode = GLSDL_TM_VERTICAL; + txi->tilew = vh; + txi->tileh = texsize; + } + else + { + txi->tilemode = GLSDL_TM_HORIZONTAL; + txi->tilew = texsize; + txi->tileh = vh; + } + + DBG3(fprintf(stderr, "tilew=%d, tileh=%d\n", txi->tilew, txi->tileh)); + return 0; +} + + +/* +TODO: + * Keep a list or tree of free texture space rectangles. + + * The space manager should actively try to merge + rectangles from the same texture. + +static int _AllocTexSpace(glSDL_TexInfo *txi) +{ + Make sure that _CalcChop() has been called. + + Try to find the smallest free area (one or more + tiles) in a single texture that would fit all + tiles of this surface. + + if that fails + { + Allocate a new texture of suitable size. + if that fails + { + Try to allocate space for each tile separately. + if that fails + return error --> + } + } + + Allocate the areas and link them to the texinfo. + return success --> +} +*/ + + +/* Add a glSDL_TexInfo struct to an SDL_Surface */ +static int glSDL_AddTexInfo(SDL_Surface *surface) +{ + glSDL_TexInfo *txi; + + if(!surface) + return -1; + if(IS_GLSDL_SURFACE(surface)) + return 0; /* Do nothing */ + + glSDL_AllocTexInfo(surface); + txi = glSDL_GetTexInfo(surface); + if(!txi) + return -2; /* Oops! Didn't get a texinfo... --> */ + + txi->virt.w = txi->lw = surface->w; + txi->virt.h = txi->lh = surface->h; + + if(_CalcChop(txi) < 0) + return -3; + + SDL_SetClipRect(surface, &txi->virt); + + return 0; +} + + +/* Create a surface of the prefered OpenGL RGB texture format */ +static SDL_Surface *_CreateRGBSurface(int w, int h) +{ + SDL_Surface *s; + Uint32 rmask, gmask, bmask; + int bits = 24; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0x00ff0000; + gmask = 0x0000ff00; + bmask = 0x000000ff; +#else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; +#endif + s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, + bits, rmask, gmask, bmask, 0); + if(s) + GLSDL_FIX_SURFACE(s); + + glSDL_AddTexInfo(s); + return s; +} + + +/* Create a surface of the prefered OpenGL RGBA texture format */ +static SDL_Surface *_CreateRGBASurface(int w, int h) +{ + SDL_Surface *s; + Uint32 rmask, gmask, bmask, amask; + int bits = 32; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; +#else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; +#endif + s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, + bits, rmask, gmask, bmask, amask); + if(s) + GLSDL_FIX_SURFACE(s); + + glSDL_AddTexInfo(s); + return s; +} + + +static void _init_formats(void) +{ + SDL_Surface *s = _CreateRGBSurface(1, 1); + if(!s) + return; + _RGBfmt = *(s->format); + glSDL_FreeSurface(s); + + s = _CreateRGBASurface(1, 1); + if(!s) + return; + _RGBAfmt = *(s->format); + glSDL_FreeSurface(s); +} + + +static int _FormatIsOk(SDL_Surface *surface) +{ + SDL_PixelFormat *pf; + if(!surface) + return 1; /* Well, there ain't much we can do anyway... */ + + pf = surface->format; + + /* Colorkeying requires an alpha channel! */ + if(surface->flags & SDL_SRCCOLORKEY) + if(!pf->Amask) + return 0; + + /* We need pitch == (width * BytesPerPixel) for glTex[Sub]Image2D() */ + if(surface->pitch != (surface->w * pf->BytesPerPixel)) + return 0; + + if(pf->Amask) + { + if(pf->BytesPerPixel != _RGBAfmt.BytesPerPixel) + return 0; + if(pf->Rmask != _RGBAfmt.Rmask) + return 0; + if(pf->Gmask != _RGBAfmt.Gmask) + return 0; + if(pf->Bmask != _RGBAfmt.Bmask) + return 0; + if(pf->Amask != _RGBAfmt.Amask) + return 0; + } + else + { + if(pf->BytesPerPixel != _RGBfmt.BytesPerPixel) + return 0; + if(pf->Rmask != _RGBfmt.Rmask) + return 0; + if(pf->Gmask != _RGBfmt.Gmask) + return 0; + if(pf->Bmask != _RGBfmt.Bmask) + return 0; + } + return 1; +} + + + +static void _key2alpha(SDL_Surface *surface) +{ + int x, y; +#ifdef CKSTATS + int transp = 0; +#endif + Uint32 ckey = surface->format->colorkey; + if(SDL_LockSurface(surface) < 0) + return; + + for(y = 0; y < surface->h; ++y) + { + Uint32 *px = (Uint32 *)((char *)surface->pixels + y*surface->pitch); + for(x = 0; x < surface->w; ++x) + if(px[x] == ckey) + { + px[x] = 0; +#ifdef CKSTATS + ++transp; +#endif + } + } +#ifdef CKSTATS + printf("glSDL: key2alpha(); %dx%d surface, %d opaque pixels.\n", + surface->w, surface->h, + surface->w * surface->h - transp); +#endif + SDL_UnlockSurface(surface); +} + + + +/*---------------------------------------------------------- + SDL style API +----------------------------------------------------------*/ + +static void _KillAllTextures(void) +{ + if(texinfotab) + { + unsigned i; +#ifdef LEAK_TRACKING + int leaked = 0; + for(i = 0; i < MAX_TEXINFOS; ++i) + if(texinfotab[i]) + { + ++leaked; + fprintf(stderr, "glSDL: Leaked TexInfo" + " %d! (%dx%d)\n", + i, + texinfotab[i]->virt.w, + texinfotab[i]->virt.h + ); + } + if(leaked) + fprintf(stderr, "glSDL: Leaked %d TexInfos!\n", leaked); +#endif + for(i = 0; i < MAX_TEXINFOS; ++i) + _FreeTexInfo(i); + free(texinfotab); + texinfotab = NULL; + } +} + +void glSDL_Quit(void) +{ + if(SDL_WasInit(SDL_INIT_VIDEO)) + { + SDL_Surface *screen = SDL_GetVideoSurface(); + glSDL_FreeTexInfo(screen); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + if(fake_screen) + { + glSDL_FreeTexInfo(fake_screen); + free(fake_screen); + fake_screen = NULL; + } + } +#ifndef LEAK_TRACKING + _KillAllTextures(); +#endif +} + + +void _glSDL_FullQuit(void) +{ +#ifdef LEAK_TRACKING + _KillAllTextures(); +#endif + glSDL_Quit(); + SDL_Quit(); +} + + +void glSDL_QuitSubSystem(Uint32 flags) +{ + if(flags & SDL_INIT_VIDEO) + glSDL_Quit(); + SDL_QuitSubSystem(flags); +} + + +SDL_Surface *glSDL_SetVideoMode(int width, int height, int bpp, Uint32 flags) +{ + SDL_Surface *screen; + GLint gl_doublebuf; + if(!(flags & SDL_GLSDL)) + { + screen = SDL_SetVideoMode(width, height, bpp, flags); + if(screen) + GLSDL_FIX_SURFACE(screen); + return screen; + } + + if((SDL_Linked_Version()->major <= 1) && + (SDL_Linked_Version()->minor <= 2) && + (SDL_Linked_Version()->patch < 5)) + fprintf(stderr, "glSDL WARNING: Using SDL version" + " 1.2.5 or later is strongly" + " recommended!\n"); + +/* + * FIXME: Here's the place to insert proper handling of this call being + * used for resizing the window... For now, just make sure we + * don't end up with invalid texinfos and stuff no matter what. + */ + _KillAllTextures(); + + /* Remove flag to avoid confusion inside SDL - just in case! */ + flags &= ~SDL_GLSDL; + + flags |= SDL_OPENGL; + if(bpp == 15) + { + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + } + else if(bpp == 16) + { + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + } + else if(bpp >= 24) + { + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + } + gl_doublebuf = flags & SDL_DOUBLEBUF; + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, gl_doublebuf); + + DBG(printf("desired = %d x %d\n", width, height);) + scale = 1; + while((width*scale < 640) && (height*scale < 480)) + ++scale; + DBG(printf("real = %d x %d\n", width*scale, height*scale);) + + screen = SDL_SetVideoMode(width*scale, height*scale, bpp, flags); + if(!screen) + return NULL; + + GLSDL_FIX_SURFACE(screen); + +#ifdef FAKE_MAXTEXSIZE + maxtexsize = FAKE_MAXTEXSIZE; +#else + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize); +#endif + DBG(fprintf(stderr, "glSDL: Max texture size: %d\n", maxtexsize)); + + _init_formats(); + + if(glSDL_AddTexInfo(screen) < 0) + { + DBG(fprintf(stderr, "glSDL: Failed to add info to screen surface!\n")); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + return NULL; + } + + glSDL_SetLogicSize(screen, width*scale, height*scale); + + /* + * Create a software shadow buffer of the requested size. + * This is used for blit-from-screen and simulation of + * direct software rendering. (Dog slow crap. It's only + * legitimate use is probably screen shots.) + */ + fake_screen = _CreateRGBSurface(screen->w / scale, + screen->h / scale); + return fake_screen; +} + + +SDL_Surface *glSDL_GetVideoSurface(void) +{ + if(fake_screen) + return fake_screen; + else + return SDL_GetVideoSurface(); +} + + +void glSDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects) +{ + if(IS_GLSDL_SURFACE(screen)) + { +/* if(screen_was_locked) + { + int i; + for(i = 0; i < numrects; ++i) + _glSDL_BlitGL(fake_screen, rects + i, + vs, rects + i); + } + else +*/ + glSDL_Flip(screen); + } + else + SDL_UpdateRects(screen, numrects, rects); + screen_was_locked = 0; +} + + +void glSDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h) +{ + SDL_Rect r; + r.x = x; + r.y = y; + r.w = w; + r.h = h; + glSDL_UpdateRects(screen, 1, &r); +} + + +int glSDL_Flip(SDL_Surface *screen) +{ + if(!IS_GLSDL_SURFACE(screen)) + return SDL_Flip(screen); +/* +TODO: Perform all rendering here, after globally reordering/optimizing + all non-overlapping operations for the frame. (Reduce texture + changes, blend mode changes etc...) + +Disadvantage: + Won't mix well with user OpenGL calls - but then again, glSDL + isn't meant for OpenGL aware applications in the first place! +*/ + /* + * Some XFree86 DRI drivers won't sync *at all* + * without glFinish()! You may end up with commands + * for several frames buffered up before any actual + * rendering is done - and then, your program will + * stall until most of the rendering is completed. + * + * (Of course, this wouldn't be much of an issue + * if the drivers did retrace sync'ed flips, but as + * most of the drivers don't, there's no way ever an + * application is going to get smooth animation + * without this kludge.) + * + * Update: That bl**dy *DRIVER* should be fixed! + * We don't need this performance killing + * kludge. (I know for sure that my current + * driver doesn't have this problem.) + */ +#ifdef STUPID_GL_WORKAROUND + glFlush(); /* Just in case. *heh* */ + SDL_GL_SwapBuffers(); + glFinish(); /* And here we kill parallel execution... :-( */ +#else + SDL_GL_SwapBuffers(); +#endif + return 0; +} + + +void glSDL_FreeSurface(SDL_Surface *surface) +{ + if(!surface) + return; + glSDL_FreeTexInfo(surface); + SDL_FreeSurface(surface); +} + + +int glSDL_LockSurface(SDL_Surface *surface) +{ + if(!surface) + return 0; + + if(IS_GLSDL_SURFACE(surface)) + { + if((surface == fake_screen) || + (SDL_GetVideoSurface() == surface)) + { + if(scale > 1) + return -1; + + glSDL_Invalidate(fake_screen, NULL); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, + fake_screen->pitch / + fake_screen->format->BytesPerPixel); + + glReadPixels(0, 0, fake_screen->w, fake_screen->h, + GL_RGB, GL_UNSIGNED_BYTE, + fake_screen->pixels); + return 0; + } + else + { + glSDL_Invalidate(surface, NULL); + return SDL_LockSurface(surface); + } + } + else + return SDL_LockSurface(surface); +} + + +void glSDL_UnlockSurface(SDL_Surface *surface) +{ + if(!surface) + return; + + if(IS_GLSDL_SURFACE(surface)) + { + glSDL_UploadSurface(surface); + if((surface == fake_screen) || + (SDL_GetVideoSurface() == surface)) + _glSDL_BlitGL(fake_screen, NULL, + SDL_GetVideoSurface(), NULL); + } + else + SDL_UnlockSurface(surface); +} + + +int glSDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key) +{ + int res = SDL_SetColorKey(surface, flag, key); + if(res < 0) + return res; + /* + * If an application does this *after* SDL_DisplayFormat, + * we're basically screwed, unless we want to do an + * in-place surface conversion hack here. + * + * What we do is just kill the glSDL texinfo... No big + * deal in most cases, as glSDL only converts once anyway, + * *unless* you keep modifying the surface. + */ + if(IS_GLSDL_SURFACE(surface)) + glSDL_FreeTexInfo(surface); + return res; +} + + +int glSDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha) +{ + /* + * This is just parameters to OpenGL, so the actual + * "work" is done in glSDL_BlitSurface(). + */ + return SDL_SetAlpha(surface, flag, alpha); +} + + +SDL_bool glSDL_SetClipRect(SDL_Surface *surface, SDL_Rect *rect) +{ + SDL_bool res; + SDL_Surface *screen; + SDL_Rect fsr; + + if(!surface) + return SDL_FALSE; + + screen = SDL_GetVideoSurface(); + + res = SDL_SetClipRect(surface, rect); + if(!res) + return SDL_FALSE; + + if(!rect) + { + fsr.x = 0; + fsr.y = 0; + fsr.w = screen->w; + fsr.h = screen->h; + rect = &fsr; + } + + if(surface == fake_screen) + { + SDL_Rect r; + r.x = rect->x * scale; + r.y = rect->y * scale; + r.w = rect->w * scale; + r.h = rect->h * scale; + surface = screen; + SDL_SetClipRect(surface, rect); + } + + if( (screen == surface) && + IS_GLSDL_SURFACE(surface) ) + { + float xscale, yscale; + glSDL_TexInfo *txi = glSDL_GetTexInfo(surface); + rect = &surface->clip_rect; + glViewport( rect->x * scale, + screen->h - (rect->y + rect->h) * scale, + rect->w * scale, + rect->h * scale); + /* + * Note that this projection is upside down in + * relation to the OpenGL coordinate system. + */ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + xscale = (float)txi->lw / (float)surface->w; + yscale = (float)txi->lh / (float)surface->h; + glOrtho( xscale*(float)rect->x, + xscale*(float)(rect->w+rect->x), + yscale*(float)(rect->h+rect->y), + yscale*(float)rect->y, + -1.0, 1.0); + return SDL_TRUE; + } + return res; +} + + +static struct +{ + int do_blend; + int do_texture; + GLint texture; + GLenum sfactor, dfactor; +} glstate; + +static void gl_reset(void) +{ + glstate.do_blend = -1; + glstate.do_blend = -1; + glstate.texture = -1; + glstate.sfactor = 0xffffffff; + glstate.dfactor = 0xffffffff; +} + +static __inline__ void gl_do_blend(int on) +{ + if(glstate.do_blend == on) + return; + + if(on) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + glstate.do_blend = on; +} + +static __inline__ void gl_do_texture(int on) +{ + if(glstate.do_texture == on) + return; + + if(on) + glEnable(GL_TEXTURE_2D); + else + glDisable(GL_TEXTURE_2D); + glstate.do_texture = on; +} + +static __inline__ void gl_blendfunc(GLenum sfactor, GLenum dfactor) +{ + if((sfactor == glstate.sfactor) && (dfactor == glstate.dfactor)) + return; + + glBlendFunc(sfactor, dfactor); + + glstate.sfactor = sfactor; + glstate.dfactor = dfactor; +} + +static __inline__ void gl_texture(GLuint tx) +{ + if(tx == glstate.texture) + return; + + glBindTexture(GL_TEXTURE_2D, tx); + glstate.texture = tx; +} + + +static int _glSDL_BlitFromGL(SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect) +{ + int i, sy0, dy0; + SDL_Rect sr, dr; + + if(scale > 1) + return -1; +/* +FIXME: Some clipping, perhaps...? :-) +*/ + /* In case the destination has an OpenGL texture... */ + glSDL_Invalidate(dst, dstrect); + + /* Abuse the fake screen buffer a little. */ + glPixelStorei(GL_UNPACK_ROW_LENGTH, fake_screen->pitch / + fake_screen->format->BytesPerPixel); + if(srcrect) + glReadPixels(srcrect->x, srcrect->y, srcrect->w, srcrect->h, + GL_RGB, GL_UNSIGNED_BYTE, fake_screen->pixels); + else + glReadPixels(0, 0, fake_screen->w, fake_screen->h, + GL_RGB, GL_UNSIGNED_BYTE, fake_screen->pixels); + + /* Blit to the actual target! (Vert. flip... Uuurgh!) */ + if(srcrect) + sr = *srcrect; + else + { + sr.x = sr.y = 0; + sr.w = dst->w; + } + + if(dstrect) + dr = *dstrect; + else + dr.x = dr.y = 0; + + i = srcrect->h; + sy0 = srcrect->y; + dy0 = dstrect->y + dstrect->h - 1; + while(i--) + { + sr.y = sy0 + i; + dr.y = dy0 - i; + sr.h = 1; + if(SDL_BlitSurface(fake_screen, &sr, dst, &dr) < 0) + return -1; + } + return 0; +} + + +static __inline__ void _BlitGL_single(glSDL_TexInfo *txi, + float x1, float y1, float x2, float y2, + int dx1, float dy1, float dx2, float dy2, + unsigned char alpha, float texscale) +{ + /* Select texture */ + if(!txi->textures) + return; + if(-1 == txi->texture[0]) + return; + gl_texture(txi->texture[0]); + + glBegin(GL_QUADS); + glColor4ub(255, 255, 255, alpha); + glTexCoord2f(x1, y1); + glVertex2i(dx1, dy1); + glTexCoord2f(x2, y1); + glVertex2i(dx2, dy1); + glTexCoord2f(x2, y2); + glVertex2i(dx2, dy2); + glTexCoord2f(x1, y2); + glVertex2i(dx1, dy2); + glEnd(); +} + +static void _BlitGL_htile(glSDL_TexInfo *txi, + float x1, float y1, float x2, float y2, + int dx1, float dy1, float dx2, float dy2, + unsigned char alpha, float texscale) +{ + float tileh = (float)txi->tileh * texscale; + float tile = floor(x1); /* / 1.0 */ + float texsize = (float)txi->texsize; + int tex = (int)tile / txi->tilespertex; + float yo = ((int)tile % txi->tilespertex) * tileh; + + /* Select texture */ + if(tex >= txi->textures) + return; + if(-1 == txi->texture[tex]) + return; + gl_texture(txi->texture[tex]); + + glBegin(GL_QUADS); + while(1) + { + int thisdx1, thisdx2; + float thisx1 = x1 - tile; + float thisx2 = x2 - tile; + + /* Stop condition */ + if(tile >= x2) + break; + + /* Maybe select next texture? */ + if(yo + tileh > 1.0) + { + ++tex; + glEnd(); + if(tex >= txi->textures) + return; + if(-1 == txi->texture[tex]) + return; + gl_texture(txi->texture[tex]); + yo = 0.0; + glBegin(GL_QUADS); + } + + /* Left clip to current tile */ + if(thisx1 < 0.0) + { + thisdx1 = dx1 - (int)(thisx1 * texsize); + thisx1 = 0.0; + } + else + thisdx1 = dx1; + + /* Right clip to current tile */ + if(thisx2 > 1.0) + { + thisdx2 = dx2 - (int)((thisx2 - 1.0) * texsize); + thisx2 = 1.0; + } + else + thisdx2 = dx2; + + glColor4ub(255, 255, 255, alpha); + glTexCoord2f(thisx1, yo + y1); + glVertex2i(thisdx1, dy1); + glTexCoord2f(thisx2, yo + y1); + glVertex2i(thisdx2, dy1); + glTexCoord2f(thisx2, yo + y2); + glVertex2i(thisdx2, dy2); + glTexCoord2f(thisx1, yo + y2); + glVertex2i(thisdx1, dy2); + + tile += 1.0; + yo += tileh; + } + glEnd(); +} + +static void _BlitGL_vtile(glSDL_TexInfo *txi, + float x1, float y1, float x2, float y2, + int dx1, float dy1, float dx2, float dy2, + unsigned char alpha, float texscale) +{ + float tilew = (float)txi->tilew * texscale; + float tile = floor(y1); + float texsize = (float)txi->texsize; + float xo = tile * tilew; + int tex = ((int)tile * txi->tilew + txi->tilew-1) / txi->texsize; + + /* Select texture */ + if(tex >= txi->textures) + return; + if(-1 == txi->texture[tex]) + return; + gl_texture(txi->texture[tex]); + + glBegin(GL_QUADS); + while(1) + { + int newtex; + int thisdy1, thisdy2; + float thisy1 = y1 - tile; + float thisy2 = y2 - tile; + + /* Stop condition */ + if(tile >= y2) + break; + + /* Maybe select next texture? */ + newtex = ((int)tile * txi->tilew + txi->tilew-1) / + txi->texsize; + if(newtex != tex) + { + tex = newtex; + glEnd(); + if(tex >= txi->textures) + return; + if(-1 == txi->texture[tex]) + return; + gl_texture(txi->texture[tex]); + xo = 0.0; + glBegin(GL_QUADS); + } + + /* Left clip to current tile */ + if(thisy1 < 0.0) + { + thisdy1 = dy1 - (int)(thisy1 * texsize); + thisy1 = 0.0; + } + else + thisdy1 = dy1; + + /* Right clip to current tile */ + if(thisy2 > 1.0) + { + thisdy2 = dy2 - (int)((thisy2 - 1.0) * texsize); + thisy2 = 1.0; + } + else + thisdy2 = dy2; + + glColor4ub(255, 255, 255, alpha); + glTexCoord2f(xo + x1, thisy1); + glVertex2i(dx1, thisdy1); + glTexCoord2f(xo + x2, thisy1); + glVertex2i(dx2, thisdy1); + glTexCoord2f(xo + x2, thisy2); + glVertex2i(dx2, thisdy2); + glTexCoord2f(xo + x1, thisy2); + glVertex2i(dx1, thisdy2); + + tile += 1.0; + xo += tilew; + } + glEnd(); +} + +static int _glSDL_BlitGL(SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect) +{ + glSDL_TexInfo *txi; + float x1, y1, x2, y2; + int dx1, dy1, dx2, dy2; + float texscale; + unsigned char alpha; + if(!src || !dst) + return -1; + + /* Cull off-screen blits. */ + if(dstrect) + { + if(dstrect->x > dst->w) + return 0; + if(dstrect->y > dst->h) + return 0; + if(srcrect) + { + if(dstrect->x + srcrect->w < 0) + return 0; + if(dstrect->y + srcrect->h < 0) + return 0; + } + else + { + if(dstrect->x + src->w < 0) + return 0; + if(dstrect->y + src->h < 0) + return 0; + } + } + + /* Make sure we have a source with a valid texture */ + glSDL_UploadSurface(src); + txi = glSDL_GetTexInfo(src); + + /* Set up blending */ + if(src->flags & (SDL_SRCALPHA | SDL_SRCCOLORKEY)) + { + gl_blendfunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_do_blend(1); + } + else + gl_do_blend(0); + + /* Enable texturing */ + gl_do_texture(1); + + /* Calculate texcoords */ + if(!srcrect) + srcrect = &txi->virt; + texscale = 1.0 / (float)txi->texsize; + x1 = (float)srcrect->x * texscale; + y1 = (float)srcrect->y * texscale; + x2 = (float)(srcrect->x+srcrect->w) * texscale; + y2 = (float)(srcrect->y+srcrect->h) * texscale; + + /* Calculate screen coords. */ + dx2 = srcrect->w * (float)txi->lw / (float)txi->virt.w; + dy2 = srcrect->h * (float)txi->lh / (float)txi->virt.h; + if(dstrect) + { + dx1 = dstrect->x; + dy1 = dstrect->y; + /* + * FIXME: dstrect should be filled in with the *clipped* + * rect for full SDL compatibility. This hack + * might break some apps... + */ + dstrect->w = dx2; + dstrect->h = dy2; + } + else + dx1 = dy1 = 0; + dx2 += dx1; + dy2 += dy1; + + /* + * Note that we actually *prevent* the use of "full surface alpha" + * and alpha channel in combination - to stay SDL 2D compatible. + */ + if((src->flags & SDL_SRCALPHA) && + (!src->format->Amask || (src->flags & SDL_SRCCOLORKEY))) + alpha = src->format->alpha; + else + alpha = 255; + + /* Render! */ + switch(txi->tilemode) + { + case GLSDL_TM_SINGLE: + _BlitGL_single(txi, x1, y1, x2, y2, + dx1, dy1, dx2, dy2, + alpha, texscale); + break; + case GLSDL_TM_HORIZONTAL: + _BlitGL_htile(txi, x1, y1, x2, y2, + dx1, dy1, dx2, dy2, + alpha, texscale); + break; + case GLSDL_TM_VERTICAL: + _BlitGL_vtile(txi, x1, y1, x2, y2, + dx1, dy1, dx2, dy2, + alpha, texscale); + break; + case GLSDL_TM_HUGE: + /* TODO */ + break; + } + + return 0; +} + + +int glSDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect) +{ + SDL_Surface *vs; + if(!src || !dst) + return -1; + + /* + * Figure out what to do: + * Not using glSDL: SDL_BlitSurface() + * screen->screen: _glSDL_BlitFromGL() + _glSDL_BlitGL() + * surface->screen: _glSDL_BlitGL() + * screen->surface: _glSDL_BlitFromGL() + * surface->surface: SDL_BlitSurface() + */ + if(!USING_GLSDL) + return SDL_BlitSurface(src, srcrect, dst, dstrect); + + vs = SDL_GetVideoSurface(); + if(src == fake_screen) + src = vs; + if(dst == fake_screen) + dst = vs; + if(src == vs) + { + if(dst == vs) + { +/* +FIXME: Any OpenGL extensions for this...? +*/ + _glSDL_BlitFromGL(srcrect, fake_screen, dstrect); + return _glSDL_BlitGL(fake_screen, srcrect, + dst, dstrect); + } + else + { + return _glSDL_BlitFromGL(srcrect, dst, dstrect); + } + } + else + { + if(dst == vs) + { + return _glSDL_BlitGL(src, srcrect, + dst, dstrect); + } + else + { + glSDL_Invalidate(dst, dstrect); + return SDL_BlitSurface(src, srcrect, dst, dstrect); + } + } +} + + +int glSDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color) +{ + SDL_Surface *vs = SDL_GetVideoSurface(); + int dx1, dy1, dx2, dy2; + Uint32 r, g, b; + + /* + * Some ugly reverse conversion for compatibility... + * (We must do this before losing the dst pointer, + * as the pixel formats of the screen and the + * fake_screen may differ!) + */ + r = color & dst->format->Rmask; + r = r >> dst->format->Rshift; + r = r << dst->format->Rloss; + + g = color & dst->format->Gmask; + g = g >> dst->format->Gshift; + g = g << dst->format->Gloss; + + b = color & dst->format->Bmask; + b = b >> dst->format->Bshift; + b = b << dst->format->Bloss; + + if(dst == fake_screen) + dst = vs; + + if(vs != dst) + glSDL_Invalidate(dst, dstrect); + + if((vs != dst) || !USING_GLSDL) + return SDL_FillRect(dst, dstrect, color); + + gl_do_texture(0); + gl_do_blend(0); + + if(!dstrect) + dstrect = &dst->clip_rect; + + dx1 = dstrect->x; + dy1 = dstrect->y; + dx2 = dx1 + dstrect->w; + dy2 = dy1 + dstrect->h; + + glBegin(GL_QUADS); + glColor3ub(r, g, b); + glVertex2i(dx1, dy1); + glVertex2i(dx2, dy1); + glVertex2i(dx2, dy2); + glVertex2i(dx1, dy2); + glEnd(); + + return 0; +} + + +SDL_Surface *glSDL_DisplayFormat(SDL_Surface *surface) +{ + SDL_Surface *s, *tmp; + if(USING_GLSDL) + { + int use_rgba = (surface->flags & SDL_SRCCOLORKEY) || + ((surface->flags & SDL_SRCALPHA) && + surface->format->Amask); + if(use_rgba) + tmp = SDL_ConvertSurface(surface, &_RGBAfmt, SDL_SWSURFACE); + else + tmp = SDL_ConvertSurface(surface, &_RGBfmt, SDL_SWSURFACE); + if(!tmp) + return NULL; + GLSDL_FIX_SURFACE(tmp); + SDL_SetAlpha(tmp, 0, 0); + + if(surface->flags & SDL_SRCCOLORKEY) + { + /* + * We drop colorkey data here, but we have to, + * or we'll run into trouble when converting, + * in particular from indexed color formats. + */ + SDL_SetColorKey(tmp, SDL_SRCCOLORKEY, + surface->format->colorkey); + _key2alpha(tmp); + } + SDL_SetColorKey(tmp, 0, 0); + + if(use_rgba) + s = _CreateRGBASurface(surface->w, surface->h); + else + s = _CreateRGBSurface(surface->w, surface->h); + if(!s) + { + glSDL_FreeSurface(tmp); + return NULL; + } + SDL_BlitSurface(tmp, NULL, s, NULL); + glSDL_FreeSurface(tmp); + + if(surface->flags & SDL_SRCALPHA) + SDL_SetAlpha(s, SDL_SRCALPHA, + surface->format->alpha); + return s; + } + else + { + s = SDL_DisplayFormat(surface); + if(s) + GLSDL_FIX_SURFACE(s); + return s; + } +} + + +SDL_Surface *glSDL_DisplayFormatAlpha(SDL_Surface *surface) +{ + SDL_Surface *s, *tmp; + if(USING_GLSDL) + { + tmp = SDL_ConvertSurface(surface, &_RGBAfmt, SDL_SWSURFACE); + if(!tmp) + return NULL; + GLSDL_FIX_SURFACE(tmp); + + SDL_SetAlpha(tmp, 0, 0); + SDL_SetColorKey(tmp, 0, 0); + s = _CreateRGBASurface(surface->w, surface->h); + if(!s) + { + glSDL_FreeSurface(tmp); + return NULL; + } + SDL_BlitSurface(tmp, NULL, s, NULL); + glSDL_FreeSurface(tmp); + + if(surface->flags & SDL_SRCCOLORKEY) + { + SDL_SetColorKey(s, SDL_SRCCOLORKEY, + surface->format->colorkey); + _key2alpha(s); + } + if(surface->flags & SDL_SRCALPHA) + SDL_SetAlpha(s, SDL_SRCALPHA, + surface->format->alpha); + return s; + } + else + { + s = SDL_DisplayFormatAlpha(surface); + if(s) + GLSDL_FIX_SURFACE(s); + return s; + } +} + + +SDL_Surface *glSDL_ConvertSurface + (SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags) +{ + SDL_Surface *s = SDL_ConvertSurface(src, fmt, flags); + if(s) + GLSDL_FIX_SURFACE(s); + return s; +} + + +SDL_Surface *glSDL_CreateRGBSurface + (Uint32 flags, int width, int height, int depth, + Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) +{ + SDL_Surface *s = SDL_CreateRGBSurface(flags, width, height, depth, + Rmask, Gmask, Bmask, Amask); + if(s) + GLSDL_FIX_SURFACE(s); + return s; +} + + +SDL_Surface *glSDL_CreateRGBSurfaceFrom(void *pixels, + int width, int height, int depth, int pitch, + Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) +{ + SDL_Surface *s = SDL_CreateRGBSurfaceFrom(pixels, + width, height, depth, pitch, + Rmask, Gmask, Bmask, Amask); + if(s) + GLSDL_FIX_SURFACE(s); + return s; +} + + +SDL_Surface *glSDL_LoadBMP(const char *file) +{ + SDL_Surface *s = SDL_LoadBMP(file); + if(s) + GLSDL_FIX_SURFACE(s); + return s; +} + + +int glSDL_SaveBMP(SDL_Surface *surface, const char *file) +{ + SDL_Rect r; + SDL_Surface *buf; + SDL_Surface *screen = SDL_GetVideoSurface(); + + if(!USING_GLSDL) + return SDL_SaveBMP(surface, file); + + if((surface != screen) && (surface != fake_screen)) + return SDL_SaveBMP(surface, file); + + buf = _CreateRGBSurface(fake_screen->w, fake_screen->h); + + r.x = 0; + r.y = 0; + r.w = fake_screen->w; + r.h = fake_screen->h; + if(_glSDL_BlitFromGL(&r, buf, &r) < 0) + return -1; + + return SDL_SaveBMP(buf, file); + + glSDL_FreeSurface(buf); +} + + + + +/*---------------------------------------------------------- + glSDL specific API extensions +----------------------------------------------------------*/ + +void glSDL_Invalidate(SDL_Surface *surface, SDL_Rect *area) +{ + glSDL_TexInfo *txi; + if(!surface) + return; + txi = glSDL_GetTexInfo(surface); + if(!txi) + return; + if(!area) + { + txi->invalid_area.x = 0; + txi->invalid_area.y = 0; + txi->invalid_area.w = surface->w; + txi->invalid_area.h = surface->h; + return; + } + txi->invalid_area = *area; +} + + +void glSDL_SetLogicSize(SDL_Surface *surface, int w, int h) +{ + SDL_Rect r; + glSDL_TexInfo *txi; + if(!IS_GLSDL_SURFACE(surface)) + return; + + txi = glSDL_GetTexInfo(surface); + + txi->lw = w; + txi->lh = h; + + if((SDL_GetVideoSurface() != surface) && (fake_screen != surface)) + return; + + r.x = r.y = 0; + r.w = w; + r.h = h; + glSDL_SetClipRect(surface, &r); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0f, 0.0f, 0.0f); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + gl_reset(); +} + + +/* Upload a single texture. */ +static int _UploadTexture(SDL_Surface *datasurf, glSDL_TexInfo *txi, int tex) +{ + int bpp = datasurf->format->BytesPerPixel; + + glGenTextures(1, (unsigned int *)&txi->texture[tex]); + glBindTexture(GL_TEXTURE_2D, txi->texture[tex]); + glPixelStorei(GL_UNPACK_ROW_LENGTH, datasurf->pitch / + datasurf->format->BytesPerPixel); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, + datasurf->format->Amask ? GL_RGBA8 : GL_RGB8, + txi->texsize, txi->texsize, 0, + datasurf->format->Amask ? GL_RGBA : GL_RGB, + GL_UNSIGNED_BYTE, NULL); + DBG(_print_glerror(1)); + + switch(txi->tilemode) + { + case GLSDL_TM_SINGLE: + case GLSDL_TM_HORIZONTAL: + { + /* Image tiled horizontally, or not at all */ + int fromx = txi->tilew * tex * txi->tilespertex; + int toy = 0; + while(toy + txi->tileh <= txi->texsize) + { + int thistw; + thistw = datasurf->w - fromx; + if(thistw > txi->tilew) + thistw = txi->tilew; + else if(thistw <= 0) + break; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, toy, + thistw, txi->tileh, + datasurf->format->Amask ? GL_RGBA : GL_RGB, + GL_UNSIGNED_BYTE, + (char *)datasurf->pixels + bpp * fromx); + DBG4(_print_glerror(2)); + fromx += txi->tilew; + toy += txi->tileh; + glFlush(); + } + break; + } + case GLSDL_TM_VERTICAL: + { + /* Image tiled vertically */ + int fromy = txi->tileh * tex * txi->tilespertex; + int tox = 0; + while(tox + txi->tilew <= txi->texsize) + { + int thisth; + thisth = datasurf->h - fromy; + if(thisth > txi->tileh) + thisth = txi->tileh; + else if(thisth <= 0) + break; + glTexSubImage2D(GL_TEXTURE_2D, 0, tox, 0, + txi->tilew, thisth, + datasurf->format->Amask ? GL_RGBA : GL_RGB, + GL_UNSIGNED_BYTE, + (char *)datasurf->pixels + datasurf->pitch * fromy); + DBG4(_print_glerror(3)); + fromy += txi->tileh; + tox += txi->tilew; + glFlush(); + } + break; + } + case GLSDL_TM_HUGE: + /* "Huge" image - tiled both ways */ + return -4; + break; + } + return 0; +} + + +int glSDL_UploadSurface(SDL_Surface *surface) +{ + SDL_Surface *datasurf = surface; + glSDL_TexInfo *txi; + int i; + + /* + * For now, we just assume that *every* texture needs + * conversion before uploading. + */ + + /* If there's no TexInfo, add one. */ + if(!IS_GLSDL_SURFACE(surface)) + glSDL_AddTexInfo(surface); + + txi = glSDL_GetTexInfo(surface); + + /* No partial updates implemented yet... */ + if(txi->invalid_area.w) + glSDL_UnloadSurface(surface); + else + { + int missing = 0; + if(txi->textures) + { + for(i = 0; i < txi->textures; ++i) + if(-1 == txi->texture[i]) + { + missing = 1; + break; + } + if(!missing) + return 0; /* They're already there! */ + } + } + + if(txi->texsize > maxtexsize) + { + fprintf(stderr, "glSDL: INTERNAL ERROR: Too large texture!\n"); + return -1; /* This surface wasn't tiled properly... */ + } + + /* + * Kludge: Convert if not of preferred RGB or RGBA format. + * + * Conversion should only be done when *really* needed. + * That is, it should rarely have to be done with OpenGL + * 1.2+. + * + * Besides, any surface that's been SDL_DisplayFormat()ed + * should already be in the best known OpenGL format - + * preferably one that makes DMA w/o conversion possible. + */ + if(_FormatIsOk(surface)) + datasurf = surface; + else + { + DBG(fprintf(stderr, "glSDL: WARNING: On-the-fly conversion performed!\n")); + if(surface->format->Amask) + datasurf = glSDL_DisplayFormatAlpha(surface); + else + datasurf = glSDL_DisplayFormat(surface); + if(!datasurf) + return -2; + } + + for(i = 0; i < txi->textures; ++i) + if(_UploadTexture(datasurf, txi, i) < 0) + return -3; + + if(datasurf != surface) + glSDL_FreeSurface(datasurf); + return 0; +} + + +static void _UnloadTexture(glSDL_TexInfo *txi) +{ + int i; + for(i = 0; i < txi->textures; ++i) + glDeleteTextures(1, (unsigned int *)&txi->texture[i]); + memset(&txi->invalid_area, 0, sizeof(txi->invalid_area)); +} + + +void glSDL_UnloadSurface(SDL_Surface *surface) +{ + if(!IS_GLSDL_SURFACE(surface)) + return; + + _UnloadTexture(glSDL_GetTexInfo(surface)); +} + + +SDL_Surface *glSDL_IMG_Load(const char *file) +{ + SDL_Surface *s; + s = IMG_Load(file); + if(s) + GLSDL_FIX_SURFACE(s); + return s; +} + +#endif /* HAVE_OPENGL */ diff --git a/glSDL.h b/glSDL.h new file mode 100644 index 0000000..3d8a1c4 --- /dev/null +++ b/glSDL.h @@ -0,0 +1,325 @@ +/*(LGPL) +------------------------------------------------------------ + glSDL 0.6 - SDL 2D API on top of OpenGL +------------------------------------------------------------ + * (c) David Olofson, 2001-2003 + * This code is released under the terms of the GNU LGPL. + */ + +#ifndef _GLSDL_H_ +#define _GLSDL_H_ + +/* + * If you don't use GNU autotools or similar, uncomment this to + * compile with OpenGL enabled: +#define HAVE_OPENGL + */ + +/* We're still using SDL datatypes here - we just add some stuff. */ +#include +#include + +#ifndef HAVE_OPENGL + +/* Fakes to make glSDL code compile with SDL. */ +#define SDL_GLSDL 0 +#define LOGIC_W(s) ( (s)->w ) +#define LOGIC_H(s) ( (s)->h ) +#define GLSDL_FIX_SURFACE(s) + +#else /* HAVE_OPENGL */ + +#include "begin_code.h" +#ifdef __cplusplus +extern "C" { +#endif + +#if (SDL_MAJOR_VERSION <= 1) && (SDL_MINOR_VERSION <= 2) && \ + (SDL_PATCHLEVEL < 5) +#warning glSDL: Using SDL version 1.2.5 or later is strongly recommended! +#endif + +/*---------------------------------------------------------- + SDL style API +----------------------------------------------------------*/ + +typedef enum glSDL_TileModes +{ + GLSDL_TM_SINGLE, + GLSDL_TM_HORIZONTAL, + GLSDL_TM_VERTICAL, + GLSDL_TM_HUGE +} glSDL_TileModes; + + +typedef struct glSDL_TexInfo +{ + /* Size of surface in logic screen pixels */ + int lw, lh; + + int textures; + int *texture; + int texsize; /* width/height of OpenGL texture */ + glSDL_TileModes tilemode; + int tilew, tileh; /* At least one must equal texsize! */ + int tilespertex; + SDL_Rect virt; /* Total size of assembled surface */ + + /* Area of surface to download when/after unlocking */ + SDL_Rect invalid_area; +} glSDL_TexInfo; + +#define GLSDL_FIX_SURFACE(s) (s)->unused1 = 0; +#define IS_GLSDL_SURFACE(s) ((s) && glSDL_GetTexInfo(s)) + +#define LOGIC_W(s) ( IS_GLSDL_SURFACE(s) ? TEXINFO(s)->lw : (s)->w ) +#define LOGIC_H(s) ( IS_GLSDL_SURFACE(s) ? TEXINFO(s)->lh : (s)->h ) + +#define SDL_GLSDL 0x00100000 /* Create an OpenGL 2D rendering context */ + + +/* + * Wraps SDL_SetVideoMode(), and adds support for the SDL_GLSDL flag. + * + * If 'flags' contains SDL_GLSDL, glSDL_SetVideoMode() sets up a "pure" + * OpenGL rendering context for use with the glSDL_ calls. + * + * SDL can be closed as usual (using SDL_ calls), but you should call + * glSDL_Quit() (kludge) to allow glSDL to clean up it's internal stuff. + */ +SDL_Surface *glSDL_SetVideoMode(int width, int height, int bpp, Uint32 flags); +void glSDL_Quit(void); + +void glSDL_QuitSubSystem(Uint32 flags); + +/* Replaces SDL_Quit() entirely, when using the override defines */ +void _glSDL_FullQuit(void); + +SDL_Surface *glSDL_GetVideoSurface(void); + +void glSDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects); +void glSDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h); + +/* + * Works like SDL_Flip(), but may also perform enqueued blits. + * (That is, it's possible that the implementation renders + * *nothing* until glSDL_Flip() is called.) + */ +int glSDL_Flip(SDL_Surface *screen); + +void glSDL_FreeSurface(SDL_Surface *surface); + +int glSDL_LockSurface(SDL_Surface *surface); +void glSDL_UnlockSurface(SDL_Surface *surface); + +/* + * Like the respective SDL functions, although they ignore + * SDL_RLEACCEL, as it makes no sense in this context. + */ +int glSDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key); +int glSDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha); + +/* + * Sets up clipping for the screen, or a SDL_Surface. + * + * Note that this function takes both SDL_Surfaces and + * glSDL_Surfaces. + */ +SDL_bool glSDL_SetClipRect(SDL_Surface *surface, SDL_Rect *rect); + +int glSDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect); + +int glSDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color); + +/* + * Convert the given surface into a SDL_Surface (if it isn't + * one already), and makes sure that the underlying SDL_Surface + * is of a pixel format suitable for fast texture downloading. + * + * Note that you *only* have to use this function if you want + * fast pixel access to surfaces (ie "procedural textures"). + * Any surfaces that aren't converted will be downloaded + * automatically upon the first call to glSDL_BlitSurface(), + * but if conversion is required, it will be required for + * every glSDL_UnlockSurface() call. + * + * IMPORTANT: + * You *can* pass an SDL_Surface directly to this function, + * and it will try to deal with it nicely. However, this + * requires that a temporary SDL_Surface is created, and + * this surface is cached only until the texture memory is + * needed for new surfaces. + */ +SDL_Surface *glSDL_DisplayFormat(SDL_Surface *surface); +SDL_Surface *glSDL_DisplayFormatAlpha(SDL_Surface *surface); + +SDL_Surface *glSDL_ConvertSurface + (SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags); +SDL_Surface *glSDL_CreateRGBSurface + (Uint32 flags, int width, int height, int depth, + Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); +SDL_Surface *glSDL_CreateRGBSurfaceFrom(void *pixels, + int width, int height, int depth, int pitch, + Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); +SDL_Surface *glSDL_LoadBMP(const char *file); +int glSDL_SaveBMP(SDL_Surface *surface, const char *file); + + + +/*---------------------------------------------------------- + glSDL specific API extensions +----------------------------------------------------------*/ + +/* + * Invalidate part of a texture. + * + * This function can be used either between calls to + * glSDL_LockSurface() and glSDL_UnlockSurface(), or before + * calling glSDL_DownloadSurface(). + * + * In either case, it causes only the specified area to be + * downloaded when unlocking the surface, or calling + * glSDL_UnlockSurface(), respectively. + * + * Note that if this function is not used, glSDL assumes that + * the entire surface has to be updated. (That is, it's safe + * to ignore this function - it's "just a performance hack.") + * + * Passing a rectangle with zero height or width cancels the + * downloading when/after unlocking the surface. Use if you + * just want to read the texture, but feel like being nice and + * obeying SDL_MUSTLOCK() - which is a good idea, as things + * may change... + * + * Passing NULL for the 'area' argument results in the entire + * surface being invalidated. + * + * NOTE: This function does NOT perform clipping! Weird or + * even Bad Things may happen if you specify areas + * that protrude outside the edges of the actual + * surface. + */ +void glSDL_Invalidate(SDL_Surface *surface, SDL_Rect *area); + +/* + * Set logic width and height of a surface. + * + * When used on the screen surface: + * Changes the OpenGL view coordinate system without + * changing the screen resolution. This is used to + * decouple the rendering coordinate system from the + * physical screen resolution, which is needed to + * achieve sub-pixel resolution without switching to + * float coords. + * + * When used on a SDL_Surface: + * Normally, surfaces are scaled so that one pixel on + * the surface corresponds to one pixel in the current + * logic screen resolution. This function makes it + * possible to change that relation by specifying the + * desired size of the rendered texture in the current + * logic screen resolution. + * + * BUG? WRT blitting *to* surfaces, this call affects + * the screen surface and other surfaces differently! + * As of now, coordinates are *always* treated as real + * pixels when blitting to normal surfaces, whereas + * they are scaled when blitting to the screen. + * + * Of course, this function can also be "abused" just to + * make rendering code independent of screen resolution, + * although that can potentially *lower* accuracy, if the + * real resolution is higher than the logic resolution. + */ +void glSDL_SetLogicSize(SDL_Surface *surface, int w, int h); + +/* + * Make sure that the texture of the specified surface is up + * to date in OpenGL texture memory. + * + * This can be used together with glSDL_UnloadSurface() to + * implement custom texture caching schemes. + * + * Returns 0 on success, or a negative value if something + * went wrong. + */ +int glSDL_UploadSurface(SDL_Surface *surface); + +/* + * Free the texture space used by the specified surface. + * + * Normally, glSDL should download textures when needed, and + * unload the oldest (in terms of use) surfaces, if it runs out + * of texture space. + */ +void glSDL_UnloadSurface(SDL_Surface *surface); + + +/* + * Get the clSDL_TexInfo struct associated with a surface. + */ +glSDL_TexInfo *glSDL_GetTexInfo(SDL_Surface *surface); + + + +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +/* Some ugly "overriding"... */ +#ifndef _GLSDL_NO_REDEFINES_ +/* + * You *always* need to lock and unlock a glSDL surface in + * order to get glSDL to update the OpenGL texture! + */ +#undef SDL_MUSTLOCK +#define SDL_MUSTLOCK(surface) \ + (surface->offset || \ + ((surface->flags & (SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_RLEACCEL)) != 0) || \ + IS_GLSDL_SURFACE(surface)) + +#define SDL_SetVideoMode glSDL_SetVideoMode +#define SDL_GetVideoSurface glSDL_GetVideoSurface +#define SDL_Quit _glSDL_FullQuit +#define SDL_QuitSubSystem glSDL_QuitSubSystem +#define SDL_UpdateRects glSDL_UpdateRects +#define SDL_UpdateRect glSDL_UpdateRect +#define SDL_Flip glSDL_Flip +#define SDL_FreeSurface glSDL_FreeSurface +#define SDL_LockSurface glSDL_LockSurface +#define SDL_UnlockSurface glSDL_UnlockSurface +#define SDL_SetColorKey glSDL_SetColorKey +#define SDL_SetAlpha glSDL_SetAlpha +#define SDL_SetClipRect glSDL_SetClipRect +#undef SDL_BlitSurface +#define SDL_BlitSurface glSDL_BlitSurface +#define SDL_FillRect glSDL_FillRect +#define SDL_DisplayFormat glSDL_DisplayFormat +#define SDL_DisplayFormatAlpha glSDL_DisplayFormatAlpha +#define SDL_ConvertSurface glSDL_ConvertSurface +#define SDL_CreateRGBSurface glSDL_CreateRGBSurface +#define SDL_CreateRGBSurfaceFrom glSDL_CreateRGBSurfaceFrom +#undef SDL_AllocSurface +#define SDL_AllocSurface glSDL_CreateRGBSurface +#undef SDL_LoadBMP +#define SDL_LoadBMP glSDL_LoadBMP +#undef SDL_SaveBMP +#define SDL_SaveBMP glSDL_SaveBMP +#define IMG_Load(x) glSDL_IMG_Load(x) +#endif + +/* Some extra overloading for common external lib calls... */ +#include "SDL/SDL_image.h" +#ifdef __cplusplus +extern "C" { +#endif +SDL_Surface *glSDL_IMG_Load(const char *file); +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_OPENGL */ + +#endif diff --git a/mac/Info.plist b/mac/Info.plist new file mode 100644 index 0000000..84736be --- /dev/null +++ b/mac/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDocumentTypes + + CFBundleExecutable + FloboPuyo + CFBundleGetInfoString + @@VERSION@@, Copyright iOS Software, 2004 + CFBundleIconFile + icon.icns + CFBundleIdentifier + com.ios.flobopoyu + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + FloboPuyo + CFBundlePackageType + APPL + CFBundleShortVersionString + @@VERSION@@ + CFBundleSignature + * + CFBundleVersion + 13 + + diff --git a/mac/icon.icns b/mac/icon.icns new file mode 100644 index 0000000..bd1cd0e Binary files /dev/null and b/mac/icon.icns differ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..64fc8ea --- /dev/null +++ b/main.cpp @@ -0,0 +1,98 @@ +#include +#include +#ifdef MACOSX +#include +#endif + +#include "PuyoCommander.h" + +#ifndef DATADIR +char *DATADIR = "data"; +#endif + +char *dataFolder = "data"; + +#ifdef MACOSX +const char *bundleDataPath = "/Contents/Resources/data"; +void show(CFStringRef formatString, ...) { + CFStringRef resultString; + CFDataRef data; + va_list argList; + + va_start(argList, formatString); + resultString = CFStringCreateWithFormatAndArguments(NULL, NULL, formatString, argList); + va_end(argList); + + data = CFStringCreateExternalRepresentation(NULL, resultString, CFStringGetSystemEncoding(), '?'); + + if (data != NULL) { + printf ("%.*s\n\n", (int)CFDataGetLength(data), CFDataGetBytePtr(data)); + CFRelease(data); + } + + CFRelease(resultString); +} +#endif + +bool fileExists(char *path) +{ + FILE *f; + f = fopen(path, "r"); + if (f == NULL) + return false; + fclose(f); + return true; +} + +#include + +int main(int argc, char *argv[]) +{ + int i; + + if (!strcmp(argv[argc-1],"-h")) { + printf("-win for windowed mode.\n"); + printf("-quiet to remove music.\n"); + printf("-nofx to remove sound FX.\n"); + return 0; + } + +#ifdef MACOSX + +#ifndef DATADIR + CFStringRef bundlePath = CFURLCopyFileSystemPath(CFBundleCopyBundleURL(CFBundleGetMainBundle()), kCFURLPOSIXPathStyle); + int pathSize = (int)CFStringGetMaximumSizeForEncoding(CFStringGetLength(bundlePath), CFStringGetSystemEncoding()) + 1; + DATADIR = (char *)malloc(pathSize + strlen(bundleDataPath)); + CFStringGetCString (bundlePath, DATADIR, pathSize, CFStringGetSystemEncoding()); + strcat(DATADIR, bundleDataPath); + + //show(CFSTR("test : %@"), CFURLCopyFileSystemPath(CFBundleCopyBundleURL(CFBundleGetMainBundle()), kCFURLPOSIXPathStyle)); + //fprintf(stderr, "bundle=%d path=%s\n", CFBundleGetMainBundle(), DATADIR); + //fprintf(stderr, "Attention ça va planter2\n"); + //CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle(),kCFStringEncodingUnicode); + //DATADIR = strdup(CFStringGetCStringPtr(CFURLGetString( CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle())), kCFStringEncodingUnicode)); + //fprintf(stderr, "Resource path: %s\n", DATADIR); +#endif +#endif + + bool fs = true; + bool snd = true; + bool fx = true; + + for (i=1; i. +.SH LICENSE +.B flobopuyo +is covered by the GNU General Public License (GPL). +.SH SEE ALSO +.BR xpuyopuyo (6), +.BR kfouleggs (6). +.SH AUTHOR +Florent Boudet +.br +Guillaume Borios +.br +Jean-Christophe Hoelt +.PP +This manual page was written by Uwe Hermann , +for the Debian project (but may be used by others). diff --git a/menu.c b/menu.c new file mode 100644 index 0000000..6ba9c9a --- /dev/null +++ b/menu.c @@ -0,0 +1,363 @@ +/* file created on Mon Aug 26 15:30:05 CEST 2002 by doom */ + +#include "menu.h" +#include +#include +#include + + +struct _MENU +{ + MenuItems items; + int active_item; + int nb_items; + IIM_Surface * menuselector; + + int sep_size; + + Sound *sound_move; + Sound *sound_valid; + + int appears; + int visible; + int hide; + + int hauteur; +}; + + +void menu_update_hauteur (Menu * menu); + + + Menu * +menu_new (MenuItems items,IIM_Surface * selimage) +{ + Menu *m = (Menu *)malloc (sizeof (Menu)); + + m->items = items; + + m->nb_items = 0; + while (items[m->nb_items++].name); + + m->sep_size = 20; // taille d'un separateur. + + m->active_item = -1; + m->sound_move = m->sound_valid = NULL; + + m->visible = m->appears = m->hide = 0; + menu_update_hauteur (m); + + menu_next_item (m); + + + m->menuselector = selimage;; + + return m; +} + + +int menu_get_active_item (Menu * menu) +{ + return menu->active_item; +} + +const char * menu_get_active_item_string (Menu * menu) +{ + return menu->items[menu_get_active_item (menu)].name; +} + +int menu_active_is (Menu * menu, const char *it_name) +{ + return (!strcmp (it_name, menu_get_active_item_string (menu))); +} + + +void menu_change_item_done (Menu * menu) +{ + menu->items[menu->active_item].priv_must_anim = 1; + if (menu->sound_move) { + audio_sound_play (menu->sound_move); + } +} + + + void +menu_next_item (Menu * menu) +{ + do { + menu->active_item++; + menu->active_item %= menu->nb_items; + } + while (menu_items_is_delimiter (menu->items, menu->active_item)); + menu_change_item_done (menu); +} + + void +menu_prev_item (Menu * menu) +{ + do { + if (menu->active_item) + menu->active_item--; + else + menu->active_item = menu->nb_items - 1; + } + while (menu_items_is_delimiter (menu->items, menu->active_item)); + menu_change_item_done (menu); +} + + +void menu_update (Menu * menu, SDL_Surface * surf) +{ + int c; + + if (menu->visible) { + if (menu->hide) { + menu->hide *= 2; + } + + if (menu->hide > surf->h) { + menu->visible = 0; + menu->hide = 0; + } + + if (menu->appears) { + for (c = 0; c < menu->nb_items; c++) { + menu->items[c].priv_vx = 0; + menu->items[c].priv_x = + ((rand () % 2) ? -1 : 1) * (16 + (rand () % 4)) * surf->w / 16; + } + menu->appears = 0; + } + + for (c = 0; c < menu->nb_items; c++) { + SDL_Surface *item_surf = menu->items[c].bitmap; + SoFont *item_font = menu->items[c].font; + + if (item_surf || item_font) { + if (menu->items[c].priv_must_anim) { + static int alt = -1; + + menu->items[c].priv_vx += alt * 15; + menu->items[c].priv_must_anim = 0; + alt = -alt; + } + + if ((fabs (menu->items[c].priv_vx) < 0.02) + && (fabs (menu->items[c].priv_x) < 0.1)) + menu->items[c].priv_x = menu->items[c].priv_vx = 0; + + menu->items[c].priv_vx -= menu->items[c].priv_x / 8; + menu->items[c].priv_vx *= 0.85; + + menu->items[c].priv_x += menu->items[c].priv_vx; + } + } + } +} + +void menu_draw (Menu * menu, SDL_Surface * surf) +{ + if (menu->visible) { + int c; // compteur d'items + int center = surf->w / 2; + int hauteur = (surf->h - menu->hauteur) / 2; + + hauteur += menu->hide; + + for (c = 0; c < menu->nb_items; c++) { + SDL_Surface *item_surf = menu->items[c].bitmap; + SoFont *item_font = menu->items[c].font; + + if (item_surf || item_font) { + int center_x; + + center_x = (int)(center + menu->items[c].priv_x); + + if (item_surf != NULL) { + SDL_Rect rect; + + rect.x = center_x - item_surf->w / 2; + rect.y = hauteur; + rect.w = item_surf->w; + rect.h = item_surf->h; + + SDL_BlitSurface (item_surf, NULL, surf, &rect); + hauteur += item_surf->h; + } + else { // font.. + static char item_name[1024]; + + if (c == menu->active_item) { + + SDL_Rect rect; + if (menu->menuselector==NULL) + { + rect.x = center - 140; rect.w = 280; + rect.y = hauteur; rect.h = SoFont_FontHeight (item_font); + SDL_FillRect (surf, &rect, 0); + rect.x = center - 150; rect.w = 300; + rect.y = hauteur + 3; rect.h = SoFont_FontHeight (item_font) - 6; + SDL_FillRect (surf, &rect, 0); + } + else + { + rect.x = center - 150; rect.w = menu->menuselector->w; + rect.y = hauteur - 3; rect.h = menu->menuselector->h; + SDL_BlitSurface (menu->menuselector->surf, NULL, surf, &rect); + } + } + + strcpy (item_name, menu->items[c].name); + if (menu->items[c].value) { + strcat (item_name, menu->items[c].value); + } + + char *pc = item_name; + int nbTab = 0; + for (; *pc; pc++) + { + if (*pc == '\t') { + while (*pc == '\t') { + *pc = 0; + pc ++; + nbTab ++; + } + break; + } + } + + if (nbTab > 0) + { + int w = 80 + nbTab * 50; + SoFont_PutString (item_font, surf, center_x-w, hauteur, item_name, NULL); + SoFont_PutString (item_font, surf, center_x+w - SoFont_TextWidth(item_font, pc), hauteur, pc, NULL); + } + else + { + SoFont_PutString (item_font, surf, + center_x - SoFont_TextWidth (item_font, + item_name) / 2, + hauteur, item_name, NULL); + } + hauteur += SoFont_FontHeight (item_font); + } + } + else + hauteur += menu->sep_size; + } + } +} + +void menu_update_hauteur (Menu * menu) +{ + int c; // compteur d'items + int hauteur = 0; + + for (c = 0; c < menu->nb_items; c++) { + SDL_Surface *item_surf = menu->items[c].bitmap; + SoFont *item_font = menu->items[c].font; + + if (item_surf || item_font) { + if (item_surf != NULL) { + hauteur += item_surf->h; + } + else { // font.. + hauteur += SoFont_FontHeight (item_font); + } + } + else + hauteur += menu->sep_size; + } + menu->hauteur = hauteur; +} + + + void +menu_set_sounds (Menu * menu, Sound * s_move, Sound * s_validate) +{ + menu->sound_move = s_move; + menu->sound_valid = s_validate; +} + + void +menu_validate (Menu * menu) +{ + if (menu->sound_valid) + audio_sound_play (menu->sound_valid); + menu->items[menu->active_item].priv_must_anim = 1; +} + + void +menu_show (Menu * menu) +{ + if (!menu_visible (menu)) { + menu->appears = 1; + menu->visible = 1; + menu->hide = 0; + } +} + + void +menu_hide (Menu * menu) +{ + if (menu_visible (menu)) + menu->hide = 1; +} + + int +menu_visible (Menu * menu) +{ + return (menu->visible) && (!menu->hide); +} + + int +menu_switch_on_off (Menu * menu, const char *name) +{ + const char *value = menu_items_get_value (menu->items, name); + + if (!strcmp (value, "ON")) { + menu_set_value (menu, name, "OFF"); + return 0; + } + else { + menu_set_value (menu, name, "ON"); + return 1; + } +} + + int +menu_progress_bar (Menu * menu, const char *name, int increment) +{ + const char *svalue = menu_items_get_value (menu->items, name); + int value = atoi (svalue); + static char nval[5]; + + if (increment == 0) + return value; + else { + value += increment; + if (value > 100) + value = 100; + if (value < 0) + value = 0; + } + + sprintf (nval, "%d %%", value); + menu_set_value (menu, name, nval); + return value; +} + + + const char * +menu_get_value (Menu * menu, const char *name) +{ + return menu_items_get_value (menu->items, name); +} + + void +menu_set_value (Menu * menu, const char *name, const char *value, int doValide) +{ + menu_items_set_value_for (menu->items, name, value); + if (doValide) + menu_validate (menu); +} diff --git a/menu.h b/menu.h new file mode 100644 index 0000000..53298b1 --- /dev/null +++ b/menu.h @@ -0,0 +1,71 @@ +/* file created on Mon Aug 26 15:30:05 CEST 2002 by doom */ + +#ifndef _menu_H +#define _menu_H + +#include "menuitems.h" +#include "audio.h" +#include "IosImgProcess.h" + +/// MENU +/// +/// gestionnaire de menu + +typedef struct _MENU Menu; + +///------------------------------ +/// FONCTION DE PLUS HAUT NIVEAU +///------------------------------ + +/// alloue un nouveau gestionnaire de menu +Menu * menu_new (MenuItems items, IIM_Surface * selimage); + +/// retourne 1 si l'item actif est it_name +int menu_active_is (Menu * menu, const char *it_name); + +void menu_next_item (Menu * menu); +void menu_prev_item (Menu * menu); + +/// met a jour les donnees "temporelles" du menu.. +/// (doit donc etre appele a chaque cycle du jeu/programme) +void menu_update (Menu * menu, SDL_Surface * surf); + +/// affiche le menu sur la surface +void menu_draw (Menu * menu, SDL_Surface * surf); + +/// null pour desactiver. +void menu_set_sounds (Menu * menu, Sound * s_move, Sound * s_validate); + +/// informe le menu que le choix a été fait.. +/// (continuer d'appeler la methode d'affichage du menu.. +/// il se chargera de ne rien faire si rien n'est à faire). +void menu_validate (Menu * menu); + +void menu_show (Menu * menu); +void menu_hide (Menu * menu); + +int menu_visible (Menu * menu); + +/// switch la valeur d'un item ON/OFF +// retourne l'etat du switch +int menu_switch_on_off (Menu * menu, const char *name); + +int menu_progress_bar (Menu * menu, const char *name, int increment); + +/// +/// Fonctions pour faire des choses vraiment particuliere +/// + +/// retourne l'index de l'item actif */ +int menu_get_active_item (Menu * menu); + +/// retourne le nom de l'item actif */ +const char *menu_get_active_item_string (Menu * menu); + +/// donne la valeur de l'item name */ +const char *menu_get_value (Menu * menu, const char *name); + +/// affecte la valeur de l'item name */ +void menu_set_value (Menu * menu, const char *name, const char *value, int doValidate=1); + +#endif /* _menu_H */ diff --git a/menuitems.c b/menuitems.c new file mode 100644 index 0000000..23e1f53 --- /dev/null +++ b/menuitems.c @@ -0,0 +1,99 @@ +/* file created on Wed Aug 28 13:32:02 CEST 2002 by doom */ + +#include "menuitems.h" +#include +#include + +/* menu items */ + +void +menu_items_set_bitmap_for (MenuItems mi, + const char *name, SDL_Surface * bitmap) +{ + int i = 0; + + while (mi[i].name) { + if (!strcmp (mi[i].name, name)) { + mi[i].bitmap = bitmap; + return; + } + i++; + } +} + +SDL_Surface * +menu_items_get_bitmap (MenuItems mi, const char *name) +{ + int i = 0; + + while (mi[i].name) { + if (!strcmp (mi[i].name, name)) { + return mi[i].bitmap; + } + i++; + } + return NULL; +} + +void +menu_items_set_font_for (MenuItems mi, const char *name, SoFont * font) +{ + int i = 0; + + while (mi[i].name) { + if (!strcmp (mi[i].name, name)) { + mi[i].font = font; + return; + } + i++; + } +} + +SoFont * +menu_items_get_font (MenuItems mi, const char *name) +{ + int i = 0; + + while (mi[i].name) { + if (!strcmp (mi[i].name, name)) { + return mi[i].font; + } + i++; + } + return NULL; +} + +void +menu_items_set_value_for (MenuItems mi, const char *name, const char *value) +{ + int i = 0; + + while (mi[i].name) { + if (!strcmp (mi[i].name, name)) { + if (mi[i].value) + free (mi[i].value); + if (value) { + mi[i].value = (char *)malloc (strlen (value) + 1); + strcpy (mi[i].value, value); + } + else + mi[i].value = NULL; + return; + } + i++; + } +} + +const char * +menu_items_get_value (MenuItems mi, const char *name) +{ + int i = 0; + + while (mi[i].name) { + if (!strcmp (mi[i].name, name)) { + return mi[i].value; + } + i++; + } + return NULL; +} diff --git a/menuitems.h b/menuitems.h new file mode 100644 index 0000000..b9f45c1 --- /dev/null +++ b/menuitems.h @@ -0,0 +1,53 @@ +/* file created on Wed Aug 28 13:32:02 CEST 2002 by doom */ + +#ifndef _menuitems_H +#define _menuitems_H + +/// MENU ITEMS + +#include "glSDL.h" +#include "sofont.h" + +/// gestion des items du menu +/// ne jamais utiliser cette structure : +/// y'a des MACRO et des fonctions pour ca */ +typedef struct _MENU_ITEM +{ + const char *name; + char *value; + SDL_Surface *bitmap; // NULL si c'est un separateur. + SoFont *font; // NULL si c'est un separateur. + + float priv_vx; + float priv_x; + int priv_must_anim; + + int inactive; +} +MenuItem; + +typedef MenuItem *MenuItems; +typedef MenuItem MenuItemsTab[]; + +#define MENUITEM_BLANKLINE {"---",NULL,NULL,NULL,0,0,0,1} +#define MENUITEM_END {NULL,NULL,NULL,NULL,0,0,0,1} +#define MENUITEM(x) {x,NULL,NULL,NULL,0,0,0,0} +#define MENUITEM_INACTIVE(x) {x,NULL,NULL,NULL,0,0,0,1} + +void menu_items_set_bitmap_for (MenuItems mi, + const char *name, SDL_Surface * bitmap); +SDL_Surface *menu_items_get_bitmap (MenuItems mi, const char *name); + +void menu_items_set_font_for (MenuItems mi, + + const char *name, SoFont * font); +SoFont *menu_items_get_font (MenuItems mi, const char *name); + +/// !!! une copie interne de value est créée !!! +void menu_items_set_value_for (MenuItems mi, const char *name, const char *value); +const char *menu_items_get_value (MenuItems mi, const char *name); + +#define menu_items_is_delimiter(mi,index) (mi[index].inactive) + + +#endif /* _menuitems_H */ diff --git a/preferences.c b/preferences.c new file mode 100644 index 0000000..7f2cbdd --- /dev/null +++ b/preferences.c @@ -0,0 +1,291 @@ +/* + * preferences.c + * + * + * Created by Guillaume Borios on 03/08/04. + * Copyright 2004 iOS. All rights reserved. + * + */ + +#include "preferences.h" + + +#ifdef __APPLE__ + +#include + +void SetIntPreference(const char * name, int value) +{ + CFStringRef nom = CFStringCreateWithCString (NULL,name,CFStringGetSystemEncoding()); + if (nom != NULL) + { + CFNumberRef aValue = CFNumberCreate(NULL,kCFNumberIntType,&value); + if (aValue != NULL) + { + CFPreferencesSetAppValue (nom,aValue,kCFPreferencesCurrentApplication); + (void)CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); + CFRelease(aValue); + } + CFRelease(nom); + } +} + +void SetBoolPreference(const char * name, int value) +{ + CFStringRef nom = CFStringCreateWithCString (NULL,name,CFStringGetSystemEncoding()); + if (nom != NULL) + { + CFPreferencesSetAppValue (nom,value?CFSTR("YES"):CFSTR("NO"),kCFPreferencesCurrentApplication); + (void)CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); + CFRelease(nom); + } +} + +int GetIntPreference(const char * name, int defaut) +{ + int retour = defaut; + + CFStringRef nom = CFStringCreateWithCString (NULL,name,CFStringGetSystemEncoding()); + + if (nom != NULL) + { + CFNumberRef value = (CFNumberRef)CFPreferencesCopyAppValue(nom,kCFPreferencesCurrentApplication); + + if (value != NULL) + { + if (CFGetTypeID(value) == CFNumberGetTypeID ()) + { + if (CFNumberGetValue (value, kCFNumberIntType,&retour) == false) + retour = defaut; + } + CFRelease(value); + } + CFRelease(nom); + } + + return retour; +} + +int GetBoolPreference(const char * name, int defaut) +{ + int retour = defaut; + + CFStringRef nom = CFStringCreateWithCString (NULL,name,CFStringGetSystemEncoding()); + + if (nom != NULL) + { + CFStringRef value = (CFStringRef)CFPreferencesCopyAppValue(nom,kCFPreferencesCurrentApplication); + + if (value != NULL) + { + if (CFGetTypeID(value) == CFStringGetTypeID ()) + { + retour = (CFStringCompare(value, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) ? 1 : 0; + } + CFRelease(value); + } + CFRelease(nom); + } + + + return retour; +} + +void SetStrPreference (const char *name, const char *value) +{ + CFStringRef nom = CFStringCreateWithCString (NULL,name,CFStringGetSystemEncoding()); + if (nom != NULL) + { + CFStringRef val = CFStringCreateWithCString (NULL,value,CFStringGetSystemEncoding()); + if (val != NULL) + { + CFPreferencesSetAppValue (nom,val,kCFPreferencesCurrentApplication); + (void)CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); + CFRelease(val); + } + CFRelease(nom); + } +} + +void GetStrPreference (const char *name, char *out, const char *defaut, const int bufferSize) +{ + if ((out==NULL) || (name==NULL)) return; + + if (defaut != NULL) + { + strncpy(out,defaut,bufferSize-1); + out[bufferSize-1]=0; + } + else out[0]=0; + + CFStringRef nom = CFStringCreateWithCString (NULL,name,CFStringGetSystemEncoding()); + + if (nom != NULL) + { + CFStringRef value = (CFStringRef)CFPreferencesCopyAppValue(nom,kCFPreferencesCurrentApplication); + + if (value != NULL) + { + if (CFGetTypeID(value) == CFStringGetTypeID ()) + { + if ((!CFStringGetCString (value, out, bufferSize, CFStringGetSystemEncoding())) && (defaut != NULL)) + strcpy(out,defaut); + } + CFRelease(value); + } + CFRelease(nom); + } +} + +#else /* Not __APPLE__ */ + +#include +#include +#include +#include +#include + +static const char * prefsfile = ".flobopuyorc"; +static const char * sep = "\n\r"; + +static char * file = NULL; +static char * home = NULL; + +static void fetchFile(void) +{ + FILE * prefs; + struct stat myStat; + + if (home == NULL) + { +#ifndef _WIN32 + char * h = getenv("HOME"); +#else + char * h = "."; +#endif + if (h==NULL) return; + home = (char *)malloc(strlen(h)+strlen(prefsfile)+2); + strcpy(home,h); + strcat(home,"/"); + strcat(home,prefsfile); + } + + if (file != NULL) + { + free(file); + file = NULL; + } + + prefs = fopen(home, "r"); + if (prefs == NULL) return; + + if (stat(home,&myStat) == 0) + { + file = (char *)malloc((size_t)(myStat.st_size)+1); + int l = fread(file, 1,(size_t)(myStat.st_size), prefs); + file[l]=0; + } + + fclose(prefs); +} + + +void SetBoolPreference(const char *name, int value) +{ + SetIntPreference(name, value); +} + +void SetIntPreference(const char *name, int value) +{ + char var[256]; + sprintf(var,"%d",value); + SetStrPreference(name,var); +} + + +int GetBoolPreference(const char *name, int defaut) +{ + return GetIntPreference(name,defaut)?true:false; +} + +int GetIntPreference(const char *name, int defaut) +{ + char var[256]; + var[0] = 0; + GetStrPreference(name,var,NULL,256); + if (var[0]) return atoi(var); + return defaut; +} + +void SetStrPreference (const char *name, const char *value) +{ + char * key; + FILE * prefs; + + fetchFile(); + + if (home == NULL) return; + + prefs = fopen(home, "w+"); + if (prefs == NULL) return; + + if (file != NULL) + { + for (key = strtok(file, sep); key; key = strtok(NULL, sep)) + { + if (strstr(key,name)==NULL) + { + fprintf(prefs,"%s\n",key); + } + } + free(file); + file = NULL; + } + + fprintf(prefs,"%s=%s\n",name,value); + + fclose(prefs); +} + + +void GetStrPreference (const char *name, char *out, const char *defaut, const int bufferSize) +{ + char * key, *copiedfile; + int tmplen; + + if ((out==NULL) || (name==NULL)) return; + + if (defaut != NULL) + { + strncpy(out,defaut,bufferSize-1); + out[bufferSize-1]=0; + } + else out[0]=0; + + if (file==NULL) fetchFile(); + if (file==NULL) return; + + char tmp[256]; + sprintf(tmp,"%s=",name); + tmplen = strlen(tmp); + + copiedfile = strdup(file); + + for (key = strtok(copiedfile, sep); key; key = strtok(NULL, sep)) + { + if (strncmp(key, tmp, tmplen) == 0) + { + strncpy(out, key+tmplen, bufferSize-1); + out[bufferSize-1] = 0; + free(copiedfile); + return; + } + } + free(copiedfile); + + return; +} + + +#endif /* Not __APPLE__ */ + diff --git a/preferences.h b/preferences.h new file mode 100644 index 0000000..f40ba9b --- /dev/null +++ b/preferences.h @@ -0,0 +1,20 @@ +/* + * preferences.h + * + * + * Created by Guillaume Borios on 03/08/04. + * Copyright 2004 iOS. All rights reserved. + * + */ + + +/* Set preferences */ +void SetBoolPreference(const char *name,int value); +void SetIntPreference (const char *name, int value); +void SetStrPreference (const char *name, const char *value); + + +/* Get preferences */ +int GetBoolPreference(const char *name, int defaut); +int GetIntPreference (const char *name, int defaut); +void GetStrPreference (const char *name, char *out, const char *defaut, const int bufferSize = 256); diff --git a/scenar.l b/scenar.l new file mode 100644 index 0000000..7aa1cb4 --- /dev/null +++ b/scenar.l @@ -0,0 +1,42 @@ +%{ + #include "scenar.y.h" + #include + #include + void yyerror(char *); + int linenum; +%} + +%S STRINGg + +%% +\n { linenum++; } + +"<--".*"-->" {} + +[-]*[0-9]+ { yylval.i = atoi(yytext); return INTEGER; } +["] { yylval.str[0]=0; BEGIN STRINGg; } +["] { BEGIN INITIAL; return STRING; } +"\\n" { strcat(yylval.str,"\n"); } +[a-zA-Z0-9\ \.\:\!\,\'\?]+ { strcat(yylval.str,yytext); } +"," { return COMMA; } + +"background" { return BACKGROUND; } +"foreground" { return FOREGROUND; } +"vector" { return VECTOR; } +"textarea" { return TEXTAREA; } +"say" { return SAY; } +"wait" { return WAIT; } +"sec" { return SEC; } +"notextarea" { return NOTEXTAREA; } +"loop" { return LOOP; } + + +[ \t] ; /* skip whitespace */ + +. { return yytext[0]; } + +%% + +int yywrap(void) { + return 1; +} diff --git a/scenar.y b/scenar.y new file mode 100644 index 0000000..dcafebc --- /dev/null +++ b/scenar.y @@ -0,0 +1,206 @@ +%{ +#include +#include "glSDL.h" +#include +#include "PuyoCommander.h" +#include "IosImgProcess.h" +#include "PuyoStory.h" +#include "SDL_prim.h" +#include "sofont.h" + +int yylex(void); +void yyerror(char *); +extern int linenum; + +static IIM_Surface *first_layer = 0; +static IIM_Surface *back_layer = 0; + +float dec_vectorx; +float dec_vectory; + +static float decx,decy; +static int txt_x,txt_y,txt_w,txt_h,txt_r; +static char txt[1024]; + +extern PuyoStory *theStory; + +void loop_scenar(int time) +{ + Uint32 start = SDL_GetTicks(); + while (start + time * 1000 > SDL_GetTicks()) { + SDL_Event e; + + while (SDL_PollEvent (&e)) { + switch (e.type) { + case SDL_QUIT: + goto mml_fin; + case SDL_KEYDOWN: + if (e.key.keysym.sym == SDLK_RETURN) { + goto mml_fin; + } + if (e.key.keysym.sym == SDLK_ESCAPE) { + goto mml_fin; + } + break; + } + } + + decx += dec_vectorx; + decy += dec_vectory; + + if (decx < 0) decx += back_layer->w; + if (decy < 0) decy += back_layer->h; + if (decx >= back_layer->w) decx -= back_layer->w; + if (decy >= back_layer->h) decy -= back_layer->h; + + theCommander->updateAll(theStory); + } +mml_fin: + return; +} + +void draw_scenar() +{ + SDL_Rect drect; + // 1- Afficher le back layer + if (back_layer) { + drect.x = (short)decx - back_layer->w; + drect.y = (short)decy; + drect.w = back_layer->w; + drect.h = back_layer->h; + SDL_BlitSurface(back_layer->surf,NULL,display,&drect); + drect.x = (short)decx; + drect.y = (short)decy - back_layer->h; + drect.w = back_layer->w; + drect.h = back_layer->h; + SDL_BlitSurface(back_layer->surf,NULL,display,&drect); + drect.x = (short)decx - back_layer->w; + drect.y = (short)decy - back_layer->h; + drect.w = back_layer->w; + drect.h = back_layer->h; + SDL_BlitSurface(back_layer->surf,NULL,display,&drect); + drect.x = (short)decx; + drect.y = (short)decy; + drect.w = back_layer->w; + drect.h = back_layer->h; + SDL_BlitSurface(back_layer->surf,NULL,display,&drect); + } + if (first_layer) { + drect.x = 0; + drect.y = display->h - first_layer->h; + drect.w = first_layer->w; + drect.h = first_layer->h; + SDL_BlitSurface(first_layer->surf,NULL,display,&drect); + } + if (txt_w>0) { + SDL_fillCircle(display, txt_x, txt_y+txt_r, txt_r, 0xffffffff); + SDL_fillCircle(display, txt_x, txt_y+txt_h-txt_r, txt_r, 0xffffffff); + SDL_fillCircle(display, txt_x+txt_w, txt_y+txt_r, txt_r, 0xffffffff); + SDL_fillCircle(display, txt_x+txt_w, txt_y+txt_h-txt_r, txt_r, 0xffffffff); + drect.x = txt_x; + drect.y = txt_y; + drect.w = txt_w; + drect.h = txt_h+1; + SDL_FillRect(display,&drect,0xffffffff); + drect.x = txt_x-txt_r; + drect.y = txt_y+txt_r; + drect.w = txt_w+2*txt_r+1; + drect.h = txt_h-2*txt_r; + SDL_FillRect(display,&drect,0xffffffff); + SoFont_PutString(theStory->commander->darkFont,display,txt_x,txt_y+10,txt,NULL); + } +} + +%} + +%union { + int i; + char str[1024]; +} + +%token INTEGER STRING +%token BACKGROUND VECTOR +%token FOREGROUND +%token MUSIC +%token WAIT SEC LOOP +%token TEXTAREA NOTEXTAREA +%token COMMA +%token SAY + +%type STRING +%type INTEGER + +%% + +scenar: prologue instructions epilogue; + +prologue: { + first_layer = back_layer = 0; + decx = decy = 0; + dec_vectorx = 0; + dec_vectory = 0; + txt_w=txt_h=0; + linenum=1; +}; +epilogue: { + if(first_layer) { IIM_Free(first_layer); first_layer=0; } + if(back_layer) { IIM_Free(back_layer); back_layer=0; } +}; + +instructions: instructions instruction + |; + +instruction: + FOREGROUND STRING { + if(first_layer) { IIM_Free(first_layer); first_layer=0; } + first_layer = IIM_Load_DisplayFormatAlpha($2); + } + |BACKGROUND STRING { + if(back_layer) { IIM_Free(back_layer); back_layer=0; } + back_layer = IIM_Load_DisplayFormatAlpha($2); + } + |VECTOR INTEGER COMMA INTEGER { + dec_vectorx = 0.01f * $2; + dec_vectory = 0.01f * $4; + } + |WAIT INTEGER SEC { + loop_scenar($2); + } + |LOOP { + loop_scenar(36000); + } + |TEXTAREA INTEGER COMMA INTEGER COMMA INTEGER COMMA INTEGER COMMA INTEGER { + txt_x = $2; + txt_y = $4; + txt_w = $6; + txt_h = $8; + txt_r = $10; + } + |NOTEXTAREA { + txt_w = txt_h = 0; + } + |SAY STRING { + strcpy(txt,$2); + } + ; + +%% + +#include + +void yyerror(char *s) { + fprintf(stderr, "ERROR LINE %d: %s\n", linenum, s); + exit(1); +} + +extern int yyrestart(FILE*f); + +void launch_scenar(const char *f) +{ + FILE *yyin = fopen(f,"r"); + if (yyin==NULL) return; + yyrestart(yyin); + yyparse(); + fclose(yyin); +} + diff --git a/scrollingtext.c b/scrollingtext.c new file mode 100644 index 0000000..667c5be --- /dev/null +++ b/scrollingtext.c @@ -0,0 +1,81 @@ +/* file created on Wed Aug 28 15:38:28 CEST 2002 by doom */ + +#include "scrollingtext.h" +#include +#include + +struct _SCROLLING_TEXT +{ + char *text; + SoFont *font; + + int pos; + int text_width; +}; + +static int scrolling_text_width (ScrollingText * st); + +ScrollingText * +scrolling_text_new (const char *text, SoFont * font) +{ + ScrollingText *st = (ScrollingText*)malloc (sizeof (ScrollingText)); + + st->text = NULL; + scrolling_text_set_font (st, font); + + scrolling_text_set_text (st, text); + + st->text_width = scrolling_text_width (st); + st->pos = 640; + return st; +} + +void +scrolling_text_set_font (ScrollingText * st, SoFont * font) +{ + st->font = font; + st->text_width = scrolling_text_width (st); +} + +void +scrolling_text_set_text (ScrollingText * st, const char *text) +{ + if (st->text) + free (st->text); + if (text) { + st->text = (char*)malloc (strlen (text) + 1); + strcpy (st->text, text); + } + else + st->text = NULL; + st->text_width = scrolling_text_width (st); +} + +void +scrolling_text_update (ScrollingText * st, SDL_Surface * surf) +{ + if ((st->text == NULL) || (st->font == NULL)) + return; + + st->pos -= 3; + if (st->pos < -st->text_width) + st->pos = surf->w; +} + +void +scrolling_text_draw (ScrollingText * st, SDL_Surface * surf, int y) +{ + if ((st->text == NULL) || (st->font == NULL)) + return; + + SoFont_PutString (st->font, surf, st->pos, y, st->text, NULL); +} + +static int +scrolling_text_width (ScrollingText * st) +{ + if ((st->text == NULL) || (st->font == NULL)) + return 0; + + return SoFont_TextWidth (st->font, st->text) + 400; +} diff --git a/scrollingtext.h b/scrollingtext.h new file mode 100644 index 0000000..9a9c462 --- /dev/null +++ b/scrollingtext.h @@ -0,0 +1,21 @@ +/* file created on Wed Aug 28 15:38:28 CEST 2002 by doom */ + +#ifndef _scrollingtext_H +#define _scrollingtext_H + +/// SCROLLING TEXT + +#include "sofont.h" +#include "glSDL.h" + +typedef struct _SCROLLING_TEXT ScrollingText; + +/// mettre NULL pour créer un objet vide.. et affecter ce qu'on veut + tard +ScrollingText *scrolling_text_new (const char *text, SoFont * font); +void scrolling_text_set_font (ScrollingText * st, SoFont * font); +void scrolling_text_set_text (ScrollingText * st, const char *text); + +void scrolling_text_update (ScrollingText * st, SDL_Surface * surf); +void scrolling_text_draw (ScrollingText * st, SDL_Surface * surf, int y); + +#endif /* _scrollingtext_H */ diff --git a/sofont.c b/sofont.c new file mode 100644 index 0000000..c2ccb27 --- /dev/null +++ b/sofont.c @@ -0,0 +1,614 @@ +#include "stdlib.h" +#include "sofont.h" +#include "string.h" + +struct _SOFONT +{ + int height; + IIM_Surface *picture; + int *CharPos; + int *Spacing; + + int max_i, spacew, cursShift; + Uint32 background; +}; + +// protected +int SoFont_DoStartNewChar (SoFont * font, Sint32 x); +void SoFont_CleanSurface (SoFont * font); + + +int +SoFont_FontHeight (SoFont * font) +{ + return font->height; +} + +int +SoFont_getMinChar (SoFont * font) +{ + return START_CHAR; +} + +int +SoFont_getMaxChar (SoFont * font) +{ + return font->max_i; +} + + +SoFont * +SoFont_new () +{ + SoFont *font = (SoFont *) malloc (sizeof (SoFont)); + + font->picture = NULL; + font->CharPos = NULL; + font->Spacing = NULL; + font->height = 0; + font->max_i = 0; + font->spacew = 0; + font->cursShift = 0; + font->background = 0; + + return font; +} + +void +SoFont_free (SoFont * font) +{ +/* if (font->picture) + SDL_FreeSurface (font->picture); */ + if (font->CharPos) + free (font->CharPos); + if (font->Spacing) + free (font->Spacing); +} + +// SoFontUtilities + +static Uint32 +SoFontGetPixel (SDL_Surface * Surface, Sint32 X, Sint32 Y) +{ + Uint8 *bits; + Uint32 Bpp; + Uint8 r, g, b; + + if (!Surface) { + fprintf (stderr, "SoFontGetPixel: No surface!\n"); + return 0; + } + if ((X < 0) || (X >= Surface->w)) { + fprintf (stderr, "SoFontGetPixel: X (%d)" " out of range!\n", X); + return 0; + } + + Bpp = Surface->format->BytesPerPixel; + + bits = ((Uint8 *) Surface->pixels) + Y * Surface->pitch + X * Bpp; + + switch (Bpp) { + case 1: + return *((Uint8 *) Surface->pixels + Y * Surface->pitch + X); + break; + case 2: + return *((Uint16 *) Surface->pixels + Y * Surface->pitch / 2 + X); + break; + case 3: + // Format/endian independent + r = *((bits) + Surface->format->Rshift / 8); + g = *((bits) + Surface->format->Gshift / 8); + b = *((bits) + Surface->format->Bshift / 8); + return SDL_MapRGB (Surface->format, r, g, b); + break; + case 4: + return *((Uint32 *) Surface->pixels + Y * Surface->pitch / 4 + X); + break; + } + fprintf (stderr, "SoFontGetPixel: Unsupported pixel format!\n"); + return 0; // David (to get rid of warning) +} + +static void +SoFontSetPixel (SDL_Surface * Surface, Sint32 X, Sint32 Y, Uint32 c) +{ + Uint8 *bits; + Uint32 Bpp; + Uint8 r, g, b; + + if (!Surface) { + fprintf (stderr, "SoFontSetPixel: No surface!\n"); + return; + } + if ((X < 0) || (X >= Surface->w)) { + fprintf (stderr, "SoFontSetPixel: X (%d)" " out of range!\n", X); + return; + } + + Bpp = Surface->format->BytesPerPixel; + + bits = ((Uint8 *) Surface->pixels) + Y * Surface->pitch + X * Bpp; + + switch (Bpp) { + case 1: + *((Uint8 *) Surface->pixels + Y * Surface->pitch + X) = (Uint8) c; + break; + case 2: + *((Uint16 *) Surface->pixels + Y * Surface->pitch / 2 + X) = (Uint16) c; + break; + case 3: + // Format/endian independent + SDL_GetRGB (c, Surface->format, &r, &g, &b); + *((bits) + Surface->format->Rshift / 8) = r; + *((bits) + Surface->format->Gshift / 8) = g; + *((bits) + Surface->format->Bshift / 8) = b; + break; + case 4: + *((Uint32 *) Surface->pixels + Y * Surface->pitch / 4 + X) = c; + break; + } +} + +#if 0 +static void +clipx (SDL_Rect * srcrect, SDL_Rect * dstrect, SDL_Rect * clip) +{ + int dwx; + + // Use if destination have the same size than source. + int dx = clip->x - dstrect->x; + + int sw = srcrect->w; // Because SDL_Rect.w are + + // unsigned. + int dw = dstrect->w; + + + if (dx > 0) { + srcrect->x += dx; + dstrect->x += dx; + + sw -= dx; + dw -= dx; + } + + dwx = (dstrect->x + dstrect->w) - (clip->x + clip->w); + + if (dwx > 0) { + sw -= dwx; + dw -= dwx; + } + + if (sw > 0) + srcrect->w = sw; + else + srcrect->w = 0; + + if (dw > 0) + dstrect->w = dw; + else + dstrect->w = 0; +} +#endif + +static void +sdcRects (SDL_Rect * source, SDL_Rect * destination, SDL_Rect clipping) +{ + int dwx, dhy; + + // Use if destination have the same size than source & + // cliping on destination + int dx = clipping.x - destination->x; + int dy = clipping.y - destination->y; + + int sw = source->w; + int sh = source->h; + + if (dx > 0) { + source->x += dx; + destination->x += dx; + + sw -= dx; + destination->w -= dx; + } + if (dy > 0) { + source->y += dy; + destination->y += dy; + + sh -= dy; + destination->h -= dy; + } + + dwx = (destination->x + destination->w) - (clipping.x + clipping.w); + dhy = (destination->y + destination->h) - (clipping.y + clipping.h); + + if (dwx > 0) { + sw -= dwx; + destination->w -= dwx; + } + if (dhy > 0) { + sh -= dhy; + destination->h -= dhy; + } + + if (sw > 0) + source->w = sw; + else + source->w = 0; + + if (sh > 0) + source->h = sh; + else + source->h = 0; + +} + +// end of SoFontUtilities + +int +SoFont_DoStartNewChar (SoFont * font, Sint32 x) +{ + if (!font->picture) + return 0; + return SoFontGetPixel (font->picture->surf, x, 0) == + SDL_MapRGB (font->picture->surf->format, 255, 0, 255); +} + +void +SoFont_CleanSurface (SoFont * font) +{ + int x = 0, y = 0; + Uint32 pix; + + if (!font->picture) + return; + + pix = SDL_MapRGB (font->picture->surf->format, 255, 0, 255); + + while (x < font->picture->w) { + y = 0; +//Why clean the entire surface? IMHO, S[o]Font should only ever +//touch - or even care about - the very top pixel row. +// while(y < picture->h) +// { + if (SoFontGetPixel (font->picture->surf, x, y) == pix) + SoFontSetPixel (font->picture->surf, x, y, font->background); +// y++; +// } + x++; + } +} + + +int +SoFont_load (SoFont * font, IIM_Surface * FontSurface) +{ + int x = 0, i = 0, p = 0, s = 0; + int cursBegin = 0; + int cursWidth = 0; + + int _CharPos[256]; + int _Spacing[256]; + + if (!FontSurface) { + fprintf (stderr, "SoFont recieved a NULL SDL_Surface\n"); + return 0; + } + font->picture = FontSurface; + font->height = font->picture->h - 1; + while (x < font->picture->w) { + if (SoFont_DoStartNewChar (font, x)) { + if (i) + _Spacing[i - 1] = 1 + x - s; + p = x; + while ((x < font->picture->w - 1) && (SoFont_DoStartNewChar (font, x))) + x++; + s = x; + // CharPos[i++] = (p + x) / 2; + _CharPos[i++] = (p + x + 1) / 2; // David, Kobo Deluxe + } + x++; + } + // Note that spacing is not needed for the last char, + // as it's just used for the blit width calculation. + if (i) + _Spacing[i - 1] = 1 + x - s; + _Spacing[i] = 0; + _CharPos[i++] = font->picture->w; + + font->max_i = START_CHAR + i - 1; + font->background = SoFontGetPixel (font->picture->surf, 0, font->height); + SDL_SetColorKey (font->picture->surf, SDL_SRCCOLORKEY, font->background); + SoFont_CleanSurface (font); + + font->CharPos = (int *) malloc (i * sizeof (int)); + font->Spacing = (int *) malloc (i * sizeof (int)); + memcpy (font->CharPos, _CharPos, i * sizeof (int)); + memcpy (font->Spacing, _Spacing, i * sizeof (int)); + + // We search for a smart space width: + // Changed from "a", "A", "0" for Kobo Deluxe. + // Spaces were *way* to wide! //David + font->spacew = 0; + if (!font->spacew) + font->spacew = SoFont_TextWidth (font, "i") * 3 / 2; + if (!font->spacew) + font->spacew = SoFont_TextWidth (font, "I") * 3 / 2; + if (!font->spacew) + font->spacew = SoFont_TextWidth (font, ".") * 3 / 2; + if (!font->spacew) + font->spacew = font->CharPos[1] - font->CharPos[0]; + + // We search for a smart cursor position: + font->cursShift = 0; + if ('|' > font->max_i) + return 1; // No bar in this font! + + if (font->background == + SoFontGetPixel (font->picture->surf, font->CharPos['|' - START_CHAR], + font->height / 2)) { + // Up to the first | color + for (cursBegin = 0; cursBegin <= SoFont_TextWidth (font, "|"); + cursBegin++) + if (font->background != SoFontGetPixel (font->picture->surf, + font->CharPos['|' - + START_CHAR] + + cursBegin, font->height / 2)) + break; + // Up to the end of the | color + for (cursWidth = 0; cursWidth <= SoFont_TextWidth (font, "|"); + cursWidth++) + if (font->background == SoFontGetPixel (font->picture->surf, + font->CharPos['|' - + START_CHAR] + + cursBegin + cursWidth, + font->height / 2)) + break; + } + else { + // Up to the end of the | color + for (cursWidth = 0; cursWidth <= SoFont_TextWidth (font, "|"); + cursWidth++) + if (font->background == SoFontGetPixel (font->picture->surf, + font->CharPos['|' - + START_CHAR] + + cursWidth, font->height / 2)) + break; + } + font->cursShift = cursBegin + 1; // cursWidth could be used if + // image format changes. + + return 1; +} + +void +SoFont_Refresh(SoFont * font) +{ + font->background = SoFontGetPixel (font->picture->surf, 0, font->height); + SDL_SetColorKey (font->picture->surf, SDL_SRCCOLORKEY, font->background); + SoFont_CleanSurface (font); +} + +void +SoFont_PutString (SoFont * font, SDL_Surface * Surface, int x, int y, + const char *text, SDL_Rect * clip) +{ + int sx = x; + int ofs, i = 0; + SDL_Rect srcrect, dstrect; + + if ((!font->picture) || (!Surface) || (!text)) + return; + + while (text[i] != '\0') { + if (text[i] == ' ') { + x += font->spacew; + i++; + } + else if (text[i] == '\n') { + x = sx; + y += font->picture->h + 2; + i++; + } + else if ((text[i] >= START_CHAR) && (text[i] <= font->max_i)) { + ofs = text[i] - START_CHAR; + srcrect.w = dstrect.w = font->CharPos[ofs + 1] - font->CharPos[ofs]; + srcrect.h = dstrect.h = font->height; + srcrect.x = font->CharPos[ofs]; + srcrect.y = 1; + dstrect.x = x; + dstrect.y = y; + x += font->Spacing[ofs]; + if (clip) + sdcRects (&srcrect, &dstrect, *clip); + SDL_BlitSurface (font->picture->surf, &srcrect, Surface, &dstrect); + i++; + } + else + i++; // other chars are ignored + } +} + +void +SoFont_PutStringWithCursor (SoFont * font, SDL_Surface * Surface, int xs, + int y, const char *text, int cursPos, + SDL_Rect * clip, int showCurs) +{ + int ofs, i = 0, x = xs; + SDL_Rect srcrect, dstrect; + + if ((!font->picture) || (!Surface) || (!text)) + return; + if ('|' > font->max_i) + showCurs = 0; + + // We want the cursor to appear under the main text. + if (showCurs) { + while (text[i] != '\0') + if (i == cursPos) + break; + else if (text[i] == ' ') { + x += font->spacew; + i++; + } + else if ((text[i] >= START_CHAR) + && (text[i] <= font->max_i)) { + ofs = text[i] - START_CHAR; + x += font->Spacing[ofs]; + i++; + } + else + i++; + ofs = '|' - START_CHAR; + + srcrect.w = dstrect.w = font->CharPos[ofs + 1] - font->CharPos[ofs]; + srcrect.h = dstrect.h = font->height; + srcrect.x = font->CharPos[ofs]; + srcrect.y = 1; + dstrect.x = x - font->cursShift; + dstrect.y = y; + if (clip) + sdcRects (&srcrect, &dstrect, *clip); + SDL_BlitSurface (font->picture->surf, &srcrect, Surface, &dstrect); + } + // Then the text: + SoFont_PutString (font, Surface, xs, y, text, clip); +} + + +int +SoFont_TextWidth_MinMax (SoFont * font, const char *text, int min, int max) +{ + int ofs, x = 0, i = min; + + if (!font->picture) + return 0; + while ((text[i] != '\0') && (i < max)) { + if (text[i] == ' ') { + x += font->spacew; + i++; + } + else if ((text[i] >= START_CHAR) && (text[i] <= font->max_i)) { + ofs = text[i] - START_CHAR; + x += font->Spacing[ofs]; + i++; + } + else + i++; + } + return x; +} + +int +SoFont_TextWidth (SoFont * font, const char *text) +{ + return SoFont_TextWidth_MinMax (font, text, 0, 255); +} + + +void +SoFont_XCenteredString (SoFont * font, SDL_Surface * Surface, int y, + const char *text, SDL_Rect * clip) +{ + if (!font->picture) + return; + SoFont_PutString (font, Surface, + Surface->w / 2 - SoFont_TextWidth (font, text) / 2, y, + text, clip); +} + +void +SoFont_CenteredString_XY (SoFont * font, SDL_Surface * Surface, int x, int y, + const char *text, SDL_Rect * clip) +{ + if (!font->picture) + return; + SoFont_PutString (font, Surface, x - SoFont_TextWidth (font, text) / 2, + y - font->height / 2, text, clip); +} + +void +SoFont_CenteredString (SoFont * font, SDL_Surface * Surface, const char *text, + SDL_Rect * clip) +{ + if (!font->picture) + return; + SoFont_CenteredString_XY (font, Surface, Surface->clip_rect.w / 2, + Surface->clip_rect.h / 2, text, clip); +} + +void +SoFont_PutStringCleverCursor (SoFont * font, SDL_Surface * Surface, + const char *text, int cursPos, SDL_Rect * r, + SDL_Rect * clip, int showCurs) +{ + int w1, w2; + + if ((!font->picture) || (!text)) + return; + + w1 = SoFont_TextWidth_MinMax (font, text, 0, cursPos); + w2 = SoFont_TextWidth (font, text); + + if ((w2 < r->w) || (w1 < r->w / 2)) + SoFont_PutStringWithCursor (font, Surface, r->x, + r->y + (r->h - font->height) / 2, text, + cursPos, clip, showCurs); + else if (w1 < w2 - r->w / 2) + SoFont_PutStringWithCursor (font, Surface, r->x - w1 + r->w / 2, + r->y + (r->h - font->height) / 2, text, + cursPos, clip, showCurs); + else + SoFont_PutStringWithCursor (font, Surface, r->x - w2 + r->w, + r->y + (r->h - font->height) / 2, text, + cursPos, clip, showCurs); +} + +int +SoFont_TextCursorAt (SoFont * font, const char *text, int px) +{ + int ofs, x = 0, i = 0, ax = 0; + + if (!font->picture) + return 0; + + if (px <= 0) + return 0; + + while (text[i] != '\0') { + if (text[i] == ' ') { + x += font->spacew; + i++; + } + else if ((text[i] >= START_CHAR) && (text[i] <= font->max_i)) { + ofs = text[i] - START_CHAR; + x += font->Spacing[ofs]; + i++; + } + else + i++; + + if (px < (ax + x) / 2) + return (i - 1); + ax = x; + } + return i; +} + +int +SoFont_CleverTextCursorAt (SoFont * font, const char *text, int px, + int cursPos, SDL_Rect * r) +{ + int w1, w2; + + if ((!font->picture) || (!text)) + return 0; + w1 = SoFont_TextWidth_MinMax (font, text, 0, cursPos); + w2 = SoFont_TextWidth (font, text); + if ((w2 < r->w) || (w1 < r->w / 2)) + return SoFont_TextCursorAt (font, text, px); + else if (w1 < w2 - r->w / 2) + return SoFont_TextCursorAt (font, text, px + w1 - (r->w / 2)); + else + return SoFont_TextCursorAt (font, text, px + w2 - r->w); +} diff --git a/sofont.h b/sofont.h new file mode 100644 index 0000000..a04472a --- /dev/null +++ b/sofont.h @@ -0,0 +1,102 @@ + /* + * SoFont - SDL Object Font Library + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library + * General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; if not, write to the Free Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Originally from C Library: Karl Bartel + * + * * SFONT - SDL Font Library by Karl Bartel * + * * * + * * All functions are explained below. There are two versions of each * + * * funtction. The first is the normal one, the function with the * + * * 2 at the end can be used when you want to handle more than one font * + * * in your program. * + * * * + * + * Copied into a C++ object to allow multiple fonts by: Luc-Olivier de + * Charriere + * + * David Olofson : + * * Strings changed to 'const char *' + * * Cursor tests first check if '|' is present. + * * Shadowed variables fixed. + * * Garbage data in spacing table fixed. (Thanks to Andreas Spångberg for + * discovering this one!) + * + * Put back into C by Jean-Christophe Hoelt */ + +#ifndef __SOFONT_H +#define __SOFONT_H + +#include "glSDL.h" +#include "IosImgProcess.h" + +typedef struct _SOFONT SoFont; + +SoFont *SoFont_new (); +void SoFont_free (SoFont * font); + +int SoFont_load (SoFont * font, IIM_Surface * FontSurface); +void SoFont_Refresh(SoFont * font); + +/// Blits a string to a surface +/// Destination: the suface you want to blit to +/// text: a string containing the text you want to blit. +void SoFont_PutString (SoFont * font, SDL_Surface * Surface, int x, int y, + const char *text, SDL_Rect * clip /*=NULL*/ ); +void SoFont_PutStringWithCursor (SoFont * font, SDL_Surface * Surface, + int x, int y, const char *text, + int cursPos, SDL_Rect * clip /*=NULL*/ , int showCurs /*=true*/ + ); + +/// Returns the width of "text" in pixels +int SoFont_TextWidth (SoFont * font, const char *text); +int SoFont_TextWidth_MinMax (SoFont * font, const char *text, + int min /*=0*/ , int max /*=255*/ ); + +int SoFont_FontHeight (SoFont * font); + +/// Blits a string to with centered x position +void SoFont_XCenteredString (SoFont * font, SDL_Surface * Surface, int y, + const char *text, SDL_Rect * clip /*=NULL*/ ); + +/// Blits a string to with centered x & y position +void SoFont_CenteredString (SoFont * font, SDL_Surface * Surface, + const char *text, SDL_Rect * clip /*=NULL*/ ); + +/// Blits a string to with centered around x & y positions +void SoFont_CenteredString_XY (SoFont * font, SDL_Surface * Surface, int x, + int y, const char *text, + + SDL_Rect * clip /*=NULL*/ ); + +/// This was specially developped for GUI +void SoFont_PutStringCleverCursor (SoFont * font, SDL_Surface * Surface, + const char *text, int cursPos, + SDL_Rect * r, + SDL_Rect * clip /*=NULL*/ , + + int showCurs /*=true*/ ); + +/// Gives the cursor position given a x-axix point in the text +int SoFont_TextCursorAt (SoFont * font, const char *text, int px); +int SoFont_CleverTextCursorAt (SoFont * font, const char *text, int px, + int cursPos, SDL_Rect * r); + +#define START_CHAR 33 +int SoFont_getMinChar (SoFont * font); +int SoFont_getMaxChar (SoFont * font); + +#endif