| @@ -35,6 +35,7 @@ DebugSprite::DebugSprite(Game *game) | |||||
| { | { | ||||
| data = new DebugSpriteData(); | data = new DebugSpriteData(); | ||||
| data->game = game; | data->game = game; | ||||
| Ticker::Ref(game); | |||||
| data->tiler = Tiler::Register("art/test/character-dress.png"); | data->tiler = Tiler::Register("art/test/character-dress.png"); | ||||
| data->x = 320; | data->x = 320; | ||||
| data->y = 206; | data->y = 206; | ||||
| @@ -71,6 +72,7 @@ void DebugSprite::TickDraw(float deltams) | |||||
| DebugSprite::~DebugSprite() | DebugSprite::~DebugSprite() | ||||
| { | { | ||||
| Ticker::Unref(data->game); | |||||
| Tiler::Deregister(data->tiler); | Tiler::Deregister(data->tiler); | ||||
| delete data; | delete data; | ||||
| } | } | ||||
| @@ -85,7 +85,7 @@ int Dict::MakeSlot(char const *name) | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| data->entities[id]->Ref(); | |||||
| Ticker::Ref(data->entities[id]); | |||||
| } | } | ||||
| return id; | return id; | ||||
| @@ -93,14 +93,14 @@ int Dict::MakeSlot(char const *name) | |||||
| void Dict::RemoveSlot(int id) | void Dict::RemoveSlot(int id) | ||||
| { | { | ||||
| if (data->entities[id]->Unref() == 0) | |||||
| if (Ticker::Unref(data->entities[id]) == 0) | |||||
| data->entities[id] = NULL; | data->entities[id] = NULL; | ||||
| } | } | ||||
| void Dict::SetEntity(int id, Entity *entity) | void Dict::SetEntity(int id, Entity *entity) | ||||
| { | { | ||||
| entity->Ref(); | |||||
| Ticker::Ref(entity); | |||||
| data->entities[id] = entity; | data->entities[id] = entity; | ||||
| } | } | ||||
| @@ -63,13 +63,3 @@ void Entity::TickDraw(float deltams) | |||||
| #endif | #endif | ||||
| } | } | ||||
| void Entity::Ref() | |||||
| { | |||||
| ref++; | |||||
| } | |||||
| int Entity::Unref() | |||||
| { | |||||
| return --ref; | |||||
| } | |||||
| @@ -22,10 +22,6 @@ class Entity | |||||
| friend class TickerData; | friend class TickerData; | ||||
| friend class Dict; | friend class Dict; | ||||
| public: | |||||
| virtual void Ref(); | |||||
| virtual int Unref(); | |||||
| protected: | protected: | ||||
| typedef enum | typedef enum | ||||
| { | { | ||||
| @@ -47,8 +43,8 @@ protected: | |||||
| virtual void TickGame(float deltams); | virtual void TickGame(float deltams); | ||||
| virtual void TickDraw(float deltams); | virtual void TickDraw(float deltams); | ||||
| Entity *next; | |||||
| int ref, destroy; | |||||
| Entity *next, *autonext; | |||||
| int ref, autorelease, destroy; | |||||
| #if !FINAL_RELEASE | #if !FINAL_RELEASE | ||||
| enum | enum | ||||
| @@ -74,7 +74,14 @@ void Font::TickDraw(float deltams) | |||||
| { | { | ||||
| Entity::TickDraw(deltams); | Entity::TickDraw(deltams); | ||||
| if (data->img) | |||||
| if (destroy) | |||||
| { | |||||
| if (data->img) | |||||
| SDL_FreeSurface(data->img); | |||||
| else | |||||
| glDeleteTextures(1, &data->texture); | |||||
| } | |||||
| else if (data->img) | |||||
| { | { | ||||
| data->width = data->img->w / 16; | data->width = data->img->w / 16; | ||||
| data->height = data->img->h / 16; | data->height = data->img->h / 16; | ||||
| @@ -91,11 +98,6 @@ void Font::TickDraw(float deltams) | |||||
| SDL_FreeSurface(data->img); | SDL_FreeSurface(data->img); | ||||
| data->img = NULL; | data->img = NULL; | ||||
| } | } | ||||
| else if (ref == 0) | |||||
| { | |||||
| glDeleteTextures(1, &data->texture); | |||||
| destroy = 1; | |||||
| } | |||||
| } | } | ||||
| char const *Font::GetName() | char const *Font::GetName() | ||||
| @@ -16,6 +16,15 @@ | |||||
| #include "glmapview.h" | #include "glmapview.h" | ||||
| #include "debugfps.h" | #include "debugfps.h" | ||||
| static gboolean delayed_quit(GtkWidget *w, GdkEvent *e, void *data) | |||||
| { | |||||
| (void)w; | |||||
| (void)e; | |||||
| (void)data; | |||||
| gtk_main_quit(); | |||||
| return TRUE; | |||||
| } | |||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||
| { | { | ||||
| /* Initialize GTK */ | /* Initialize GTK */ | ||||
| @@ -41,12 +50,14 @@ int main(int argc, char **argv) | |||||
| GlMapView *glmapview = new GlMapView(builder); | GlMapView *glmapview = new GlMapView(builder); | ||||
| /* Show window. We're good to go! */ | /* Show window. We're good to go! */ | ||||
| gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(builder, "window"))); | |||||
| GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window")); | |||||
| gtk_widget_show_all(window); | |||||
| gtk_signal_connect(GTK_OBJECT(window), "delete_event", | |||||
| GTK_SIGNAL_FUNC(delayed_quit), NULL); | |||||
| g_object_unref(G_OBJECT(builder)); | g_object_unref(G_OBJECT(builder)); | ||||
| new DebugFps(); | |||||
| glmapview->LoadMap("maps/testmap.tmx"); | glmapview->LoadMap("maps/testmap.tmx"); | ||||
| glmapview->SetFocus(); | |||||
| new DebugFps(); | |||||
| gtk_main(); | gtk_main(); | ||||
| @@ -4,7 +4,6 @@ | |||||
| <!-- interface-naming-policy project-wide --> | <!-- interface-naming-policy project-wide --> | ||||
| <object class="GtkWindow" id="window"> | <object class="GtkWindow" id="window"> | ||||
| <property name="title" translatable="yes">Deus Hax Editor</property> | <property name="title" translatable="yes">Deus Hax Editor</property> | ||||
| <signal name="delete_event" handler="gtk_main_quit"/> | |||||
| <child> | <child> | ||||
| <object class="GtkVBox" id="vbox1"> | <object class="GtkVBox" id="vbox1"> | ||||
| <property name="visible">True</property> | <property name="visible">True</property> | ||||
| @@ -23,8 +22,8 @@ | |||||
| <child> | <child> | ||||
| <object class="GtkImageMenuItem" id="imagemenuitem1"> | <object class="GtkImageMenuItem" id="imagemenuitem1"> | ||||
| <property name="visible">True</property> | <property name="visible">True</property> | ||||
| <property name="related_action">action_new</property> | |||||
| <property name="use_action_appearance">True</property> | <property name="use_action_appearance">True</property> | ||||
| <property name="related_action">action_new</property> | |||||
| <property name="use_underline">True</property> | <property name="use_underline">True</property> | ||||
| <property name="use_stock">True</property> | <property name="use_stock">True</property> | ||||
| </object> | </object> | ||||
| @@ -32,8 +31,8 @@ | |||||
| <child> | <child> | ||||
| <object class="GtkImageMenuItem" id="imagemenuitem2"> | <object class="GtkImageMenuItem" id="imagemenuitem2"> | ||||
| <property name="visible">True</property> | <property name="visible">True</property> | ||||
| <property name="related_action">action_open</property> | |||||
| <property name="use_action_appearance">True</property> | <property name="use_action_appearance">True</property> | ||||
| <property name="related_action">action_open</property> | |||||
| <property name="use_underline">True</property> | <property name="use_underline">True</property> | ||||
| <property name="use_stock">True</property> | <property name="use_stock">True</property> | ||||
| </object> | </object> | ||||
| @@ -67,6 +66,7 @@ | |||||
| <property name="use_stock">True</property> | <property name="use_stock">True</property> | ||||
| <property name="always_show_image">True</property> | <property name="always_show_image">True</property> | ||||
| <signal name="activate" handler="gtk_main_quit"/> | <signal name="activate" handler="gtk_main_quit"/> | ||||
| <signal name="activate" handler="gtk_true"/> | |||||
| </object> | </object> | ||||
| </child> | </child> | ||||
| </object> | </object> | ||||
| @@ -158,8 +158,8 @@ | |||||
| <child> | <child> | ||||
| <object class="GtkToolButton" id="toolbutton2"> | <object class="GtkToolButton" id="toolbutton2"> | ||||
| <property name="visible">True</property> | <property name="visible">True</property> | ||||
| <property name="related_action">action_new</property> | |||||
| <property name="use_action_appearance">True</property> | <property name="use_action_appearance">True</property> | ||||
| <property name="related_action">action_new</property> | |||||
| <property name="label" translatable="yes">toolbutton</property> | <property name="label" translatable="yes">toolbutton</property> | ||||
| <property name="use_underline">True</property> | <property name="use_underline">True</property> | ||||
| </object> | </object> | ||||
| @@ -171,8 +171,8 @@ | |||||
| <child> | <child> | ||||
| <object class="GtkToolButton" id="toolbutton1"> | <object class="GtkToolButton" id="toolbutton1"> | ||||
| <property name="visible">True</property> | <property name="visible">True</property> | ||||
| <property name="related_action">action_open</property> | |||||
| <property name="use_action_appearance">True</property> | <property name="use_action_appearance">True</property> | ||||
| <property name="related_action">action_open</property> | |||||
| <property name="label" translatable="yes">Open...</property> | <property name="label" translatable="yes">Open...</property> | ||||
| <property name="use_underline">True</property> | <property name="use_underline">True</property> | ||||
| </object> | </object> | ||||
| @@ -184,8 +184,8 @@ | |||||
| <child> | <child> | ||||
| <object class="GtkToolButton" id="toolbutton4"> | <object class="GtkToolButton" id="toolbutton4"> | ||||
| <property name="visible">True</property> | <property name="visible">True</property> | ||||
| <property name="related_action">action_save</property> | |||||
| <property name="use_action_appearance">True</property> | <property name="use_action_appearance">True</property> | ||||
| <property name="related_action">action_save</property> | |||||
| <property name="label" translatable="yes">Save</property> | <property name="label" translatable="yes">Save</property> | ||||
| <property name="use_underline">True</property> | <property name="use_underline">True</property> | ||||
| </object> | </object> | ||||
| @@ -50,11 +50,10 @@ GlMapView::GlMapView(GtkBuilder *builder) | |||||
| * stealing time from the GTK loop when the callback time exceeds | * stealing time from the GTK loop when the callback time exceeds | ||||
| * the timeout value. */ | * the timeout value. */ | ||||
| g_idle_add((GSourceFunc)IdleTickSignal, this); | g_idle_add((GSourceFunc)IdleTickSignal, this); | ||||
| gtk_quit_add(0, (GtkFunction)ShutdownSignal, this); | |||||
| gtk_signal_connect(GTK_OBJECT(glarea), "realize", | gtk_signal_connect(GTK_OBJECT(glarea), "realize", | ||||
| GTK_SIGNAL_FUNC(SetupSignal), this); | GTK_SIGNAL_FUNC(SetupSignal), this); | ||||
| gtk_signal_connect(GTK_OBJECT(glarea), "destroy", | |||||
| GTK_SIGNAL_FUNC(DestroySignal), this); | |||||
| gtk_signal_connect(GTK_OBJECT(glarea), "expose_event", | gtk_signal_connect(GTK_OBJECT(glarea), "expose_event", | ||||
| GTK_SIGNAL_FUNC(DrawSignal), this); | GTK_SIGNAL_FUNC(DrawSignal), this); | ||||
| gtk_signal_connect(GTK_OBJECT(glarea), "configure_event", | gtk_signal_connect(GTK_OBJECT(glarea), "configure_event", | ||||
| @@ -75,17 +74,28 @@ void GlMapView::LoadMap(char const *path) | |||||
| { | { | ||||
| // FIXME: detect when the map viewer is killed | // FIXME: detect when the map viewer is killed | ||||
| mapviewer = new MapViewer(path); | mapviewer = new MapViewer(path); | ||||
| Ticker::Ref(mapviewer); | |||||
| UpdateAdjustments(); | UpdateAdjustments(); | ||||
| } | } | ||||
| void GlMapView::SetFocus() | |||||
| void GlMapView::CloseMap() | |||||
| { | { | ||||
| gtk_widget_grab_focus(glarea); | |||||
| if (mapviewer) | |||||
| Ticker::Unref(mapviewer); | |||||
| mapviewer = NULL; | |||||
| UpdateAdjustments(); | |||||
| } | } | ||||
| gboolean GlMapView::IdleTick() | gboolean GlMapView::IdleTick() | ||||
| { | { | ||||
| if (Ticker::Finished()) | |||||
| { | |||||
| gtk_main_quit(); | |||||
| return FALSE; | |||||
| } | |||||
| // FIXME: do not do anything if the previous tick was too recent? | // FIXME: do not do anything if the previous tick was too recent? | ||||
| ticking = TRUE; | ticking = TRUE; | ||||
| @@ -104,6 +114,7 @@ gboolean GlMapView::IdleTick() | |||||
| gboolean GlMapView::Setup() | gboolean GlMapView::Setup() | ||||
| { | { | ||||
| /* Set up display */ | /* Set up display */ | ||||
| gtk_widget_grab_focus(glarea); | |||||
| if (gtk_gl_area_make_current(GTK_GL_AREA(glarea))) | if (gtk_gl_area_make_current(GTK_GL_AREA(glarea))) | ||||
| Video::Setup(glarea->allocation.width, glarea->allocation.height); | Video::Setup(glarea->allocation.width, glarea->allocation.height); | ||||
| @@ -112,9 +123,13 @@ gboolean GlMapView::Setup() | |||||
| return TRUE; | return TRUE; | ||||
| } | } | ||||
| gboolean GlMapView::Destroy() | |||||
| gboolean GlMapView::Shutdown() | |||||
| { | { | ||||
| g_idle_remove_by_data(this); | |||||
| CloseMap(); | |||||
| Ticker::Shutdown(); | |||||
| /* Hijack the exit process by adding another level of gtk_main */ | |||||
| gtk_widget_set_sensitive(gtk_widget_get_toplevel(glarea), FALSE); | |||||
| gtk_main(); | |||||
| return TRUE; | return TRUE; | ||||
| } | } | ||||
| @@ -239,10 +254,9 @@ gboolean GlMapView::SetupSignal(GtkWidget *w, GlMapView *that) | |||||
| return that->Setup(); | return that->Setup(); | ||||
| } | } | ||||
| gboolean GlMapView::DestroySignal(GtkWidget *w, GlMapView *that) | |||||
| gboolean GlMapView::ShutdownSignal(GlMapView *that) | |||||
| { | { | ||||
| (void)w; | |||||
| return that->Destroy(); | |||||
| return that->Shutdown(); | |||||
| } | } | ||||
| gboolean GlMapView::DrawSignal(GtkWidget *w, GdkEventExpose *e, | gboolean GlMapView::DrawSignal(GtkWidget *w, GdkEventExpose *e, | ||||
| @@ -13,13 +13,13 @@ class GlMapView | |||||
| public: | public: | ||||
| GlMapView(GtkBuilder *builder); | GlMapView(GtkBuilder *builder); | ||||
| void LoadMap(char const *path); | void LoadMap(char const *path); | ||||
| void SetFocus(); | |||||
| void CloseMap(); | |||||
| private: | private: | ||||
| /* Private methods */ | /* Private methods */ | ||||
| gboolean IdleTick(); | gboolean IdleTick(); | ||||
| gboolean Setup(); | gboolean Setup(); | ||||
| gboolean Destroy(); | |||||
| gboolean Shutdown(); | |||||
| gboolean Draw(GdkEventExpose *e); | gboolean Draw(GdkEventExpose *e); | ||||
| void Scroll(double dx, double dy); | void Scroll(double dx, double dy); | ||||
| void UpdateAdjustments(); | void UpdateAdjustments(); | ||||
| @@ -30,7 +30,7 @@ private: | |||||
| /* Private signal slots */ | /* Private signal slots */ | ||||
| static gboolean IdleTickSignal(GlMapView *that); | static gboolean IdleTickSignal(GlMapView *that); | ||||
| static gboolean SetupSignal(GtkWidget *w, GlMapView *that); | static gboolean SetupSignal(GtkWidget *w, GlMapView *that); | ||||
| static gboolean DestroySignal(GtkWidget *w, GlMapView *that); | |||||
| static gboolean ShutdownSignal(GlMapView *that); | |||||
| static gboolean DrawSignal(GtkWidget *w, GdkEventExpose *e, | static gboolean DrawSignal(GtkWidget *w, GdkEventExpose *e, | ||||
| GlMapView *that); | GlMapView *that); | ||||
| static gboolean ReshapeSignal(GtkWidget *w, GdkEventConfigure *e, | static gboolean ReshapeSignal(GtkWidget *w, GdkEventConfigure *e, | ||||
| @@ -21,7 +21,6 @@ class SdlInputData | |||||
| friend class SdlInput; | friend class SdlInput; | ||||
| private: | private: | ||||
| Game *game; | |||||
| int mx, my; | int mx, my; | ||||
| }; | }; | ||||
| @@ -29,12 +28,11 @@ private: | |||||
| * Public SdlInput class | * Public SdlInput class | ||||
| */ | */ | ||||
| SdlInput::SdlInput(Game *game) | |||||
| SdlInput::SdlInput() | |||||
| { | { | ||||
| SDL_Init(SDL_INIT_TIMER); | SDL_Init(SDL_INIT_TIMER); | ||||
| data = new SdlInputData(); | data = new SdlInputData(); | ||||
| data->game = game; | |||||
| SDL_GetMouseState(&data->mx, &data->my); | SDL_GetMouseState(&data->mx, &data->my); | ||||
| } | } | ||||
| @@ -47,9 +45,6 @@ void SdlInput::TickGame(float deltams) | |||||
| { | { | ||||
| Entity::TickGame(deltams); | Entity::TickGame(deltams); | ||||
| if (data->game->Finished()) | |||||
| destroy = 1; | |||||
| /* Handle mouse input */ | /* Handle mouse input */ | ||||
| SDL_GetMouseState(&data->mx, &data->my); | SDL_GetMouseState(&data->mx, &data->my); | ||||
| @@ -58,7 +53,7 @@ void SdlInput::TickGame(float deltams) | |||||
| while (SDL_PollEvent(&event)) | while (SDL_PollEvent(&event)) | ||||
| { | { | ||||
| if (event.type == SDL_QUIT) | if (event.type == SDL_QUIT) | ||||
| data->game->Quit(); | |||||
| Ticker::Shutdown(); | |||||
| #if 0 | #if 0 | ||||
| else if (event.type == SDL_KEYDOWN) | else if (event.type == SDL_KEYDOWN) | ||||
| Input::KeyPressed(event.key.keysym.sym, deltams); | Input::KeyPressed(event.key.keysym.sym, deltams); | ||||
| @@ -19,7 +19,7 @@ class SdlInputData; | |||||
| class SdlInput : public Entity | class SdlInput : public Entity | ||||
| { | { | ||||
| public: | public: | ||||
| SdlInput(Game *game); | |||||
| SdlInput(); | |||||
| virtual ~SdlInput(); | virtual ~SdlInput(); | ||||
| protected: | protected: | ||||
| @@ -48,12 +48,12 @@ int main(int argc, char **argv) | |||||
| Game *game = new Game("maps/testmap.tmx"); | Game *game = new Game("maps/testmap.tmx"); | ||||
| /* Register an input driver and some debug stuff */ | /* Register an input driver and some debug stuff */ | ||||
| new SdlInput(game); | |||||
| new SdlInput(); | |||||
| new DebugFps(); | new DebugFps(); | ||||
| new DebugSprite(game); | new DebugSprite(game); | ||||
| //new DebugRecord("lolengine.ogg"); | //new DebugRecord("lolengine.ogg"); | ||||
| while (!game->Finished()) | |||||
| while (!Ticker::Finished()) | |||||
| { | { | ||||
| /* Tick the game */ | /* Tick the game */ | ||||
| Ticker::TickGame(); | Ticker::TickGame(); | ||||
| @@ -23,7 +23,8 @@ static class TickerData | |||||
| public: | public: | ||||
| TickerData() : | TickerData() : | ||||
| todo(0), nentities(0), | |||||
| todolist(0), autolist(0), | |||||
| nentities(0), | |||||
| frame(0), deltams(0), bias(0) | frame(0), deltams(0), bias(0) | ||||
| { | { | ||||
| for (int i = 0; i < Entity::GROUP_COUNT; i++) | for (int i = 0; i < Entity::GROUP_COUNT; i++) | ||||
| @@ -35,17 +36,26 @@ public: | |||||
| #if !FINAL_RELEASE | #if !FINAL_RELEASE | ||||
| if (nentities) | if (nentities) | ||||
| fprintf(stderr, "ERROR: still %i entities in ticker\n", nentities); | fprintf(stderr, "ERROR: still %i entities in ticker\n", nentities); | ||||
| if (autolist) | |||||
| { | |||||
| int count = 0; | |||||
| for (Entity *e = autolist; e; e = e->autonext, count++) | |||||
| ; | |||||
| fprintf(stderr, "ERROR: still %i autoreleased entities\n", count); | |||||
| } | |||||
| fprintf(stderr, "INFO: %i frames required to quit\n", | |||||
| frame - quitframe); | |||||
| #endif | #endif | ||||
| } | } | ||||
| private: | private: | ||||
| /* Entity management */ | /* Entity management */ | ||||
| Entity *todo; | |||||
| Entity *todolist, *autolist; | |||||
| Entity *list[Entity::GROUP_COUNT]; | Entity *list[Entity::GROUP_COUNT]; | ||||
| int nentities; | int nentities; | ||||
| /* Fixed framerate management */ | /* Fixed framerate management */ | ||||
| int frame; | |||||
| int frame, quitframe; | |||||
| Timer timer; | Timer timer; | ||||
| float deltams, bias; | float deltams, bias; | ||||
| } | } | ||||
| @@ -62,8 +72,55 @@ void Ticker::Register(Entity *entity) | |||||
| /* If we are called from its constructor, the object's vtable is not | /* If we are called from its constructor, the object's vtable is not | ||||
| * ready yet, so we do not know which group this entity belongs to. Wait | * ready yet, so we do not know which group this entity belongs to. Wait | ||||
| * until the first tick. */ | * until the first tick. */ | ||||
| entity->next = data->todo; | |||||
| data->todo = entity; | |||||
| entity->next = data->todolist; | |||||
| data->todolist = entity; | |||||
| /* Objects are autoreleased by default. Put them in a circular list. */ | |||||
| entity->autorelease = 1; | |||||
| entity->autonext = data->autolist; | |||||
| data->autolist = entity; | |||||
| entity->ref = 1; | |||||
| data->nentities++; | |||||
| } | |||||
| void Ticker::Ref(Entity *entity) | |||||
| { | |||||
| #if !FINAL_RELEASE | |||||
| if (entity->destroy) | |||||
| fprintf(stderr, "ERROR: refing entity scheduled for destruction\n"); | |||||
| #endif | |||||
| if (entity->autorelease) | |||||
| { | |||||
| /* Get the entity out of the autorelease list. This is usually | |||||
| * very fast since the first entry in autolist is the last | |||||
| * registered entity. */ | |||||
| for (Entity *e = data->autolist, *prev = NULL; e; | |||||
| prev = e, e = e->autonext) | |||||
| { | |||||
| if (e == entity) | |||||
| { | |||||
| if (prev) | |||||
| prev->autonext = e->autonext; | |||||
| else | |||||
| data->autolist = e->autonext; | |||||
| break; | |||||
| } | |||||
| } | |||||
| entity->autorelease = 0; | |||||
| } | |||||
| else | |||||
| entity->ref++; | |||||
| } | |||||
| int Ticker::Unref(Entity *entity) | |||||
| { | |||||
| #if !FINAL_RELEASE | |||||
| if (entity->ref <= 0) | |||||
| fprintf(stderr, "ERROR: dereferencing unreferenced entity\n"); | |||||
| if (entity->autorelease) | |||||
| fprintf(stderr, "ERROR: dereferencing autoreleased entity\n"); | |||||
| #endif | |||||
| return --entity->ref; | |||||
| } | } | ||||
| void Ticker::TickGame() | void Ticker::TickGame() | ||||
| @@ -73,6 +130,17 @@ void Ticker::TickGame() | |||||
| Profiler::Start(Profiler::STAT_TICK_GAME); | Profiler::Start(Profiler::STAT_TICK_GAME); | ||||
| #if 0 | |||||
| fprintf(stderr, "-------------------------------------\n"); | |||||
| for (int i = 0; i < Entity::GROUP_COUNT; i++) | |||||
| { | |||||
| fprintf(stderr, "Group %i\n", i); | |||||
| for (Entity *e = data->list[i]; e; e = e->next) | |||||
| fprintf(stderr, " \\-- %s (ref %i, destroy %i)\n", e->GetName(), e->ref, e->destroy); | |||||
| } | |||||
| #endif | |||||
| data->frame++; | data->frame++; | ||||
| data->deltams = data->timer.GetMs(); | data->deltams = data->timer.GetMs(); | ||||
| @@ -82,7 +150,8 @@ void Ticker::TickGame() | |||||
| * before inserting awaiting objects, because there is no way these | * before inserting awaiting objects, because there is no way these | ||||
| * are already marked for destruction. */ | * are already marked for destruction. */ | ||||
| for (int i = 0; i < Entity::GROUP_COUNT; i++) | for (int i = 0; i < Entity::GROUP_COUNT; i++) | ||||
| for (Entity *e = data->list[i], *prev = NULL; e; prev = e, e = e->next) | |||||
| for (Entity *e = data->list[i], *prev = NULL; e; ) | |||||
| { | |||||
| if (e->destroy) | if (e->destroy) | ||||
| { | { | ||||
| if (prev) | if (prev) | ||||
| @@ -90,20 +159,30 @@ void Ticker::TickGame() | |||||
| else | else | ||||
| data->list[i] = e->next; | data->list[i] = e->next; | ||||
| Entity *tmp = e; | |||||
| e = e->next; | |||||
| delete tmp; | |||||
| data->nentities--; | data->nentities--; | ||||
| delete e; | |||||
| } | } | ||||
| else | |||||
| { | |||||
| if (e->ref <= 0) | |||||
| e->destroy = 1; | |||||
| prev = e; | |||||
| e = e->next; | |||||
| } | |||||
| } | |||||
| /* Insert waiting objects into the appropriate lists */ | /* Insert waiting objects into the appropriate lists */ | ||||
| while (data->todo) | |||||
| while (data->todolist) | |||||
| { | { | ||||
| Entity *e = data->todo; | |||||
| data->todo = e->next; | |||||
| Entity *e = data->todolist; | |||||
| data->todolist = e->next; | |||||
| int i = e->GetGroup(); | int i = e->GetGroup(); | ||||
| e->next = data->list[i]; | e->next = data->list[i]; | ||||
| data->list[i] = e; | data->list[i] = e; | ||||
| data->nentities++; | |||||
| } | } | ||||
| /* Tick objects for the game loop */ | /* Tick objects for the game loop */ | ||||
| @@ -169,3 +248,20 @@ int Ticker::GetFrameNum() | |||||
| return data->frame; | return data->frame; | ||||
| } | } | ||||
| void Ticker::Shutdown() | |||||
| { | |||||
| /* We're bailing out. Release all autorelease objects. */ | |||||
| while (data->autolist) | |||||
| { | |||||
| data->autolist->ref--; | |||||
| data->autolist = data->autolist->autonext; | |||||
| } | |||||
| data->quitframe = data->frame; | |||||
| } | |||||
| int Ticker::Finished() | |||||
| { | |||||
| return !data->nentities; | |||||
| } | |||||
| @@ -20,11 +20,16 @@ class Ticker | |||||
| { | { | ||||
| public: | public: | ||||
| static void Register(Entity *entity); | static void Register(Entity *entity); | ||||
| static void Ref(Entity *entity); | |||||
| static int Unref(Entity *entity); | |||||
| static void TickGame(); | static void TickGame(); | ||||
| static void TickDraw(); | static void TickDraw(); | ||||
| static void ClampFps(float deltams); | static void ClampFps(float deltams); | ||||
| static int GetFrameNum(); | static int GetFrameNum(); | ||||
| static void Shutdown(); | |||||
| static int Finished(); | |||||
| }; | }; | ||||
| #endif // __DH_TICKER_H__ | #endif // __DH_TICKER_H__ | ||||
| @@ -83,7 +83,14 @@ void TileSet::TickDraw(float deltams) | |||||
| { | { | ||||
| Entity::TickDraw(deltams); | Entity::TickDraw(deltams); | ||||
| if (data->img) | |||||
| if (destroy) | |||||
| { | |||||
| if (data->img) | |||||
| SDL_FreeSurface(data->img); | |||||
| else | |||||
| glDeleteTextures(1, &data->texture); | |||||
| } | |||||
| else if (data->img) | |||||
| { | { | ||||
| glGenTextures(1, &data->texture); | glGenTextures(1, &data->texture); | ||||
| glBindTexture(GL_TEXTURE_2D, data->texture); | glBindTexture(GL_TEXTURE_2D, data->texture); | ||||
| @@ -97,11 +104,6 @@ void TileSet::TickDraw(float deltams) | |||||
| SDL_FreeSurface(data->img); | SDL_FreeSurface(data->img); | ||||
| data->img = NULL; | data->img = NULL; | ||||
| } | } | ||||
| else if (ref == 0) | |||||
| { | |||||
| glDeleteTextures(1, &data->texture); | |||||
| destroy = 1; | |||||
| } | |||||
| } | } | ||||
| char const *TileSet::GetName() | char const *TileSet::GetName() | ||||