1
0
mirror of https://github.com/blupi-games/planetblupi synced 2024-12-30 10:15:36 +01:00
planetblupi/src/path.cxx
2017-08-08 18:49:01 +02:00

386 lines
10 KiB
C++

/*
* This file is part of the planetblupi source code
* Copyright (C) 1997 Daniel Roux, EPSITEC SA & Denis Dumoulin
* Copyright (C) 2017 Mathieu Schroeter
* http://epsitec.ch; http://www.blupi.org; http://github.com/blupi-games
*
* 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 3 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, see http://gnu.org/licenses
*/
#include "action.h"
#include "decor.h"
#include "fifo.h"
// Mémorise toutes les positions des blupi.
void CDecor::CheminMemPos (Sint32 exRank)
{
Sint32 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, Sint32 & rank)
{
Sint32 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
Sint32 CDecor::CheminARebours (Sint32 rank)
{
Sint32 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 (Sint32 rank)
{
Sint32 pos, last, dest, dist;
Sint32 step, dir, cout, action, max, next, ampli;
Sint32 dx, dy;
Sint32 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 (Sint32 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 = (Sint32) (dy * dy) + (Sint32) (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 (
Sint32 rank, Sint32 pos, Sint32 dir, Sint32 & next, Sint32 & ampli,
Sint32 & cout, Sint32 & action)
{
POINT cel, vector, newCel;
bool bFree;
Sint32 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_WALK;
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 (Sint32 rank, Sint32 & action)
{
Sint32 cout; // prix pour aller dans une direction
Sint32 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, (Uint8) MAXCELX * (Uint8) 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 (Sint32 rank, POINT dest, Sint32 button)
{
Sint32 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;
}