diff --git a/cell.cpp b/cell.cpp index 3330f09a..2ed87a29 100644 --- a/cell.cpp +++ b/cell.cpp @@ -249,6 +249,7 @@ EX int dirdiff(int dd, int t) { EX int cellcount = 0; EX void destroy_cell(cell *c) { + while(c->contents) c->contents->unlist_and_unref(); tailored_delete(c); cellcount--; } diff --git a/celldrawer.cpp b/celldrawer.cpp index a2fb539e..2f01f31e 100644 --- a/celldrawer.cpp +++ b/celldrawer.cpp @@ -57,7 +57,6 @@ struct celldrawer { void draw(); bool cell_clipped(); void draw_fallanims(); - bool draw_shmup_monster(); void draw_gravity_particles(); void set_land_floor(const shiftmatrix& Vf); @@ -2975,7 +2974,7 @@ void celldrawer::draw() { Vboat = Vd; - draw_shmup_monster(); + FOR_LIST(li, c->contents) li->draw(self); poly_outline = OUTLINE_DEFAULT; diff --git a/locations.cpp b/locations.cpp index d6196bc3..bac9b566 100644 --- a/locations.cpp +++ b/locations.cpp @@ -22,6 +22,45 @@ extern int cellcount, heptacount; #define NOBARRIERS 127 #define NOBARRIERS2 125 +using cell_content_list = struct cell_content*; + +namespace shmup { struct monster; } + +struct cell_content { + cell_content *next; + cell_content_list *last; + virtual shmup::monster *as_monster() { return nullptr; } + + void remove_from_list() { + if(last) { last[0] = next; next->last = last; next = nullptr; last = nullptr; } + } + + void add_to_list(cell_content_list& l) { + remove_from_list(); + last = &l; + next = l; + next->last = &next; + l = this; + } + + int refs; + cell_content() { refs = 1; } + + virtual ~cell_content() { + remove_from_list(); + } + + void unref() { + refs--; + if(!refs) delete this; + } + + void unlist_and_unref() { remove_from_list(); unref(); } + virtual void draw(struct celldrawer& cd) {} + }; + +#define FOR_LIST(it, ml) for(cell_content *it = (ml); it; it = ml->next) + /** \brief Cell information for the game. struct cell builds on this */ struct gcell { @@ -98,11 +137,14 @@ struct gcell { #ifdef CELLID int cellid; #endif + + cell_content_list contents; gcell() { #ifdef CELLID - cellid = cellcount; + cellid = cellcount; #endif + contents = nullptr; } }; diff --git a/radar.cpp b/radar.cpp index 58327cf6..575f86a8 100644 --- a/radar.cpp +++ b/radar.cpp @@ -67,12 +67,8 @@ EX void addradar(const shiftpoint h1, const shiftpoint h2, color_t col) { void celldrawer::drawcell_in_radar() { #if CAP_SHMUP if(shmup::on) { - pair p = - shmup::monstersAt.equal_range(c); - for(shmup::mit it = p.first; it != p.second; it++) { - shmup::monster* m = it->second; + FOR_LIST(it, c->contents) if(shmup::monster *m = it->as_monster()) addradar(V*m->at, minf[m->type].glyph, minf[m->type].color, 0xFF0000FF); - } } #endif if(c->monst) diff --git a/shmup.cpp b/shmup.cpp index 8c327369..5c3dbcdf 100644 --- a/shmup.cpp +++ b/shmup.cpp @@ -33,9 +33,13 @@ ld sqdist(shiftpoint a, shiftpoint b) { EX namespace shmup { +EX hookset hooks_destroy_monster; + #if HDR -struct monster : dim_listener { +extern struct monster *mousetarget, *lmousetarget; + +struct monster : cell_content, dim_listener { eMonster type; cell *base; ///< on which base cell this monster currently is cell *torigin; ///< tortoises: origin, butterflies: last position @@ -60,18 +64,19 @@ struct monster : dim_listener { bool isVirtual; ///< off the screen: gmatrix is unknown, and pat equals at hyperpoint inertia;///< for frictionless lands - int refs; ///< +1 for every reference (parent, lists of active monsters) - int split_owner; ///< in splitscreen mode, which player handles this int split_tick; ///< in which tick was split_owner computed void reset(); - + + monster *as_monster() override { return this; } + void draw(celldrawer &cd) override; + monster() { reset(); - refs = 1; split_tick = -1; split_owner = -1; + split_tick = -1; split_owner = -1; no_targetting = false; - dead = false; inBoat = false; parent = NULL; + dead = false; inBoat = false; parent = nullptr; } eMonster get_parenttype() { return parent ? parent->type : moNone; } @@ -84,11 +89,15 @@ struct monster : dim_listener { void rebasePat(const shiftmatrix& new_pat, cell *tgt); - void remove_reference(); - + ~monster() { + if(mousetarget == this) mousetarget = nullptr; + if(lmousetarget == this) lmousetarget = nullptr; + callhooks(hooks_destroy_monster, this); + if(parent) parent->unref(); + } + void set_parent(monster *par) { - if(parent == par) return; - if(parent) parent->remove_reference(); + if(parent) parent->unref(); parent = par; parent->refs++; } @@ -117,22 +126,9 @@ EX eLand delayed_safety_land; bool lastdead = false; -EX multimap monstersAt; +EX cell_content_list active; -#if HDR -typedef multimap::iterator mit; -#endif - -EX vector active, nonvirtual, additional; - -void monster::remove_reference() { - refs--; - if(!refs) { - if(parent) parent->remove_reference(); - nonvirtual.erase(std::remove(nonvirtual.begin(), nonvirtual.end(), this), nonvirtual.end()); - delete this; - } - } +EX vector nonvirtual, additional; cell *findbaseAround(shiftpoint p, cell *around, int maxsteps) { @@ -170,9 +166,7 @@ cell *findbaseAround(const shiftmatrix& H, cell *around, int maxsteps) { return asinh(h[2]); } */ -void monster::store() { - monstersAt.insert(make_pair(base, this)); - } +void monster::store() { add_to_list(base->contents); } void monster::findpat() { isVirtual = !gmatrix.count(base) || invalid_matrix(gmatrix[base].T); @@ -1565,8 +1559,10 @@ EX bool verifyTeleport() { return true; } +#define FOR_MONSTERS_IN_LIST(it, m, ml) FOR_LIST(it, ml) if(monster *m = it->as_monster()) + void destroyMimics() { - for(monster *m: active) + FOR_MONSTERS_IN_LIST(it, m, active) if(isMimic(m->type)) m->dead = true; } @@ -2568,15 +2564,11 @@ void moveMonster(monster *m, int delta) { } } -void activateMonstersAt(cell *c) { - pair p = - monstersAt.equal_range(c); - for(mit it = p.first; it != p.second;) { - mit itplus = it; - itplus++; - active.push_back(it->second); - monstersAt.erase(it); - it = itplus; +void activate_monsters_at(cell *c) { + auto p = &c->contents; + while(*p) { + if(monster *m = (*p)->as_monster()) m->add_to_list(active); + else p = &((*p)->next); } if(c->monst && isMimic(c->monst)) c->monst = moNone; // mimics are awakened by awakenMimics @@ -2596,22 +2588,10 @@ void activateMonstersAt(cell *c) { enemy->inertia = random_spin() * point2(SCALE/3000., 0); } c->monst = moNone; - active.push_back(enemy); + enemy->add_to_list(active); } } -EX void fixStorage() { - - vector restore; - - for(auto it = monstersAt.begin(); it != monstersAt.end(); it++) - restore.push_back(it->second); - - monstersAt.clear(); - - for(monster *m: restore) m->store(); - } - EX hookset hooks_turn; /** the amount of time chars are disabled in PvP */ @@ -2687,28 +2667,24 @@ EX void turn(int delta) { // detect active monsters if(doall) - for(cell *c: currentmap->allcells()) activateMonstersAt(c); + for(cell *c: currentmap->allcells()) activate_monsters_at(c); else for(auto& p: gmatrix) - activateMonstersAt(p.first); - - /* printf("size: gmatrix = %ld, active = %ld, monstersAt = %ld, delta = %d\n", - gmatrix.size(), active.size(), monstersAt.size(), - delta); */ + activate_monsters_at(p.first); bool exists[motypes]; for(int i=0; ifindpat(); if(m->isVirtual) continue; else nonvirtual.push_back(m); exists[movegroup(m->type)] = true; } - for(monster *m: active) { + FOR_MONSTERS_IN_LIST(it, m, active) { switch(m->type) { case moPlayer: @@ -2724,7 +2700,7 @@ EX void turn(int delta) { } } - for(monster *m: active) { + FOR_MONSTERS_IN_LIST(it, m, active) { if(isMimic(m->type)) moveMimic(m); } @@ -2886,8 +2862,7 @@ EX void turn(int delta) { } } - for(monster *m: additional) - active.push_back(m); + for(monster *m: additional) m->add_to_list(active); additional.clear(); if(delayed_safety) { @@ -2895,18 +2870,13 @@ EX void turn(int delta) { delayed_safety = false; } - // deactivate all monsters - for(monster *m: active) - if(m->dead && m->type != moPlayer) { - if(m == mousetarget) mousetarget = NULL; - if(m == lmousetarget) lmousetarget = NULL; - m->remove_reference(); - } - else { + while(active) { + auto *m = (monster*) active; + if(m->dead && m->type != moPlayer) + m->unlist_and_unref(); + else m->store(); - } - - active.clear(); + } } EX void recall() { @@ -2954,29 +2924,13 @@ EX void init() { } EX bool boatAt(cell *c) { - pair p = - monstersAt.equal_range(c); - for(mit it = p.first; it != p.second; it++) { - monster* m = it->second; - if(m->inBoat) return true; - } + FOR_MONSTERS_IN_LIST(it, m, c->contents) if(m->inBoat) return true; return false; } EX hookset hooks_draw; -EX void clearMonsters() { - for(mit it = monstersAt.begin(); it != monstersAt.end(); it++) - delete(it->second); - for(monster *m: active) m->remove_reference(); - mousetarget = NULL; - lmousetarget = NULL; - monstersAt.clear(); - active.clear(); - } - EX void clearMemory() { - clearMonsters(); while(!traplist.empty()) traplist.pop(); curtime = 0; speed_saving = 0; @@ -2995,7 +2949,6 @@ void gamedata(hr::gamedata* gd) { gd->store(nextdragon); gd->store(visibleAt); gd->store(traplist); - gd->store(monstersAt); gd->store(active); gd->store(mousetarget); gd->store(lmousetarget); @@ -3018,7 +2971,7 @@ EX bool playerInBoat(int i) { } EX void destroyBoats(cell *c) { - for(monster *m: active) + FOR_MONSTERS_IN_LIST(it, m, active) if(m->base == c && m->inBoat) m->inBoat = false; } @@ -3039,17 +2992,7 @@ EX void addShmupHelp(string& out) { } auto hooks = addHook(hooks_clearmemory, 0, shmup::clearMemory) + - addHook(hooks_gamedata, 0, shmup::gamedata) + - addHook(hooks_removecells, 0, [] () { - for(mit it = monstersAt.begin(); it != monstersAt.end();) { - if(is_cell_removed(it->first)) { - monstersAt.insert(make_pair(nullptr, it->second)); - auto it0 = it; it++; - monstersAt.erase(it0); - } - else it++; - } - }); + addHook(hooks_gamedata, 0, shmup::gamedata); EX void switch_shmup() { stop_game(); @@ -3071,21 +3014,19 @@ EX void draw_collision_debug() { EX } -bool celldrawer::draw_shmup_monster() { - using namespace shmup; +void shmup::monster::draw(struct hr::celldrawer& cd) { #if CAP_SHAPES + using namespace shmup; + + auto m = this; + cell *c = cd.c; + auto& Vd = cd.Vd; + auto& V = cd.V; + auto& Vboat = cd.Vboat; - pair p = - monstersAt.equal_range(c); - - if(p.first == p.second) return false; ld zlev = -geom3::factor_to_lev(zlevel(tC0(Vd.T))); - vector monsters; - - for(mit it = p.first; it != p.second; it++) { - monster* m = it->second; - if(c != m->base) continue; // may happen in RogueViz Collatz + if(1) { m->pat = ggmatrix(m->base) * m->at; shiftmatrix view = V * m->at; @@ -3097,7 +3038,7 @@ bool celldrawer::draw_shmup_monster() { bool half_elliptic = elliptic && GDIM == 3 && WDIM == 2; bool mirrored = det(view.T) > 0; - if(half_elliptic && mirrored) continue; + if(half_elliptic && mirrored) return; if(!mouseout()) { if(m->no_targetting) ; else @@ -3133,7 +3074,7 @@ bool celldrawer::draw_shmup_monster() { int q = isize(ptds); if(q != isize(ptds) && !m->inBoat) pushdown(c, q, view, zlev, true, false); - if(callhandlers(false, hooks_draw, V, c, m)) continue; + if(callhandlers(false, hooks_draw, V, c, m)) return; switch(m->type) { case moPlayer: { @@ -3284,7 +3225,6 @@ bool celldrawer::draw_shmup_monster() { } #endif - return false; } } diff --git a/tour.cpp b/tour.cpp index c8a89219..2c81f893 100644 --- a/tour.cpp +++ b/tour.cpp @@ -1106,7 +1106,6 @@ EX slide default_slides[] = { resetview(); } if(mode == 3) { - shmup::clearMonsters(); gamestack::pop(); } }