/* * Copyright (C) 2002,2003 Daniel Heck * * 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. * * $Id: d_models.hh,v 1.1 2003/01/09 18:25:39 dheck Exp $ */ #include "SDL_image.h" #include "enigma.hh" #include "lua.hh" #include "options.hh" #include "px/px.hh" #include "px/cache.hh" #include "options.hh" #include "display.hh" #include #include #include using namespace display; using namespace std; using namespace px; namespace { class SurfaceAlloc_Alpha { public: Surface *acquire(const std::string &name) { return enigma::LoadImage(name.c_str()); } void release(Surface *s) { delete s; } }; class SurfaceAlloc { public: Surface * acquire(const std::string &name) { string fname = enigma::FindDataFile(string("gfx/") + name+".png"); SDL_Surface *s = IMG_Load(fname.c_str()); if (s) { SDL_Surface *img = 0; if (options::BitsPerPixel == 8) { SDL_SetColorKey(s, SDL_SRCCOLORKEY|SDL_RLEACCEL, SDL_MapRGB(s->format, 255,0,255)); img = SDL_DisplayFormat(s); } else { if (s->flags & SDL_SRCALPHA) { img = SDL_DisplayFormatAlpha(s); } else { SDL_SetColorKey(s, SDL_SRCCOLORKEY|SDL_RLEACCEL, SDL_MapRGB(s->format, 255,0,255)); img = SDL_DisplayFormat(s); } } if (img) { SDL_FreeSurface(s); return new Surface(img); } return new Surface(s); } return 0; } void release(Surface *s) {delete s;} }; typedef cache::Cache SurfaceCache; typedef cache::Cache SurfaceCache_Alpha; } //---------------------------------------- // Image //---------------------------------------- namespace { class Image { public: // Constructors. Image(px::Surface *i) : image(i), rect(), refcount(1) { rect.w = i->width(); rect.h = i->height(); } Image(px::Surface *i, px::Rect r) : image(i), rect(r), refcount(1) {} // Destructor. ~Image() { // `image' need not be deleted; this is handled in the // global image cache. } // Functions. void incref() { ++refcount; } void decref() {if (--refcount == 0) delete this;} void draw(px::GC &gc, int x, int y) { blit(gc, x, y, image, rect); } // Variables. px::Surface *image; px::Rect rect; // location of image inside surface int refcount; }; } //---------------------------------------- // ImageModel //---------------------------------------- namespace { class ImageModel : public Model { Image* image; int xoff, yoff; // relative origin of the image public: ImageModel(Image *i, int xo, int yo) : image(i), xoff(xo), yoff(yo) { image->incref(); } ImageModel(Surface *s, int xo, int yo) : image(new Image(s)), xoff(xo), yoff(yo) {} ImageModel(Surface *s, const Rect &r, int xo, int yo) : image(new Image(s, r)), xoff(xo), yoff(yo) {} ~ImageModel() {image->decref();} void draw(px::GC &gc, int x, int y) { assert(image); image->draw(gc, x+xoff, y+yoff); } void draw_shade(px::GC &gc, int x, int y) {} Model *clone() {return new ImageModel(image, xoff, yoff);} }; } //---------------------------------------- // ShadedModel //---------------------------------------- namespace { class ShadedModel : public Model { public: ShadedModel(Model *m, Model *sh) {model=m; shade=sh;} ~ShadedModel() { delete model; delete shade; } // Model interface void activate(double worldx, double worldy, bool is_sprite) { model->activate(worldx, worldy, is_sprite); } void deactivate() { model->deactivate(); } void set_callback(ModelCallback *cb) { model->set_callback(cb); } void reverse() { model->reverse(); } void draw(px::GC &gc, int x, int y) { if(model) model->draw(gc,x,y); } void draw_shade(px::GC &gc, int x, int y) { if(shade) shade->draw(gc,x,y); } Model *clone() { return new ShadedModel(model->clone(), shade->clone()); } private: Model *model, *shade; }; } //---------------------------------------------------------------------- // CompositeModel // // This kind of model consists of two layers: one background layer and // some foreground layer that is drawn over the background. Both // layers may be animated. This model make it easy to compose complex // animations from simple ones; see models-2d.lua for a few examples. //---------------------------------------------------------------------- namespace { class CompositeModel : public Model { Model *bg, *fg; public: CompositeModel(Model *b, Model *f) : bg(b), fg(f) {} ~CompositeModel() { delete bg; delete fg; } // Animation interface void set_callback(ModelCallback *cb) { bg->set_callback(cb); fg->set_callback(cb); } double duration() const { return 0; } void reverse() { bg->reverse(); fg->reverse(); } // Model interface void activate(double worldx, double worldy, bool is_sprite) { bg->activate(worldx, worldy, is_sprite); fg->activate(worldx, worldy, is_sprite); } void deactivate() { bg->deactivate(); fg->deactivate(); } void draw(px::GC &gc, int x, int y) { bg->draw(gc,x,y); fg->draw(gc,x,y); } void draw_shade(px::GC &gc, int x, int y) { bg->draw_shade(gc,x,y); } Model *clone() { return new CompositeModel(bg->clone(), fg->clone()); } }; } //---------------------------------------- // RandomModel //---------------------------------------- namespace { class RandomModel : public Model { vector modelnames; public: void draw(px::GC &gc, int x, int y) {} void draw_shade(px::GC &gc, int x, int y) {} void add_model(const string &name) {modelnames.push_back(name);} // Model interface Model *clone(); }; } //---------------------------------------- // Alias //---------------------------------------- namespace { class AliasModel : public Model { string name; public: AliasModel(const string &modelname) : name(modelname) {} void draw(px::GC &gc, int x, int y) {} void draw_shade(px::GC &gc, int x, int y) {} Model *clone(); }; } //---------------------------------------- // Animation //---------------------------------------- namespace { class AnimFrame : public px::Nocopy { public: AnimFrame(Model *m, double dur) : model(m), duration(dur) {} ~AnimFrame() { delete model; } Model *model; double duration; }; class AnimRep { public: AnimRep(bool l) : loop(l), refcount(1) {} ~AnimRep() { delete_sequence(frames.begin(), frames.end()); } double duration() const { double d = 0; for (unsigned i=0; iduration; return d; } vector frames; bool loop; int refcount; }; class Anim2d : public Model, public px::Nocopy { public: Anim2d(bool loop) : rep(new AnimRep(loop)) {} ~Anim2d(); double duration() const; void set_callback(ModelCallback *cb) { callback = cb; } void add_frame(Model *m, double duration); // Model interface void draw(px::GC &gc, int x, int y); void draw_shade(px::GC &gc, int x, int y); Model *clone() { return new Anim2d(rep); } void reverse() { reversep = !reversep; } void activate(double worldx, double worldy, bool is_sprite); void deactivate(); void tick(double dtime); bool has_changed(Rect &changed_region); bool is_garbage() const { return finishedp; } private: Anim2d(AnimRep *r); AnimRep *rep; unsigned curframe; // Current frame number. double frametime; // Elapsed time since frame was activated bool finishedp; // Animation has finished bool changedp; // Model state has changed since last redraw bool reversep; // Play the animation in reverse direction double posx, posy; // World coordinates of the sprite bool is_sprite; // False if item/stone animation ModelCallback *callback; }; }