/* * XEvil(TM) Copyright (C) 1994,2000 Steve Hardt and Michael Judge * http://www.xevil.com * satan@xevil.com * * 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, the file "gpl.txt"; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA, or visit http://www.gnu.org. */ // "locator.C" #if X11 #ifndef NO_PRAGMAS #pragma implementation "locator.h" #endif #endif // Include Files #include "stdafx.h" #if WIN32 #include "resource.h" #endif #include "utils.h" #include "coord.h" #include "id.h" #include "area.h" #include "physical.h" #include "locator.h" #include "actual.h" #include "sound_cmn.h" #include "sound.h" #if WIN32 #include "resource.h" #endif #include "bitmaps/locator/locator.bitmaps" // for arrows // When we reach this, start deleting objects. #define OL_LIST_WARN ((int)(OL_LIST_MAX * 0.9)) OLgridEntry::OLgridEntry(OLentry *e) { entry = e; prev = next = NULL; #if X11 wasDrawn = False; #endif } const Area &OLgridEntry::get_area() { assert(prev); return entry->physical->get_area(); } int OLgridEntry::get_drawing_level() { assert(entry->physical && prev); return entry->physical->get_drawing_level(); } void OLgridEntry::insert(OLgridEntry* grid[OL_GRID_VERT_MAX] [OL_GRID_HORIZ_MAX], const GLoc &gl) { assert((prev == NULL) && (next == NULL)); assert(Locator::valid(gl)); entry->gloc = gl; next = grid[gl.vert][gl.horiz]->next; prev = grid[gl.vert][gl.horiz]; if (grid[gl.vert][gl.horiz]->next) grid[gl.vert][gl.horiz]->next->prev = this; grid[gl.vert][gl.horiz]->next = this; } void OLgridEntry::remove() { assert(prev); if (next) next->prev = prev; prev->next = next; prev = next = NULL; } #if X11 OLshadowEntry::OLshadowEntry() { entry = NULL; prev = next = NULL; orphaned = False; wasDrawn = False; } OLshadowEntry::OLshadowEntry(const Area &a,OLentry *e) { area = a; entry = e; prev = next = NULL; orphaned = False; wasDrawn = False; } void OLshadowEntry::insert(OLshadowEntry * shadows[OL_GRID_VERT_MAX] [OL_GRID_HORIZ_MAX], const GLoc &gl) { assert((prev == NULL) && (next == NULL)); entry->shadowGloc = gl; assert(Locator::valid(gl)); next = shadows[gl.vert][gl.horiz]->next; prev = shadows[gl.vert][gl.horiz]; if (shadows[gl.vert][gl.horiz]->next) shadows[gl.vert][gl.horiz]->next->prev = this; shadows[gl.vert][gl.horiz]->next = this; } void OLshadowEntry::remove() { assert (prev); if (next) next->prev = prev; prev->next = next; prev = next = NULL; } #endif HumanP Incarnator::operator () () { // First non-null human that wants to be reincarnated >= index n. while (n < locator->humansMax && !(locator->humans[n] && locator->reincarnating[n] && locator->reincarnateTimers[n].ready())) { n++; } if (n == locator->humansMax) { return NULL; } else { // Extract human. locator->reincarnating[n] = False; return locator->humans[n]; } } PhysicalP PhysicalIter::operator () () { while (n < locator->listMax && !locator->list[n].valid) { n++; } assert(n <= locator->listMax); if (n == locator->listMax) { return NULL; } else { n++; return locator->list[n-1].physical; } } ArenaMessageIter::ArenaMessageIter(Locator &l,const IntelId &i) { ptr = l.arenaHead; locator = &l; intelId = i; } char *ArenaMessageIter::next(Boolean &propagate) { while (ptr != locator->arenaTail) { int ret = -1; // If exclusive, intels must match. if (locator->arenaMessages[ptr].exclusive) { if (locator->arenaMessages[ptr].intelId == intelId) { ret = ptr; } } // Return first message found. else { ret = ptr; } // Increment pointer even if we found a match. ptr = (ptr + 1) % OL_MESSAGES_MAX; if (ret != -1) { // Set return value of propagate. propagate = locator->arenaMessages[ret].propagate; return locator->arenaMessages[ret].msg; } } // Didn't find a match. return NULL; } Locator::Locator(WorldP w, int drawAlg,SoundManager * s) { // assert(TEAMS_MAX >= HUMANS_MAX * 2); no longer true contextCount = 0; world = w; drawingAlgorithm = drawAlg; drawRects = False; soundManager = s; register_contexts(); uniqueGen = 0; listMax = 0; for (int c = 0; c < OL_GRID_HORIZ_MAX; c++) { for (int r = 0; r < OL_GRID_VERT_MAX; r++) { grid[r][c] = new OLgridEntry(NULL); assert(grid[r][c]); #if X11 shadows[r][c] = new OLshadowEntry(); assert(shadows[r][c]); #endif } } addNum = 0; useDelList = False; delNum = 0; rememberSounds = False; soundsNum = 0; #if X11 shadowDelNum = 0; #endif xValid = XVARS_VALID_INIT; head = tail = 0; arenaHead = arenaTail = 0; messagesIgnore = False; humansMax = enemiesNum = neutralsNum = 0; for (int n = 0; n < HUMANS_MAX; n++) { Timer nTimer(REINCARNATE_TIME); reincarnateTimers[n] = nTimer; } persistentTeamsNum = teamsNum = 0; } Locator::~Locator() { reset(); for (int c = 0; c < OL_GRID_HORIZ_MAX; c++) for (int r = 0; r < OL_GRID_VERT_MAX; r++) { assert(grid[r][c]); delete grid[r][c]; #if X11 assert(shadows[r][c]); delete shadows[r][c]; #endif } #if X11 for (;shadowDelNum>0;shadowDelNum--) { assert(shadowDelList[shadowDelNum-1]); delete shadowDelList[shadowDelNum-1]; } #endif } void Locator::add(PhysicalP p) { if (addNum >= OL_LIST_MAX) { cerr << "Object locator is full. Can only have " << OL_LIST_MAX << " objects in the game." << endl; assert(0); } addList[addNum] = p; addNum++; p->set_id(reserve_list_entry(p)); } void Locator::add_as(PhysicalP p,const Id &id) { assert(id.index >= 0 && id.index < OL_LIST_MAX); // Kill any object that might have been at that location. // clean up the grid, possibly add to the shadowDelList if (id.index < listMax && list[id.index].valid && list[id.index].physical) { // die, fucker del_now(list[id.index].physical); } // Stolen from Locator::add if (addNum >= OL_LIST_MAX) { cerr << "Object locator is full. Can only have " << OL_LIST_MAX << " objects in the game." << endl; assert(0); } // addList[addNum] = p;a // addNum++; // Make all entries less than id.index invalid. while (listMax <= id.index) { list[listMax].valid = False; list[listMax].reserved = False; listMax++; } // if (id.index >= listMax) { // Since Locator::del_now() may have decreased listMax // listMax = id.index + 1; //} list[id.index].valid = False; list[id.index].reserved = True; list[id.index].physical = p; // Just to make sure. p->set_id(id); add_now(p); } void Locator::get_nearby(PhysicalP nearby[OL_NEARBY_MAX],int &nitems, PhysicalP p,int radius) { //stupid MS compiler gets ambiguous call without the recast to double int glocRadius = (int)ceil((double)((double)radius / OL_GRID_SIZE_MIN)); int radius_2 = radius * radius; nitems = 0; const Id id = p->get_id(); GLoc pGLoc = list[id.index].gloc; const Area &pArea = p->get_area(); Pos pPos = pArea.get_middle(); GLoc gloc; for (gloc.horiz = pGLoc.horiz - glocRadius; gloc.horiz <= pGLoc.horiz + glocRadius; gloc.horiz++) for (gloc.vert = pGLoc.vert - glocRadius; gloc.vert <= pGLoc.vert + glocRadius; gloc.vert++) if (valid(gloc)) { // Skip the header. OLgridEntry *ge = grid[gloc.vert][gloc.horiz]->get_next(); while (ge) { PhysicalP p2 = ge->get_physical(); if (p != p2) { const Area& area = p2->get_area(); if (pPos.distance_2(area.get_middle()) <= radius_2) { assert(nitems < OL_NEARBY_MAX); nearby[nitems] = p2; nitems++; } } ge = ge->get_next(); } } } PhysicalP Locator::lookup(const Id &id,Boolean recentAddition) { if (id.index != Id::INVALID && id.index < listMax && (list[id.index].valid || // recentAddition toggles whether recently added objects can be // looked up. (recentAddition && list[id.index].reserved))) { Id testId = list[id.index].physical->get_id(); if (testId == id) { return list[id.index].physical; } } return NULL; } IntelP Locator::lookup(const IntelId &iId) { if (iId.index == IntelId::INVALID) { return NULL; } // Check humans. if (iId.index < humansMax && humans[iId.index]) { IntelId testId = humans[iId.index]->get_intel_id(); if (testId == iId) { return humans[iId.index]; } } // Check enemies. if (iId.index < enemiesNum && enemies[iId.index]->alive()) { IntelId testId = enemies[iId.index]->get_intel_id(); if (testId == iId) { return enemies[iId.index]; } } // Check neutrals. if (iId.index < neutralsNum && neutrals[iId.index]->alive()) { IntelId testId = neutrals[iId.index]->get_intel_id(); if (testId == iId) { return neutrals[iId.index]; } } return NULL; } // Used on both UNIX (LargeViewport only) and Windows. void Locator::draw_directly(CMN_DRAWABLE window,Xvars &xvars,int dpyNum, const Area &area) { if (!xvars.is_valid(xValid)) { init_x(xvars,IX_INIT,NULL); } // Get all the grid squares covering the area in question. GLoc gridStart; GLoc gridFinish; grid_covering_area(gridStart,gridFinish,area); // Avoid double drawing composite objects. clear_already_drawn_flag(gridStart,gridFinish); // Loop over all drawing levels, all grid squares, all objects in each // grid square. GLoc gloc; for (int dLevel = 0; dLevel < DRAWING_LEVELS; dLevel++) { for (gloc.vert = gridStart.vert; gloc.vert < gridFinish.vert; gloc.vert++) { for (gloc.horiz = gridStart.horiz; gloc.horiz < gridFinish.horiz; gloc.horiz++) { if (!valid(gloc)) { continue; } OLgridEntry *ge = grid[gloc.vert][gloc.horiz]->get_next(); while (ge) { OLentry *ent = ge->get_entry(); assert (ent->gloc == gloc); // Must be visible this turn and be at the current drawing level. if ((ge->get_mapped() || ge->get_flash()) && (ge->get_drawing_level() == dLevel)) { PhysicalP p = ge->get_physical(); assert(p); // Usually only draws one object, except for Composite objects // with drawing order. draw_chain(window,xvars,dpyNum,area,p); } ge = ge->get_next(); } // while ge } // horiz } // vert } // dlevel } void Locator::draw_ticks(CMN_DRAWABLE window,Xvars &xvars,int dpyNum, const Area &area,const Id &id, ITickRenderer* renderer) { renderer->begin_draw(dpyNum); PhysicalP p = lookup(id); // Only draw tick marks if there is a Physical to center them on. if (p) { // In window coords. Area winArea = xvars.stretch_area(area); Pos winPos = winArea.get_pos(); Size winSize = winArea.get_size(); // Get all objects within a certain range. // Same range as machine players. PhysicalP nearby[OL_NEARBY_MAX]; int nitems; get_nearby(nearby,nitems,p,IT_VISION_RANGE); for (int n = 0; n < nitems; n++) { // TICK_MAX means don't draw tick. NetDataP netData = nearby[n]->get_net_data(); TickType tt = netData->get_tick_type(); // Use tick type specified by the NetData first. If there is none, // let the Locator compute it. if (tt == TICK_MAX) { tt = compute_tick_type(nearby[n],p->get_intel()); } // Should we draw a tick for the Physical if (tt != TICK_MAX) { const Area &checkArea = nearby[n]->get_area(); // Only draw tick marks for objects off-screen. // The Client decides whether or not to draw ticks, even if the // Server decided what type of tick to draw. if (!checkArea.overlap(area)) { Pos middle = checkArea.get_middle(); int windowX = xvars.stretch_x(middle.x) - winPos.x; int windowY = xvars.stretch_y(middle.y) - winPos.y; if (windowY < 0) { // along top edge if (windowX < 0) { // upper left corner if (!renderer->draw_tick(tt,window,xvars,dpyNum, winSize,CO_UP_L,0)) { break; } } else if (windowX < winSize.width) { // regular case if (!renderer->draw_tick(tt,window,xvars,dpyNum, winSize,CO_UP,windowX)) { break; } } else { // upper right if (!renderer->draw_tick(tt,window,xvars,dpyNum, winSize,CO_UP_R,0)) { break; } } } else if (windowY >= winSize.height) { // along bottom edge if (windowX < 0) { // bottom left corner if (!renderer->draw_tick(tt,window,xvars,dpyNum, winSize,CO_DN_L,0)) { break; } } else if (windowX < winSize.width) { // normal case if (!renderer->draw_tick(tt,window,xvars,dpyNum, winSize,CO_DN,windowX)) { break; } } else { // bottom right if (!renderer->draw_tick(tt,window,xvars,dpyNum, winSize,CO_DN_R,0)) { break; } } } else { // right or left side if (windowX < 0) { if (!renderer->draw_tick(tt,window,xvars,dpyNum, winSize,CO_L,windowY)) { break; } } else if (windowX >= winSize.width) { if (!renderer->draw_tick(tt,window,xvars,dpyNum, winSize,CO_R,windowY)) { break; } } else { assert(0); } } } // overlap }// not TICK_MAX } // for } // if (p) renderer->end_draw(dpyNum); } // For Locator's implementation of ITickRenderer. Does nothing. void Locator::begin_draw(int) { } // For Locator's implementation of ITickRenderer. Does nothing. void Locator::end_draw(int) { } void Locator::draw_chain(CMN_DRAWABLE window,Xvars &xvars,int dpyNum, const Area &area,PhysicalP p) { Id id = p->get_id(); assert(lookup(id) == p); // Don't draw multiple times. // Since p was drawn, we can be sure that p's draw_before_me() objects // were drawn. if (list[id.index].alreadyDrawn) { return; } // Can't reject p if it is outside area yet. We must call all the way down // p's chain because p may have some draw-before that does overlap area. // First draw any objects that must be drawn before p. CompositeP comp = p->get_composite(); if (comp) { // We are relying on get_draw_before_me() not to get in an infinite loop. PhysicalP q = comp->get_draw_before_me(); if (q) { draw_chain(window,xvars,dpyNum,area,q); } } // Now we can reject p if it is outside area. const Area &areaP = p->get_area(); if (areaP.overlap(area)) { // Physical::draw() will do the correct offset. // Pass in area, not areaP. p->draw(window,xvars,dpyNum,area); list[id.index].alreadyDrawn = True; } } void Locator::reset() { // Destroy the addList. int n; for (n = 0; n < addNum; n++) { delete addList[n]; } addNum = 0; for (n = 0; n < listMax; n++) { if (list[n].valid) { del_now(list[n].physical); } } listMax = 0; // Don't reset this, because the client may be getting old packets. // uniqueGen = 0; // Check grid integrity. There should only be the headers remaining. for (int c = 0; c < OL_GRID_HORIZ_MAX; c++) { for (int r = 0; r < OL_GRID_VERT_MAX; r++) { if (!grid[r][c]) { cerr << "Error in grid integrity. grid[" << r << "][" << c << "]" << " is null" << endl; } else if (grid[r][c]->get_next() != NULL) { cerr << "Error in grid integrity. grid[" << r << "][" << c << "]" << " has a next entry." << endl; } } } clear_all_messages(); for (n = 0; n < humansMax; n++) { // May be null. delete humans[n]; } for (n = 0; n < enemiesNum; n++) { delete enemies[n]; } for (n = 0; n < neutralsNum; n++) { delete neutrals[n]; } humansMax = enemiesNum = neutralsNum = 0; for (n = 0; n < teamsNum; n++) { delete_team_data(teams[n]); } teamsNum = 0; for (n = 0; n < persistentTeamsNum; n++) { delete_team_data(persistentTeams[n]); } persistentTeamsNum = 0; } void Locator::clear_all_messages() { // Delete all messages remaining in both queues. char *msg; while (msg = message_deq()) { delete msg; } IntelId dummy; Quanta dummy2 = 0; Boolean dummy3 = False; do { arena_message_deq(&msg,dummy,dummy2,dummy3); delete msg; } while (msg); } void Locator::level_reset() { // Kill off non-persistent teams. for (int n = 0; n < teamsNum; n++) { delete_team_data(teams[n]); } teamsNum = 0; } void Locator::clock() { #if X11 kill_shadow_del_list(); shadows_follow_physicals(); #endif collision_checks(); intelligence_and_act(); kill_outside_world(); kill_excess_objects(); check_for_death(); update_phase(); grid_follows_physicals(); // Also sets list[n].collided = False. add_new_objects(); delete_dead(); reincarnate_clock(); } void Locator::client_pre_clock() { #if X11 kill_shadow_del_list(); shadows_follow_physicals(); #endif } void Locator::client_post_clock() { grid_follows_physicals(); // Also sets list[n].collided = False. // add_new_objects(); // delete_dead(); // reincarnate_clock(); } #if X11 void Locator::kill_shadow_del_list() { // Kill shadow delete list. int n; for (n = 0; n < shadowDelNum; n++) { shadowDelList[n]->remove(); delete shadowDelList[n]; } shadowDelNum = 0; } void Locator::shadows_follow_physicals() { int n; // Move shadows to previous area. for (n = 0; n < listMax; n++) { if (list[n].valid && (list[n].mapped || list[n].flash)) { list[n].shadowEntry->set_area(list[n].physical->get_area()); GLoc realGloc = compute_gloc(list[n].shadowEntry->get_area()); if (list[n].shadowGloc != realGloc) { list[n].shadowEntry->remove(); list[n].shadowEntry->insert(shadows,realGloc); } } } } #endif void Locator::intelligence_and_act() { /* Intelligence phase and action phase. Set next variables. Externally visible state should not change. Exceptions to this: canTake. */ int n; for (n = 0; n < listMax; n++) { if (list[n].valid) { // Think before you act. list[n].physical->intelligence(); list[n].physical->act(); } } } void Locator::kill_outside_world() { int n; for (n = 0; n < listMax; n++) { if (list[n].valid && (list[n].mapped || list[n].flash)) { const Area &area = list[n].physical->get_area_next(); if (!world->inside(area.middle_wsquare())) { // (&& !list[n].physical->delete_me()) list[n].physical->set_quiet_death(); list[n].physical->kill_self(); arena_message_enq(Utils::strdup("Knocked Out Of World"), list[n].physical); } } } } void Locator::check_for_death() { // Check for death. I.e. healthNext < 0. /* NOTE: If using kill_self in this phase, an object must manually call die and check for die_called(). Hack in Frog::die. */ int n; for (n = 0; n < listMax; n++) { if (list[n].valid && list[n].physical->get_health_next() < 0 && list[n].physical->get_health() >= 0 && !list[n].physical->die_called()) { list[n].physical->die(); } } } void Locator::update_phase() { // Update externally visable state. int n; for (n = 0; n < listMax; n++) { if (list[n].valid) { list[n].physical->update(); } } } void Locator::grid_follows_physicals() { /* Update the locator grid, shifting gridEntries around if necessary. */ int n; for (n = 0; n < listMax; n++) { if (list[n].valid) { allign_flash_and_mapped(n); if (list[n].mapped || list[n].flash) { const Area &area = list[n].physical->get_area(); if (world->inside(area.middle_wsquare())) { GLoc realGLoc = compute_gloc(list[n].physical); if (list[n].gloc != realGLoc) { list[n].gridEntry->remove(); list[n].gridEntry->insert(grid,realGLoc); } } else { if (!(list[n].physical->die_called() && list[n].physical->delete_me())) { // For Server and StandAlone, we should have already cleaned up // in kill_outside_world. // For Client, we do it here. del_now(list[n].physical); /* cerr << "item knocked out of world, but die " << list[n].physical->die_called() << " and delete " << list[n].physical->delete_me() << " not both called " << endl; */ } } // inside } // mapped || flash list[n].collided = False; } // if valid } // for n } void Locator::add_new_objects() { // Add all objects on the list. int n; for (n = 0; n < addNum; n++) { add_now(addList[n]); } addNum = 0; } void Locator::delete_dead() { // Delete objects that are dead. int n; for (n = 0; n < listMax; n++) { if (list[n].valid && list[n].physical->delete_me()) { del_now(list[n].physical); } } } void Locator::message_enq(char *msg) { if (messagesIgnore) { delete msg; return; } if ((tail + 1) % OL_MESSAGES_MAX != head) { messages[tail] = msg; tail = (tail + 1) % OL_MESSAGES_MAX; } else { delete msg; // cout << "Warning: Message queue overflow. Discarding message." << endl; } } char *Locator::message_deq() { if (head == tail) return NULL; int ret = head; head = (head + 1) % OL_MESSAGES_MAX; return messages[ret]; } char *Locator::peek_most_recent_message() { if (head == tail) { return NULL; } return messages[Utils::mod(tail - 1,OL_MESSAGES_MAX)]; } void Locator::arena_message_enq(char *msg,IntelId *intelId, Quanta time,Boolean propagate) { // Don't disable arenaMessages. // if (messagesIgnore) { // delete msg; // return; //} if ((arenaTail + 1) % OL_MESSAGES_MAX != arenaHead) { // Fill ArenaMessage structure. arenaMessages[arenaTail].msg = msg; arenaMessages[arenaTail].exclusive = (intelId != NULL); arenaMessages[arenaTail].time = time; arenaMessages[arenaTail].propagate = propagate; if (intelId) { arenaMessages[arenaTail].intelId = *intelId; } arenaTail = (arenaTail + 1) % OL_MESSAGES_MAX; } else { delete msg; // cout << "Warning: Message queue overflow. Discarding message." << endl; } } void Locator::arena_message_enq(char *msg,PhysicalP p) { IntelP intel = p->get_intel(); if (intel) { IntelId intelId = intel->get_intel_id(); // Will always do default amount of time. arena_message_enq(msg,&intelId); } else { delete msg; } } Boolean Locator::arena_message_deq(char **msg,IntelId &intelId, Quanta &time,Boolean &propagate) { assert(msg); if (arenaHead == arenaTail) { *msg = NULL; time = -1; return False; } int ret = arenaHead; arenaHead = (arenaHead + 1) % OL_MESSAGES_MAX; *msg = arenaMessages[ret].msg; time = arenaMessages[ret].time; // may be -1 propagate = arenaMessages[ret].propagate; if (arenaMessages[ret].exclusive) { intelId = arenaMessages[ret].intelId; return True; } else { return False; } } void Locator::register_human(HumanP h) { // find an empty spot int n; for (n = 0; n < humansMax; n++) { if (!humans[n]) { break; } } // Increase size of array. if (n == humansMax) { assert(humansMax < HUMANS_MAX); humansMax++; } // n is now a valid spot to insert the human. // Assign h an IntelId. IntelId iId = h->get_intel_id(); assert(iId.index == IntelId::INVALID); iId.index = n; iId.unique = uniqueGen; uniqueGen++; h->set_intel_id(iId); // Add to the array of Humans. humans[n] = h; reincarnating[n] = False; reincarnateTimers[n].reset(); } void Locator::register_human_as(HumanP h) { IntelId iId = h->get_intel_id(); assert(iId.index != IntelId::INVALID && iId.index < HUMANS_MAX); if (iId.index < humansMax && humans[iId.index]) { // kill human off if one was already there. delete humans[iId.index]; } // Increment humansMax while (humansMax <= iId.index) { humans[humansMax] = NULL; reincarnating[humansMax] = False; reincarnateTimers[humansMax].reset(); humansMax++; } humans[iId.index] = h; // These values don't matter. reincarnating[iId.index] = False; reincarnateTimers[iId.index].reset(); } void Locator::unregister_human(const IntelId &iId) { IntelP intel = lookup(iId); if (!(intel && intel->is_human())) { return; } delete humans[iId.index]; humans[iId.index] = NULL; // Decrease effective size of list. int n = iId.index; if (n == humansMax - 1) { // Scan back til we hit a valid entry. while(n > 0 && !humans[n - 1]) { n--; } humansMax = n; } } void Locator::register_enemy(EnemyP m) { IntelId iId = m->get_intel_id(); assert(iId.index == IntelId::INVALID); iId.unique = uniqueGen; uniqueGen++; // Try inserting in current array. for (int n = 0; n < enemiesNum; n++) { if (!enemies[n]->alive()) { delete enemies[n]; enemies[n] = m; iId.index = n; m->set_intel_id(iId); return; } } // Else add to array. assert(enemiesNum < ENEMIES_MAX); enemies[enemiesNum] = m; iId.index = enemiesNum; m->set_intel_id(iId); enemiesNum++; } void Locator::register_neutral(NeutralP m) { IntelId iId = m->get_intel_id(); assert(iId.index == IntelId::INVALID); iId.unique = uniqueGen; uniqueGen++; // Try inserting in current array. for (int n = 0; n < neutralsNum; n++) { if (!neutrals[n]->alive()) { delete neutrals[n]; neutrals[n] = m; iId.index = n; m->set_intel_id(iId); return; } } // Else add to array. assert(neutralsNum < NEUTRALS_MAX); neutrals[neutralsNum] = m; iId.index = neutralsNum; m->set_intel_id(iId); neutralsNum++; } int Locator::humans_playing() { int n,ret; for (n = 0, ret = 0; n < humansMax; n++) { if (humans[n] && (humans[n]->alive() || humans[n]->reincarnate_me())) { ret++; } } return ret; } int Locator::enemies_alive() { int n,ret; for (n = 0, ret = 0; n < enemiesNum; n++) if (enemies[n]->alive()) ret++; return ret; } int Locator::neutrals_alive() { int n,ret; for (n = 0, ret = 0; n < neutralsNum; n++) if (neutrals[n]->alive()) ret++; return ret; } int Locator::humans_registered() { // Count elements of humans[] that are non-null. int ret = 0; for (int n = 0; n < humansMax; n++) { if (humans[n]) { ret++; } } return ret; } HumanP Locator::get_human(int which) { int humansBefore = 0; for (int n = 0; n < humansMax; n++) { if (humans[n]) { if (which == humansBefore) { return humans[n]; } // Count number of valid humans before n. humansBefore++; } } // Asked for a human that doesn't exist. assert(0); return NULL; } void Locator::set_remember_deleted(Boolean val) { if (val == useDelList) { return; } if (val) { assert(delNum == 0); useDelList = True; } else { // Clear out existing requests. delNum = 0; useDelList = False; } } void Locator::set_remember_sounds(Boolean val) { if (val == rememberSounds) { return; } if (val) { assert(soundsNum == 0); rememberSounds = True; } else { // Clear out existing requests. soundsNum = 0; rememberSounds = False; } } TeamId Locator::add_persistent_team(Boolean (*same_team) (LocatorP,PhysicalP,PhysicalP), TeamOptionsP ops) { // Code dupd in add_team() assert(persistentTeamsNum < TEAMS_MAX); // same unique number generator as regular teams. TeamId ret = uniqueGen; // ret.index = persistentTeamsNum; // ret.unique = uniqueGen; uniqueGen++; persistentTeams[persistentTeamsNum].teamId = ret; if (ops) { persistentTeams[persistentTeamsNum].options = *ops; } else { TeamOptions defaultOps; persistentTeams[persistentTeamsNum].options = defaultOps; } persistentTeams[persistentTeamsNum].same_team = same_team; persistentTeamsNum++; return ret; } TeamId Locator::add_team(Boolean (*same_team)(LocatorP,PhysicalP,PhysicalP), TeamOptionsP ops) { // Code dupd in add_persistent_team() assert(teamsNum < TEAMS_MAX); TeamId ret = uniqueGen; // ret.index = teamsNum; // ret.unique = uniqueGen; uniqueGen++; teams[teamsNum].teamId = ret; if (ops) { teams[teamsNum].options = *ops; } else { TeamOptions defaultOps; teams[teamsNum].options = defaultOps; } teams[teamsNum].same_team = same_team; teamsNum++; return ret; } Boolean Locator::same_team(TeamOptions &ops,PhysicalP p1,PhysicalP p2) { // Is there a better way to do this? int n; // regular teams for (n = 0; n < teamsNum; n++) { if (teams[n].same_team(this,p1,p2)) { // Both are on the same team according to rule n. ops = teams[n].options; return True; } } // persistent teams for (n = 0; n < persistentTeamsNum; n++) { if (persistentTeams[n].same_team(this,p1,p2)) { // Both are on team n. ops = persistentTeams[n].options; return True; } } // Tried all the teams. return False; } void Locator::delete_team_data(OLTeam &team) { // Does nothing now. } const PhysicalContext *Locator::get_context(ClassId classId) { if (classId >= 0 && classId < A_CLASSES_NUM) { return contexts[classId]; } return NULL; } int Locator::filter_contexts(const PhysicalContext *contextList[A_CLASSES_NUM], ClassId idList[A_CLASSES_NUM], Boolean (* filter)(const PhysicalContext *,void *), void *closure) { assert(list); int numFound = 0; int n; for (n = 0; n < A_CLASSES_NUM; n++) if (contexts[n] && filter(contexts[n],closure)) { assert(numFound < A_CLASSES_NUM); if (contextList) { contextList[numFound] = contexts[n]; } if (idList) { // We guarantee that the index of a context in Locator::contexts // is the ClassId of that context. idList[numFound] = (ClassId)n; } numFound++; } return numFound; } void Locator::get_item_info(Boolean &isItem,Boolean &isWeapon, CMN_BITS_ID &iconId,ClassId cId) { assert(cId >= 0 && cId < A_None); // A regular class. if (cId < A_CLASSES_NUM) { if (contexts[cId] && contexts[cId]->get_item_info != NULL) { contexts[cId]->get_item_info(isItem,isWeapon,iconId); } else { isItem = False; isWeapon = False; iconId = (CMN_BITS_ID)0; } } // One of the "non-existent" classes, see coord.h else { const OLItemInfo &iInfo = nonExistentClassesItemInfo[cId - A_CLASSES_NUM]; isItem = iInfo.isItem; isWeapon = iInfo.isWeapon; iconId = iInfo.iconId; } } TickType Locator::compute_tick_type(PhysicalP p,IntelP relativeTo) { TickType tt = TICK_MAX; IntelP intel = p->get_intel(); // Have intel and not be invisible to draw tick marks. if (intel && p->is_moving() && !((MovingP)p)->is_invisible()) { // Choose appropriate type of tick. if (intel->is_human()) { tt = TICK_HUMAN; } else if (intel->is_enemy()) { tt = TICK_ENEMY; } else { tt = TICK_NEUTRAL; // Slaves of other humans should have the tick mark of a human. IntelP pMaster = lookup(((MachineP)intel)->get_master_intel_id()); if (pMaster) { assert(pMaster->is_human()); if (pMaster != relativeTo) { tt = TICK_HUMAN; } } } } // Draw nearby objects of great importance, e.g Flag, Xit, AltarOfSin else { // Could use PhysicalContext, but why bother. if (p->get_class_id() == A_Flag || p->get_class_id() == A_Xit || p->get_class_id() == A_AltarOfSin) { tt = TICK_OTHER; } } return tt; } void Locator::add_now(PhysicalP p) { Id id = p->get_id(); int n = id.index; assert (list[n].physical == p); list[n].valid = True; list[n].collided = False; list[n].collidable = p->collidable(); list[n].mapped = p->get_mapped(); list[n].flash = p->get_flash(); list[n].alreadyDrawn = False; // Maybe we should set the reserved flag to False? if (list[n].mapped || list[n].flash) { // Create and add shadow and grid entries. list[n].gridEntry = new OLgridEntry(&list[n]); assert(list[n].gridEntry); GLoc gloc = compute_gloc(list[n].physical); list[n].gridEntry->insert(grid,gloc); #if X11 list[n].shadowEntry = new OLshadowEntry(p->get_area(),&list[n]); assert(list[n].shadowEntry); list[n].shadowEntry->insert(shadows,gloc); #endif } } void Locator::del_now(PhysicalP p) { Id id = p->get_id(); // Object has been added and is in locator, add it to the delete list and // kill its OLEntry and shadowEntry if (list[id.index].valid) { // Add to the delete list. // This code copied in allign_flash_and_mapped. if (useDelList) { if (delNum < OL_LIST_MAX) { delList[delNum] = id; delNum++; } else { cerr << "List of recently deleted objects has overflown." << endl; } } list[id.index].valid = False; list[id.index].reserved = False; if (list[id.index].mapped || list[id.index].flash) { list[id.index].gridEntry->remove(); delete list[id.index].gridEntry; #if X11 shadowDelList[shadowDelNum] = list[id.index].shadowEntry; list[id.index].shadowEntry->set_orphaned(); shadowDelNum++; #endif } } // Object has been added, but we are deleting it before Locator has a chance // to clock and complete the process. So, remove from the add list and // unreserve the entry. else { if (!list[id.index].reserved) { cerr << "Locator::del_now() bad ID when deleting object" << endl; return; } list[id.index].reserved = False; // Already know that valid is False. // Delete p from the addList. int n; Boolean found = False; for (n = 0; n < addNum; n++) { if (addList[n] == p) { addList[n] = addList[addNum - 1]; addList[addNum - 1] = NULL; addNum--; found = True; break; } } // Make sure the Physical was actually in the add list. assert(found); } // Decrease effective size of list. if (id.index == listMax - 1) { // Scan back til we hit a valid entry. while(id.index > 0 && !list[id.index-1].valid) { id.index--; } listMax = id.index; } // We now make sure to delete p. delete p; } void Locator::allign_flash_and_mapped(int n) { Boolean flash = list[n].physical->get_flash(); Boolean mapped = list[n].physical->get_mapped(); // Add new OLgridEntry and shadowEntry. if (!(list[n].mapped || list[n].flash) && (mapped || flash)) { list[n].gridEntry = new OLgridEntry(&list[n]); assert(list[n].gridEntry); GLoc gloc = compute_gloc(list[n].physical); list[n].gridEntry->insert(grid,gloc); #if X11 list[n].shadowEntry = new OLshadowEntry(list[n].physical->get_area(),&list[n]); assert(list[n].shadowEntry); list[n].shadowEntry->insert(shadows,gloc); #endif } // Destroy grid entry and mark shadow entry for deletion. if ((list[n].mapped || list[n].flash) && !(mapped || flash)) { list[n].gridEntry->remove(); delete list[n].gridEntry; #if X11 shadowDelList[shadowDelNum] = list[n].shadowEntry; list[n].shadowEntry->set_orphaned(); shadowDelNum++; #endif // kind of a hack using delList for recently unmapped objects. if (useDelList) { if (delNum < OL_LIST_MAX) { delList[delNum] = list[n].physical->get_id(); delNum++; } else { cerr << "List of recently deleted objects has overflown." << endl; } } } list[n].mapped = mapped; list[n].flash = flash; } Id Locator::reserve_list_entry(PhysicalP p) { // Must use this form of get_id(), since this is one of the few places // where it is valid for the id not to be set. Id dummy; for (int n = 0; ; n++) { assert(n <= listMax); // Increase effective size of list. if (n == listMax) { // If Locator list is full, we are in deep shit. This will // probably cause an assertion somewhere. if (n >= OL_LIST_MAX) { cerr << "ERROR: Exceeded maximum number of " << OL_LIST_MAX << " objects in the game." << endl; // Return an invalid Id. return dummy; } list[n].valid = False; list[n].reserved = False; listMax++; } // Found an empty entry. if (!list[n].valid && !list[n].reserved) { Id ret; ret.index = n; ret.unique = uniqueGen; uniqueGen++; list[n].reserved = True; list[n].physical = p; return ret; } } } /* Collision phase. Physical::collide will be called at most once for any object. */ void Locator::collision_checks() { for (int n = 0; n < listMax; n++) { if (list[n].valid && !list[n].collided && list[n].mapped && list[n].collidable && list[n].physical->alive()) { // So corpses don't collide, see below. PhysicalP p1 = list[n].physical; GLoc gloc; for (gloc.vert = list[n].gloc.vert - 1; gloc.vert <= list[n].gloc.vert + 1; gloc.vert++) { for (gloc.horiz = list[n].gloc.horiz - 1; gloc.horiz <= list[n].gloc.horiz + 1; gloc.horiz++) { if (valid(gloc) && possible_overlap(p1->get_area(),gloc)) { // Skip the header. OLgridEntry *ge = grid[gloc.vert][gloc.horiz]->get_next(); while (ge) { PhysicalP p2 = ge->get_physical(); assert(p2); IntelP intel1 = p1->get_intel(); IntelP intel2 = p2->get_intel(); TeamOptions ops; if ( !ge->get_collided() && ge->get_mapped() && ge->get_collidable() && p1 != p2 && // Don't collide with self. // Make sure neither physical is set not to // collide with the other. p2->get_dont_collide() != p1->get_id() && p1->get_dont_collide() != p2->get_id() && // Teammates don't collide with each other !(same_team(ops,p1,p2) && ops.membersDontCollide) && // If both have intels, make sure neither intel // is set not to collide with the other. // If they don't both have intels, continue. (! (intel1 && intel2) || (intel1->get_dont_collide() != intel2->get_intel_id() && intel2->get_dont_collide() != intel1->get_intel_id()) ) && p2->alive() // So corpses don't collide, see below. ) { // Composite objects don't collide with each other. // We could do this with teams. CompositeP c1 = p1->get_composite(); CompositeP c2 = p2->get_composite(); if (c1 && c2 && c1->get_composite_id() == c2->get_composite_id()) { ge = ge->get_next(); continue; } /// Check for the dont_collide_composite() flag if (c1 && c1->get_composite_id() == p2->get_dont_collide_composite()) { ge = ge->get_next(); continue; } if (c2 && c2->get_composite_id() == p1->get_dont_collide_composite()) { ge = ge->get_next(); continue; } const Area &a1 = p1->get_area(); if (a1.overlap(p2->get_area())) { if (p1->get_mass() > p2->get_mass()) { p2->avoid(p1); } else if (p2->get_mass() > p1->get_mass()) { p1->avoid(p2); } else if (Utils::coin_flip()) { p1->avoid(p2); } else { p2->avoid(p1); } p1->collide(p2); p2->collide(p1); list[n].collided = True; ge->set_collided(True); } // overlap } // big-ass if // Also called above to "continue" ge = ge->get_next(); } // while ge } // valid gloc } // gloc horiz } // gloc vert } // if list valid } // for n } GLoc Locator::compute_gloc(PhysicalP p) { const Area &area = p->get_area(); #ifndef NDEBUG Size size = area.get_size(); assert(size.width <= OL_GRID_WIDTH && size.height <= OL_GRID_HEIGHT); #endif Pos middle = area.get_middle(); GLoc gloc; gloc.horiz = middle.x / OL_GRID_WIDTH; gloc.vert = middle.y / OL_GRID_HEIGHT; return gloc; } GLoc Locator::compute_gloc(const Area &area) { Pos middle = area.get_middle(); GLoc gloc; gloc.horiz = middle.x / OL_GRID_WIDTH; gloc.vert = middle.y / OL_GRID_HEIGHT; return gloc; } Boolean Locator::possible_overlap(const Area &area,const GLoc &gloc) { //static int tr; //static int fa; Pos pos(gloc.horiz * OL_GRID_WIDTH - OL_GRID_WIDTH / 2, gloc.vert * OL_GRID_HEIGHT - OL_GRID_HEIGHT / 2); Size size; size.set(2 * OL_GRID_WIDTH,2 * OL_GRID_HEIGHT); Area virtGLoc(AR_RECT,pos,size); if (virtGLoc.overlap(area)) { //tr++; //cout << "true/false: " << tr << "/" << fa << " ratio: " << ((float)tr / (float)fa) << endl; return True; } else { //fa++; //cout << "true/false: " << tr << "/" << fa << " ratio: " << ((float)tr / (float)fa) << endl; return False; } } void Locator::reincarnate_clock(){ for (int n = 0; n < humansMax; n++) { if (humans[n]) { if (!reincarnating[n] && humans[n]->reincarnate_me()) { reincarnating[n] = True; reincarnateTimers[n].set(); } reincarnateTimers[n].clock(); } } } void Locator::kill_excess_objects() { // Count number of existing objects. int count = 0; int n; for (n = 0; n < listMax; n++) { if (list[n].valid || list[n].reserved) { count++; } } // If we decide to trim off the excess objects. if (count > OL_LIST_WARN) { cerr << "Approaching critical number of objects in XEvil." << " Trim some of the excess." << endl; // Kill unheld items. for (n = 0; n < listMax; n++) { if (list[n].valid) { PhysicalP p = list[n].physical; if (p->is_item() && !((ItemP)p)->is_held()) { p->set_quiet_death(); p->kill_self(); } } } // Count remaining int postCount = 0; for (n = 0; n < listMax; n++) { if (list[n].valid || list[n].reserved) { postCount++; } } cerr << "Was " << count << " objects, there are now " << postCount << " objects." << endl; } } void Locator::clear_already_drawn_flag(const GLoc &gridStart, const GLoc &gridFinish) { GLoc gloc; for (gloc.vert = gridStart.vert; gloc.vert < gridFinish.vert; gloc.vert++) { for (gloc.horiz = gridStart.horiz; gloc.horiz < gridFinish.horiz; gloc.horiz++) { if (valid(gloc)) { OLgridEntry *ge = grid[gloc.vert][gloc.horiz]->get_next(); while (ge) { OLentry *ent = ge->get_entry(); // Clear the flag. ent->alreadyDrawn = False; ge = ge->get_next(); } } } } } void Locator::grid_covering_area(GLoc& start,GLoc& finish,const Area& area) { Pos aPos; Size aSize; area.get_rect(aPos,aSize); start.horiz = (int)floor((aPos.x / WSQUARE_WIDTH)/(float)OL_GRID_COL_MAX) - 1; start.vert = (int)floor((aPos.y / WSQUARE_HEIGHT)/(float)OL_GRID_ROW_MAX) - 1; finish.horiz = (int)ceil(ceil((float)(aPos.x + aSize.width) / (float)WSQUARE_WIDTH)/ (float) OL_GRID_COL_MAX) + 1; finish.vert = (int)ceil(ceil((float)(aPos.y + aSize.height)/(float)WSQUARE_HEIGHT)/ (float) OL_GRID_ROW_MAX) + 1; } void Locator::register_contexts() { // As good a place as any to put this call. Ability::register_abilities(); // Set all contexts to NULL. int n; for (n = 0; n < A_CLASSES_NUM; n++) { contexts[n] = NULL; } // Here we go... register_context(&Explosion::context); register_context(&Fire::context); register_context(&FireExplosion::context); register_context(&NProtection::context.physicalContext); register_context(&TProtection::context.physicalContext); register_context(&XProtection::context.physicalContext); register_context(&Trapdoor::context.physicalContext); register_context(&Home::context.physicalContext); register_context(&Shell::context.movingContext.physicalContext); register_context(&SwapShell::context.movingContext.physicalContext); register_context(&Lance::context.movingContext.physicalContext); register_context(&Laser::context.movingContext.physicalContext); register_context(&FrogShell::context.movingContext.physicalContext); register_context(&Fireball::context.movingContext.physicalContext); register_context(&Missile::context.movingContext.physicalContext); register_context(&Star::context.movingContext.physicalContext); register_context(&Blood::context.movingContext.physicalContext); register_context(&GreenBlood::context.movingContext.physicalContext); register_context(&OilDroplet::context.movingContext.physicalContext); register_context(&Grenade::context.movingContext.physicalContext); register_context(&Napalm::context.movingContext.physicalContext); register_context(&Egg::context.movingContext.physicalContext); register_context(&Xit::context.fallingContext.movingContext.physicalContext); register_context(&Flag::context.fallingContext.movingContext.physicalContext); register_context(&Rock::context.heavyContext.fallingContext.movingContext.physicalContext); register_context(&Weight::context.heavyContext.fallingContext.movingContext.physicalContext); register_context(&AltarOfSin::context.fallingContext.movingContext.physicalContext); register_context(&Doppel::context.itemContext.fallingContext. movingContext.physicalContext); register_context(&Cloak::context.fallingContext. movingContext.physicalContext); register_context(&Transmogifier::context.itemContext.fallingContext. movingContext.physicalContext); register_context(&MedKit::context.itemContext.fallingContext. movingContext.physicalContext); register_context(&CrackPipe::context.autoUseContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&Caffine::context.autoUseContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&PCP::context.autoUseContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&NShield::context.autoUseContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&TShield::context.autoUseContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&Bomb::context.itemContext.fallingContext. movingContext.physicalContext); register_context(&Chainsaw::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&Pistol::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&MGun::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&Lancer::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&AutoLancer::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&FThrower::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&Launcher::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&Grenades::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&Napalms::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&Stars::context.weaponContext.itemContext.fallingContext. movingContext.physicalContext); register_context(&Swapper::context.gunContext.weaponContext.itemContext. fallingContext.movingContext.physicalContext); register_context(&FrogGun::context.gunContext.weaponContext.itemContext. fallingContext.movingContext.physicalContext); register_context(&DogWhistle::context.weaponContext.itemContext. fallingContext.movingContext.physicalContext); register_context(&DemonSummoner::context.weaponContext.itemContext. fallingContext.movingContext.physicalContext); register_context(&Enforcer::creatureContext.movingContext.physicalContext); register_context(&Frog::creatureContext.movingContext.physicalContext); register_context(&Hero::creatureContext.movingContext.physicalContext); register_context(&Ninja::creatureContext.movingContext.physicalContext); register_context(&Alien::creatureContext.movingContext.physicalContext); register_context(&RedHugger::creatureContext. movingContext.physicalContext); register_context(&GreenHugger::creatureContext. movingContext.physicalContext); register_context(&ChopperBoy::creatureContext.movingContext.physicalContext); register_context(&Seal::creatureContext.movingContext.physicalContext); register_context(&FireDemon::creatureContext.movingContext.physicalContext); register_context(&Dragon::creatureContext.movingContext.physicalContext); register_context(&Walker::creatureContext.movingContext.physicalContext); register_context(&Dog::creatureContext.movingContext.physicalContext); register_context(&Mutt::creatureContext.movingContext.physicalContext); register_context(&Yeti::creatureContext.movingContext.physicalContext); register_context(&PhysMover::context); assert(contextCount <= A_CLASSES_NUM); // Some sanity checking, see if something is wrong with the // newly programmed PhysicalContexts. for (n = 0; n < A_CLASSES_NUM; n++) { const PhysicalContext *cx = contexts[n]; if (cx) { // Make sure that the index of a context is the same as it's classId. assert(cx->classId == n); // If potentialEnemy, enemyWeight is greater than zero. // Not the converse, see Seals. if (cx->potentialEnemy) { assert(cx->enemyWeight > 0); } assert(cx->enemyWeight >= 0); assert(cx->create); // Should have a get_stats() method iff one of the stats* flags is // set. assert((cx->statsCreations || cx->statsUses || cx->statsDeaths) == (cx->get_stats != NULL)); // Can't act as both a weapon and otherItem. assert(!(cx->potentialWeapon && cx->potentialOtherItem)); // If Physical could be created as either a weapon or otherItem, make // sure objectWorldPercent is greater than zero. assert((cx->potentialWeapon || cx->potentialOtherItem) == (cx->objectWorldPercent > 0)); assert(cx->objectWorldPercent >= 0); } } if (contextCount < A_CLASSES_NUM) { cout << "ERROR: Locator::register_contexts(): Not all classes have been " << "registered" << endl; } } void Locator::register_context(const PhysicalContext *context) { contexts[context->classId] = context; // int cCountPre = contextCount; contextCount++; // int cCountPost = contextCount; // cerr << "cCountPre is " << cCountPre << " post is " << cCountPost << // " contextCount@ " << &contextCount << " is " << contextCount << endl; } Boolean Locator::submitSoundRequest(SoundRequest req) { // Don't bother submitting or remembering the sound request if no sound. SoundName name = req.get_sound_name(); // Don't use lookup, because we want to submit the sound if we are // a Server sending to Client. if (!(name > 0 && name < SoundNames::SOUND_MAX)) { return False; } // Server will rember recent sounds so it can send them to the Client. if (rememberSounds && soundsNum < OL_RECENT_SOUNDS_MAX) { sounds[soundsNum] = req; soundsNum++; } if (!SoundNames::lookup(name)) { return False; } return soundManager->submitRequest(req); }