platform-game/Forráskód/modules/floaters.c
Andras Schmelczer c4f7565d18
All checks were successful
Build & deploy web version / Build WebAssembly & deploy (push) Successful in 1m1s
Add mobile support
2026-08-09 20:39:30 +01:00

281 lines
11 KiB
C

#include "floaters.h"
#define enemyMinSize 0.75 /**< Ellenségek oldalhosszának kezdeti értéke [m].*/
#define enemyMaxSize 4 /**< Az ellenségek, ha összeérnek, összetapadnak, de ennél ne lehetnek nagyobbak [m]. */
#define enemyVelocity 0.006 /**< [m/ms] */
#define enemySlowing 0.15 /**< Ha az ellenség nekiütközik valaminek, akkor a sebessége ennyiszeresére csökken. */
#define slownessDebuffDuration 2000 /**< Ha az ellenség nekiütközik valaminek, akkor a sebessége ennyi ideig lesz alacsnyabb. [ms] */
#define projectileSize 0.2 /**< A játékos által lőtt lövedék oldalhossza [m]. */
#define projectileVelocity 0.03 /**< A játékos által lőtt lövedék kezdő sebessége [m/ms] */
#define projectileVelocityYMax 0.035 /**< A lövedék sebességét befolyásolja a játékos sebessége (a kiulövés pillanatában), illetve a gravitációs gyorsulás, viszont sosem lehet gyorsabb. */
#define floaterColor (colors) { (color) {229, 22, 22, 255}, (color) {200, 20, 20, 255}, (color) {168, 16, 16, 255} } /**< A lövedék és az ellenség ugyanolyan színű */
floater* floaterList = NULL; /**< Láncolt lista, ami az ellenségeket és lövedékeket is tartalmazza. Nagyon hasonló a kettő, ezért célszerű közösen kezelni őket.*/
///Lista elejére beszúr.
floater* addToFloaterList() {
floater* newfloater = (floater*) malloc(sizeof(floater));
newfloater -> next = floaterList;
floaterList = newfloater;
return newfloater;
}
///Felszabadítja az ellenségek és lövedékek listáját.
void freeFloaterList() {
if(floaterList == NULL) return;
floater* moving = floaterList;
floater* tmp;
while(moving != NULL) {
tmp = moving;
moving = moving -> next;
free(tmp);
}
floaterList = NULL;
}
///A játékos közelében elhelyez egy új ellenséget.
void createEnemy() {
floater* newEnemy = addToFloaterList();
newEnemy -> type = enemy;
newEnemy -> velocity = (vector2D) {0, 0};
newEnemy -> slownessDebuffLeft = 0;
newEnemy -> size = enemyMinSize;
vector3D playerPosition = getPlayerRenderObject().position;
int i = 0;
do {
newEnemy -> position.x = playerPosition.x + (float) (randomPlusOrMinus() * randomIntWithinBorders(50 - i, 20));
newEnemy -> position.y = playerPosition.y + (float) (randomIntWithinBorders(40 - i, 15));
i++;
}
while(!isWithinTheMap(newEnemy -> position, (vector2D) {newEnemy -> size, newEnemy -> size}));
}
/** \brief Lövedék létrehozása.
* A megadott paraméterek függvényében a létrehozott lövedék az egér irányafele indul el.
* \param helyvektor a játékoshoz
* \param térvektor a játékos méretéhez
* \param síkvektor* játékos sebesség (ezt Newton III. értelmében megváltoztatja
* \param koordinátája az egérnek
* A játékost ellenkező irányba elmozdítja. A lövedék kezdősebbességéhez hozzáadódik a játékos sebessége.
*/
void createProjectile(vector3D playerPosition, vector3D playerSize, vector2D* playerVelocity, mousePos mouse) {
floater* newProjectile = addToFloaterList();
newProjectile -> type = projectile;
newProjectile -> position.x = playerPosition.x + playerSize.x / 2;
newProjectile -> position.y = playerPosition.y + playerSize.y / 2;
newProjectile -> size = projectileSize;
vector2D playerCoord = get2DPoint((vector3D) {newProjectile -> position.x, newProjectile -> position.y, 0});
float deltaX = mouse.x - playerCoord.x;
float deltaY = playerCoord.y - mouse.y;
float distance = sqrt(deltaX * deltaX + deltaY * deltaY);
float compensation = 0;
if(distance > 0) compensation = projectileVelocity / distance;
newProjectile -> velocity = (vector2D) {
deltaX * compensation + playerVelocity -> x,
deltaY * compensation + playerVelocity -> y
};
playerVelocity -> x -= newProjectile -> velocity.x * 0.25;
playerVelocity -> y -= newProjectile -> velocity.y * 0.25;
}
///Listából kiszed.
void destroyFloater(floater* floaterToDestroy) {
if(floaterList == floaterToDestroy) floaterList = floaterToDestroy -> next;
else {
floater* previous = floaterList;
while(previous != NULL && previous -> next != floaterToDestroy)
previous = previous -> next;
previous -> next = floaterToDestroy -> next;
}
free(floaterToDestroy);
}
/** \brief Megnézi összeér-e két ellenség, ha igen, összeolvasztja őket.
* A kisebb ellenség eltűnik, a nagyobbnak a térfogata a kisebbéjével nő.
* \param ellenség1
* \param ellenség2
* \return Összeértek-e?
*/
static bool mergeEnemies(floater* enemy1, floater* enemy2) {
floater* enemyBigger = enemy1 -> size > enemy2 -> size ? enemy1 : enemy2;
floater* enemySmaller = enemy1 -> size > enemy2 -> size ? enemy2 : enemy1;
if(isBoxOverlappingBox(
enemy1 -> position, (vector2D) {enemy1 -> size, enemy1 -> size},
enemy2 -> position, (vector2D) {enemy2 -> size, enemy2 -> size}
)) {
enemyBigger -> size = smallerF(fDist(enemyBigger -> size, enemySmaller -> size), enemyMaxSize);
destroyFloater(enemySmaller);
return true;
}
return false;
}
///Kezeli ha egy ellenség le lett lőve, a lövedék eltűnik, az ellenség mérete pedig csökken, vagy ha túl kicsi, az ellenség eltűnik.
static void projectileEnemyCollision(floater* projectile) {
floater* currentFloater = floaterList;
while(currentFloater != NULL) {
if(currentFloater -> type == enemy &&
isBoxOverlappingBox(
projectile -> position, (vector2D) {projectile -> size, projectile -> size},
currentFloater -> position, (vector2D) {currentFloater -> size, currentFloater -> size}
)
) {
destroyFloater(projectile);
currentFloater -> size = sqrt(square(currentFloater -> size) - 0.95 * square(enemyMinSize * enemyMinSize));
if(currentFloater -> size < enemyMinSize * 1.05)
destroyFloater(currentFloater);
}
currentFloater = currentFloater -> next;
}
}
/** \brief Megnézi, hogy ütközött-e valami egy kockával, ha igen, kezeli.
* A kocka mindenképpen megsemmisül.
* \param helyvektor a floaterhez
* \param mérete a flaoternek
* \return Ütköztek-e?
* A visszatérési érték alapján megfelelően lehet kezelni az esetet.
*/
static bool floaterSolidCollision(vector2D position, float size) {
block* blockToDestroy;
if(
isWithinTheMap(position, (vector2D) {size, size}) &&
(detectCollision(position, (vector2D) {size, size}, solid, &blockToDestroy) ||
detectCollision(position, (vector2D) {size, size}, treasure, &blockToDestroy)) &&
blockToDestroy != NULL
) {
*blockToDestroy = air;
return true;
}
return false;
}
/** \brief Mozgatja az adott ellenséget.
* Figyel minden lehetsége ütközésre.
* \param ellenség
* \param ms eltelt idő
*/
static void moveEnemy(floater* thisEnemy, int deltaTime) {
float velocity = enemyVelocity;
if(thisEnemy -> slownessDebuffLeft > 0) {
thisEnemy -> slownessDebuffLeft -= deltaTime;
velocity *= enemySlowing;
}
renderObject player = getPlayerRenderObject();
vector3D playerPosition = player.position;
float deltaX = (playerPosition.x - thisEnemy -> position.x);
float deltaY = (playerPosition.y - thisEnemy -> position.y);
float compensation = 0;
float distance = fDist(deltaX, deltaY);
if(distance > 0)
compensation = velocity / distance;
thisEnemy -> position.x += deltaX * compensation * deltaTime;
thisEnemy -> position.y += deltaY * compensation * deltaTime;
if(floaterSolidCollision(thisEnemy -> position, thisEnemy -> size))
thisEnemy -> slownessDebuffLeft = slownessDebuffDuration;
if(isBoxOverlappingBox((vector2D) {player.position.x, player.position.y}, (vector2D){player.size.x, player.size.y},
thisEnemy -> position, (vector2D) {thisEnemy -> size, thisEnemy -> size})) setDie(true);
floater* currentEnemy = floaterList;
while(currentEnemy != NULL && (thisEnemy == currentEnemy || !mergeEnemies(thisEnemy, currentEnemy)))
currentEnemy = currentEnemy -> next;
}
/** \brief Mozgatja az adott lövedéket.
* Figyel minden lehetsége ütközésre.
* \param lövedék
* \param ms eltelt idő
*/
static void moveProjectile(floater* thisProjectile, int deltaTime) {
thisProjectile -> velocity.y -= g * deltaTime;
if(thisProjectile -> velocity.y < -projectileVelocityYMax) thisProjectile -> velocity.y = -projectileVelocityYMax;
thisProjectile -> position.x += thisProjectile -> velocity.x * deltaTime;
thisProjectile -> position.y += thisProjectile -> velocity.y * deltaTime;
if(
!isWithinTheMap(thisProjectile -> position, (vector2D) {projectileSize, projectileSize}) ||
floaterSolidCollision(thisProjectile -> position, thisProjectile -> size)
)
destroyFloater(thisProjectile);
projectileEnemyCollision(thisProjectile);
}
///Minden floaterre meghívja a szükséges mozgatási műveletet.
void moveFloaters(int deltaTime) {
floater* currentFloater = floaterList;
while(currentFloater != NULL) {
if(currentFloater -> type == projectile) moveProjectile(currentFloater, deltaTime);
else moveEnemy(currentFloater, deltaTime);
currentFloater = currentFloater -> next;
}
}
/** \brief Visszaadja az adott térfogatban lévő floater testeket.
* A sorrend nem elhanyagolható 3D-s testek rajzolásakor. Figyelni kell, melyik melyik másikat takarja el.
* Ezért kell "feldarabolni" őket. Itt még csak az egy adott m^3-ben lévő testek adatait adja oda a rajzoló függvénynek.
* Maga a feldarabolás már nem ennek a függvénynek a dolga.
* \param koord x
* \param koord y
* \param Hova kell írnia a testeket
* \param Max mennyit adhat vissza
* A feldarabolásról részletesebben a camera.c modulban.
*/
void getFloatersRenderObject(int x, int y, renderObject* renderMap, int renderObjectsPerCube) {
renderObject enemyRenderObject;
enemyRenderObject.colors = floaterColor;
int floaterCounter = 0;
floater* currentFloater = floaterList;
while(currentFloater != NULL) {
if(
(int)currentFloater -> position.x <= x && x <= (int)(currentFloater -> position.x + currentFloater -> size) &&
(int)currentFloater -> position.y <= y && y <= (int)(currentFloater -> position.y + currentFloater -> size)
) {
enemyRenderObject.position = (vector3D) {currentFloater -> position.x, currentFloater -> position.y, 0.5};
enemyRenderObject.size = (vector3D) {currentFloater -> size, currentFloater -> size, currentFloater -> size/2};
enemyRenderObject.doesExist = true;
if(floaterCounter < renderObjectsPerCube - 1) {
renderMap[floaterCounter] = enemyRenderObject;
floaterCounter++;
}
}
currentFloater = currentFloater -> next;
}
enemyRenderObject.doesExist = false;
renderMap[floaterCounter] = enemyRenderObject;
}