#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.03 /**< 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; intVector2D playerCoord = get2DPoint((vector3D) {newProjectile -> position.x, newProjectile -> position.y, 0}); int deltaX = mouse.x - playerCoord.x; int 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; }