// chemin.cpp // (c) 1997, Denis Dumoulin #include "DECOR.H" #include "FIFO.H" #include "ACTION.H" // Mémorise toutes les positions des blupi. void CDecor::CheminMemPos(int exRank) { int rank, index; m_cheminNbPos = 0; index = 0; for ( rank=0 ; rank<MAXBLUPI ; rank++ ) { if ( m_blupi[rank].bExist && m_blupi[rank].perso != 6 && // pas le détonnateur de mine rank != exRank ) { m_cheminPos[index] = m_blupi[rank].cel; m_cheminRank[index] = rank; m_cheminNbPos ++; index ++; if ( m_blupi[rank].destCel.x != m_blupi[rank].cel.x || m_blupi[rank].destCel.y != m_blupi[rank].cel.y ) { m_cheminPos[index] = m_blupi[rank].destCel; m_cheminRank[index] = rank; m_cheminNbPos ++; index ++; } } } } // Teste si une positiion est occupée par un blupi. bool CDecor::CheminTestPos(POINT pos, int &rank) { int i; for ( i=0 ; i<m_cheminNbPos ; i++ ) { if ( pos.x == m_cheminPos[i].x && pos.y == m_cheminPos[i].y ) { rank = m_cheminRank[i]; return true; } } return false; } // une fois le but trouvé, reprend en arrière // à la recherche du chemin le plus court // retourne la direction à prendre int CDecor::CheminARebours(int rank) { int pos, rebours, last, dir, set; POINT v; pos = m_blupi[rank].goalCel.y*MAXCELX + m_blupi[rank].goalCel.x; rebours = m_cheminWork[pos]; if ( rebours == 0 ) return -1; while ( true ) { bis: for ( set=0 ; set<2 ; set++ ) { for ( dir=set ; dir<8 ; dir+=2 ) { v = GetVector(dir*16); last = pos + v.y*MAXCELX+v.x; if ( m_cheminWork[last] > 0 && m_cheminWork[last] < rebours ) { pos = last; rebours = m_cheminWork[pos]; if (rebours==1) return (dir>=4) ? dir-4 : dir+4; goto bis; // interrompt le for... } } } // ne devrait jamais arriver ! return -1; } } // troisième méthode de recherche // semblable à la précédente, // mais les points à explorer sont classés selon leur distance à la cible void CDecor::CheminFillTerrain(int rank) { long pos, last, dest, dist; int step, dir, cout, action, max, next, ampli; int dx, dy; int but = 1000; if ( m_blupi[rank].cel.x == m_blupi[rank].goalCel.x && m_blupi[rank].cel.y == m_blupi[rank].goalCel.y ) return; pos = m_blupi[rank].cel.y*MAXCELX + m_blupi[rank].cel.x; dest = m_blupi[rank].goalCel.y*MAXCELX + m_blupi[rank].goalCel.x; CPileTriee fifo(2*MAXCELX+2*MAXCELY); // les variantes possibles fifo.put(pos, 0); // position de départ m_cheminWork[pos] = 1; // première position // répète jusqu'à trouvé ou plus de possibilités max = 500; while ( max-- > 0 ) { // reprend une variante de chemin pos = fifo.get(); if ( pos < 0 ) break; step = m_cheminWork[pos]; // on est arrivé au but ? //? if ( pos == dest ) return; if ( pos == dest ) { but = step; // hélas trop lent ! max = 50; } // est-ce vraiment trop loin ? if ( step > 200 ) return; // marque les cases autour du point if ( step < but ) for ( dir=0 ; dir<8 ; dir++ ) { if ( CheminTestDirection(rank, pos, dir, next, ampli, cout, action) ) { last = pos + ampli*next; if ( last<0 || last>=MAXCELX*MAXCELY ) continue; if ( m_cheminWork[last] == 0 || m_cheminWork[last] > step+cout ) { // marque les cases sautées for (int i=1; i<ampli;i++) { m_cheminWork[pos+i*next] = step+cout-ampli+i; } m_cheminWork[last] = step+cout; //? char buffer[50]; //? sprintf(buffer, "word = %d;%d %d\n", last%200, last/200, step+cout); //? OutputDebug(buffer); dx = m_blupi[rank].goalCel.x - last%MAXCELX; dy = m_blupi[rank].goalCel.y - last/MAXCELX; //? dist = dy*dy + dx*dx; dist = (long)(dy*dy) + (long)(dx*dx); fifo.put(last, dist); } } } } } // routine déterninant si une direction est possible // retourne l'incrément pour passer à la nouvelle case // et le "prix à payer" pour aller dans cette direction // coût doit être déterminé en sortie bool CDecor::CheminTestDirection(int rank, int pos, int dir, int &next, int &li, int &cout, int &action) { POINT cel, vector, newCel; bool bFree; int tryDirect, workBlupi, rankHere; cel.x = pos%MAXCELX; cel.y = pos/MAXCELX; tryDirect = dir*16; vector = GetVector(tryDirect); // Peut-on glisser dans cette direction ? bFree = IsFreeGlisse(cel, tryDirect, rank, action); cout = 5; // coût élevé if ( !bFree ) { // Peut-on marcher normalement ? bFree = IsFreeDirect(cel, tryDirect, rank); action = ACTION_MARCHE; cout = 1; // coût minimal } if ( !bFree ) { // Peut-on sauter ? bFree = IsFreeJump(cel, tryDirect, rank, action); cout = 3; // coût élevé } ampli = GetAmplitude(action); // a <- amplitude (1..5) cout *= ampli; // coût plus élevé si grande amplitude // Blupi peut aller sur le lieu de la construction. if ( !bFree && m_blupi[rank].passCel.x != -1 ) { newCel = m_blupi[rank].passCel; workBlupi = m_decor[newCel.x/2][newCel.y/2].workBlupi; if ( m_blupi[rank].passCel.x/2 == (cel.x+vector.x*ampli)/2 && m_blupi[rank].passCel.y/2 == (cel.y+vector.y*ampli)/2 && (workBlupi < 0 || workBlupi == rank) ) { bFree = true; cout = 1; } } if ( bFree ) // chemin libre (sans tenir compte des perso) ? { newCel.x = cel.x + vector.x*ampli; newCel.y = cel.y + vector.y*ampli; // newCel <- arrivée suposée if ( m_blupi[rank].perso == 3 ) // tracks ? { // Le tracks peut aller sur les blupi // pour les écraser ! if ( IsTracksHere(newCel, false) ) // autre perso ici ? { return false; } } else { //? if ( IsBlupiHere(newCel, false) ) // autre perso ici ? if ( CheminTestPos(newCel, rankHere) ) // autre perso ici ? { // Si blupi immobile, comme si obstacle qq. //? if ( m_blupi[m_blupiHere].goalCel.x == -1 ) return false; if ( m_blupi[rankHere].goalCel.x == -1 ) return false; // Si blupi mobile, possible mais coût élevé. cout = 20; } } next = vector.y*MAXCELX + vector.x; return true; } return false; } // Retourne true si on a assigné une nouvelle direction à blupi. bool CDecor::CheminCherche(int rank, int &action) { int cout; // prix pour aller dans une direction int pos, dir, next, ampli; // Déjà à destination ? if ( m_blupi[rank].cel.x == m_blupi[rank].goalCel.x && m_blupi[rank].cel.y == m_blupi[rank].goalCel.y ) { return false; } // Destination occupée ? if ( IsBlupiHereEx(m_blupi[rank].goalCel, rank, false) ) { return false; } memset(m_cheminWork, 0, (BYTE)MAXCELX*(BYTE)MAXCELY); CheminMemPos(rank); // fait un remplissage du map de travail // autour du point de départ CheminFillTerrain(rank); // cherche le chemin à partir de la destination dir = CheminARebours(rank); if ( dir < 0 ) return false; pos = m_blupi[rank].cel.y*MAXCELX + m_blupi[rank].cel.x; if ( CheminTestDirection(rank, pos, dir, next, ampli, cout, action) && cout < 20 ) { m_blupi[rank].sDirect = 16*dir; return true; } // ne devrait jamais arriver ! return false; } // Teste s'il est possible de se rendre à un endroit donné. bool CDecor::IsCheminFree(int rank, POINT dest, int button) { int action, sDirect; POINT goalCel, passCel, limit; bool bOK; if ( button == BUTTON_STOP ) return true; goalCel = m_blupi[rank].goalCel; passCel = m_blupi[rank].passCel; sDirect = m_blupi[rank].sDirect; if ( IsFreeCelEmbarque(dest, rank, action, limit) ) { dest = limit; } if ( IsFreeCelDebarque(dest, rank, action, limit) ) { dest = limit; } if ( button == BUTTON_GO && m_decor[dest.x/2][dest.y/2].objectChannel == CHOBJECT && (m_decor[dest.x/2][dest.y/2].objectIcon == 118 || // jeep ? m_decor[dest.x/2][dest.y/2].objectIcon == 16 ) && // armure ? dest.x%2 == 1 && dest.y%2 == 1 ) { dest.y --; // va à côté jeep/armure } if ( button == BUTTON_GO && m_decor[dest.x/2][dest.y/2].objectChannel == CHOBJECT && m_decor[dest.x/2][dest.y/2].objectIcon == 113 ) // maison ? { dest.x = (dest.x/2)*2+1; dest.y = (dest.y/2)*2+1; // sous la porte } if ( m_blupi[rank].cel.x == dest.x && m_blupi[rank].cel.y == dest.y ) return true; m_blupi[rank].goalCel = dest; if ( m_decor[dest.x/2][dest.y/2].objectChannel == CHOBJECT && button != BUTTON_GO ) { m_blupi[rank].passCel = dest; // si arbres/fleurs/bateau/etc. } bOK = CheminCherche(rank, action); m_blupi[rank].goalCel = goalCel; m_blupi[rank].passCel = passCel; m_blupi[rank].sDirect = sDirect; return bOK; }