feat: initial revision
This commit is contained in:
616
src/main.c
616
src/main.c
@@ -1,23 +1,462 @@
|
|||||||
/*
|
/*
|
||||||
Raylib example file.
|
Shooting arrows - ya know
|
||||||
This is an example main file for a simple raylib project.
|
|
||||||
Use this as a starting point or replace it with your code.
|
|
||||||
|
|
||||||
by Jeffery Myers is marked with CC0 1.0. To view a copy of this license, visit https://creativecommons.org/publicdomain/zero/1.0/
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "raylib.h"
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "resource_dir.h" // utility header for SearchAndSetResourceDir
|
#include "raylib.h"
|
||||||
|
#include "resource_dir.h"
|
||||||
|
|
||||||
|
const float G = 458.0;
|
||||||
|
const float attritionCoefficient = 95.0;
|
||||||
|
const int archerSize = 10;
|
||||||
|
const float archerSpeed = 96.0;
|
||||||
|
const float maxBowTension = 921.0;
|
||||||
|
const float bowTensionSpeed = 12.0;
|
||||||
|
|
||||||
|
const float spacing = 16;
|
||||||
|
const float bowTensionIndicatorWidth = 360;
|
||||||
|
const float bowTensionIndicatorHeight = 40;
|
||||||
|
|
||||||
|
const float arrowLength = 20.0f;
|
||||||
|
const int groundHeight = 40;
|
||||||
|
|
||||||
|
#define MAX_BLOOD_PARTICLES 800
|
||||||
|
|
||||||
|
// Utility function to map a value from one range to another
|
||||||
|
float map(float value, float fromLow, float fromHigh, float toLow, float toHigh)
|
||||||
|
{
|
||||||
|
return toLow + (toHigh - toLow) * (value - fromLow) / (fromHigh - fromLow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility function to normalize a Vector2
|
||||||
|
Vector2 normalized(Vector2 v)
|
||||||
|
{
|
||||||
|
float len = sqrtf(v.x * v.x + v.y * v.y);
|
||||||
|
if (len == 0.0f)
|
||||||
|
{
|
||||||
|
Vector2 zero = {0.0f, 0.0f};
|
||||||
|
return zero;
|
||||||
|
}
|
||||||
|
Vector2 n = {v.x / len, v.y / len};
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct Target Target;
|
||||||
|
typedef struct Arrow Arrow;
|
||||||
|
|
||||||
|
struct Arrow
|
||||||
|
{
|
||||||
|
Vector2 position;
|
||||||
|
Vector2 speed;
|
||||||
|
Vector2 acceleration;
|
||||||
|
Vector2 direction;
|
||||||
|
bool stuck;
|
||||||
|
Target *hitTarget;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ArrowNode ArrowNode;
|
||||||
|
|
||||||
|
struct ArrowNode
|
||||||
|
{
|
||||||
|
Arrow *data;
|
||||||
|
ArrowNode *prev;
|
||||||
|
ArrowNode *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bleed pattern: alternating durations of squirt (spawn particles) and pause
|
||||||
|
// {squirt, pause, squirt, pause, squirt, pause, squirt,..., 0} — 0 marks end
|
||||||
|
#define BLEED_PHASES 18
|
||||||
|
const float bleedPattern[BLEED_PHASES] = {0.08f, 0.06f, 0.38f, 0.25f, 0.08f, 2.06f, 0.38f, 0.25f, 0.10f, 1.06f, 0.08f, 0.3f, 2.2f, 0.4f, 1.1f, 0.9f, 0.8f, 0.0f};
|
||||||
|
// even indices = squirt, odd indices = pause, last 0 = done
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Vector2 position;
|
||||||
|
Vector2 velocity;
|
||||||
|
float lifetime;
|
||||||
|
float maxLifetime;
|
||||||
|
bool active;
|
||||||
|
} BloodParticle;
|
||||||
|
|
||||||
|
BloodParticle bloodParticles[MAX_BLOOD_PARTICLES];
|
||||||
|
|
||||||
|
void spawn_blood(Vector2 origin, Vector2 direction, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MAX_BLOOD_PARTICLES && count > 0; i++)
|
||||||
|
{
|
||||||
|
if (!bloodParticles[i].active)
|
||||||
|
{
|
||||||
|
bloodParticles[i].active = true;
|
||||||
|
bloodParticles[i].position = origin;
|
||||||
|
// base direction is opposite to the arrow's flight
|
||||||
|
float baseAngle = atan2f(-direction.y, -direction.x);
|
||||||
|
// spread within ~90 degrees of the opposite direction
|
||||||
|
float spread = ((float)GetRandomValue(-45, 45)) * DEG2RAD;
|
||||||
|
float angle = baseAngle + spread;
|
||||||
|
float speed = (float)GetRandomValue(0, 320);
|
||||||
|
bloodParticles[i].velocity.x = cosf(angle) * speed;
|
||||||
|
bloodParticles[i].velocity.y = sinf(angle) * speed;
|
||||||
|
bloodParticles[i].maxLifetime = 0.4f + (float)GetRandomValue(10, 475) / 100.0f;
|
||||||
|
bloodParticles[i].lifetime = bloodParticles[i].maxLifetime;
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_blood(float deltaTime)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MAX_BLOOD_PARTICLES; i++)
|
||||||
|
{
|
||||||
|
if (!bloodParticles[i].active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bloodParticles[i].lifetime -= deltaTime;
|
||||||
|
if (bloodParticles[i].lifetime <= 0)
|
||||||
|
{
|
||||||
|
bloodParticles[i].active = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bloodParticles[i].velocity.y += G * 0.5f * deltaTime;
|
||||||
|
bloodParticles[i].position.x += bloodParticles[i].velocity.x * deltaTime;
|
||||||
|
bloodParticles[i].position.y += bloodParticles[i].velocity.y * deltaTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_blood(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MAX_BLOOD_PARTICLES; i++)
|
||||||
|
{
|
||||||
|
if (!bloodParticles[i].active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float alpha = bloodParticles[i].lifetime / bloodParticles[i].maxLifetime;
|
||||||
|
unsigned char a = (unsigned char)(alpha * 255);
|
||||||
|
Color c = {200, 0, 0, a};
|
||||||
|
float size = 2.0f + alpha * 2.0f;
|
||||||
|
DrawRectangle(
|
||||||
|
(int)(bloodParticles[i].position.x - size / 2),
|
||||||
|
(int)(bloodParticles[i].position.y - size / 2),
|
||||||
|
(int)size, (int)size, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Target
|
||||||
|
{
|
||||||
|
Vector2 position;
|
||||||
|
Vector2 speed;
|
||||||
|
bool bleeding;
|
||||||
|
bool grounded;
|
||||||
|
int bleedPhase;
|
||||||
|
float bleedTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct TargetNode TargetNode;
|
||||||
|
|
||||||
|
struct TargetNode
|
||||||
|
{
|
||||||
|
Target *data;
|
||||||
|
TargetNode *prev;
|
||||||
|
TargetNode *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
void add_target(TargetNode *head, Vector2 position)
|
||||||
|
{
|
||||||
|
TargetNode *el = head;
|
||||||
|
while (el->next)
|
||||||
|
{
|
||||||
|
el = el->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
Target *t = malloc(sizeof(Target));
|
||||||
|
t->position = position;
|
||||||
|
t->speed.x = 0;
|
||||||
|
t->speed.y = 0;
|
||||||
|
t->bleeding = false;
|
||||||
|
t->grounded = false;
|
||||||
|
t->bleedPhase = 0;
|
||||||
|
t->bleedTimer = 0;
|
||||||
|
|
||||||
|
TargetNode *newNode = (TargetNode *)malloc(sizeof(TargetNode));
|
||||||
|
newNode->data = t;
|
||||||
|
newNode->prev = el;
|
||||||
|
newNode->next = NULL;
|
||||||
|
|
||||||
|
el->next = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_target(TargetNode *node)
|
||||||
|
{
|
||||||
|
node->prev->next = node->next;
|
||||||
|
if (node->next)
|
||||||
|
node->next->prev = node->prev;
|
||||||
|
free(node->data);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire_arrow(ArrowNode *head, Vector2 origin, Vector2 acceleration)
|
||||||
|
{
|
||||||
|
if (!head)
|
||||||
|
{
|
||||||
|
TraceLog(LOG_ERROR, "Cannot fire arrow: head pointer is NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrowNode *el = head;
|
||||||
|
while (el->next)
|
||||||
|
{
|
||||||
|
el = el->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
Arrow *a = malloc(sizeof(Arrow));
|
||||||
|
a->position.x = origin.x;
|
||||||
|
a->position.y = origin.y;
|
||||||
|
a->speed.x = acceleration.x;
|
||||||
|
a->speed.y = acceleration.y;
|
||||||
|
a->acceleration.x = 0;
|
||||||
|
a->acceleration.y = 0;
|
||||||
|
a->direction = normalized(a->speed);
|
||||||
|
a->stuck = false;
|
||||||
|
a->hitTarget = NULL;
|
||||||
|
|
||||||
|
ArrowNode *newNode = (ArrowNode *)malloc(sizeof(ArrowNode));
|
||||||
|
newNode->data = a;
|
||||||
|
newNode->prev = el;
|
||||||
|
newNode->next = NULL;
|
||||||
|
|
||||||
|
el->next = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_arrows(ArrowNode *head, TargetNode *targets, Texture targetTex, float deltaTime, float attrition)
|
||||||
|
{
|
||||||
|
Rectangle groundRect = {.x = 0, .y = GetScreenHeight() - groundHeight, .width = GetScreenWidth(), .height = groundHeight};
|
||||||
|
|
||||||
|
ArrowNode *el = head->next;
|
||||||
|
while (el != NULL)
|
||||||
|
{
|
||||||
|
Arrow *a = el->data;
|
||||||
|
|
||||||
|
// skip stuck arrows
|
||||||
|
if (a->stuck)
|
||||||
|
{
|
||||||
|
el = el->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
a->speed.x += a->acceleration.x * deltaTime;
|
||||||
|
a->speed.y += a->acceleration.y * deltaTime;
|
||||||
|
|
||||||
|
// apply gravity directly to speed
|
||||||
|
a->speed.y += G * deltaTime;
|
||||||
|
|
||||||
|
a->position.x += a->speed.x * deltaTime;
|
||||||
|
a->position.y += a->speed.y * deltaTime;
|
||||||
|
|
||||||
|
// update flight direction
|
||||||
|
a->direction = normalized(a->speed);
|
||||||
|
|
||||||
|
// to simulate attrition will scale down the acceleration by a constant
|
||||||
|
float decay = powf(attrition, deltaTime);
|
||||||
|
a->acceleration.x *= decay;
|
||||||
|
a->acceleration.y *= decay;
|
||||||
|
|
||||||
|
// check for target collisions
|
||||||
|
TargetNode *tEl = targets->next;
|
||||||
|
while (tEl != NULL)
|
||||||
|
{
|
||||||
|
Target *t = tEl->data;
|
||||||
|
if (t->bleeding)
|
||||||
|
{
|
||||||
|
tEl = tEl->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Rectangle targetRect = {t->position.x, t->position.y, (float)targetTex.width, (float)targetTex.height};
|
||||||
|
if (CheckCollisionPointRec(a->position, targetRect))
|
||||||
|
{
|
||||||
|
t->bleeding = true;
|
||||||
|
t->bleedPhase = 0;
|
||||||
|
t->bleedTimer = bleedPattern[0];
|
||||||
|
|
||||||
|
// penetrate into the target proportionally to hit speed
|
||||||
|
float hitSpeed = sqrtf(a->speed.x * a->speed.x + a->speed.y * a->speed.y);
|
||||||
|
float maxPenetration = arrowLength * 1.1f;
|
||||||
|
float penetration = (hitSpeed / maxBowTension) * maxPenetration;
|
||||||
|
a->position.x += a->direction.x * penetration;
|
||||||
|
a->position.y += a->direction.y * penetration;
|
||||||
|
|
||||||
|
a->acceleration.x = a->acceleration.y = a->speed.x = a->speed.y = 0;
|
||||||
|
a->stuck = true;
|
||||||
|
a->hitTarget = t;
|
||||||
|
TraceLog(LOG_INFO, "Target hit!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tEl = tEl->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a->stuck)
|
||||||
|
{
|
||||||
|
el = el->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for ground collision
|
||||||
|
if (CheckCollisionPointRec(a->position, groundRect))
|
||||||
|
{
|
||||||
|
a->position.y = GetScreenHeight() - groundHeight;
|
||||||
|
a->acceleration.x = a->acceleration.y = a->speed.x = a->speed.y = 0;
|
||||||
|
a->stuck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if arrow went offscreen, remove it
|
||||||
|
if (
|
||||||
|
(a->position.x > GetScreenWidth() || a->position.x < 0) ||
|
||||||
|
a->position.y > GetScreenHeight())
|
||||||
|
{
|
||||||
|
el->prev->next = el->next;
|
||||||
|
if (el->next)
|
||||||
|
el->next->prev = el->prev;
|
||||||
|
|
||||||
|
ArrowNode *orphan = el;
|
||||||
|
|
||||||
|
el = el->next;
|
||||||
|
free(orphan->data);
|
||||||
|
free(orphan);
|
||||||
|
|
||||||
|
TraceLog(LOG_INFO, "Removing arrow");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
el = el->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_arrow(ArrowNode *node)
|
||||||
|
{
|
||||||
|
node->prev->next = node->next;
|
||||||
|
if (node->next)
|
||||||
|
node->next->prev = node->prev;
|
||||||
|
free(node->data);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_targets(TargetNode *targets, ArrowNode *arrows, Texture targetTex, float deltaTime)
|
||||||
|
{
|
||||||
|
TargetNode *tEl = targets->next;
|
||||||
|
while (tEl != NULL)
|
||||||
|
{
|
||||||
|
Target *t = tEl->data;
|
||||||
|
if (!t->bleeding)
|
||||||
|
{
|
||||||
|
tEl = tEl->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply gravity and move the target while falling
|
||||||
|
if (!t->grounded)
|
||||||
|
{
|
||||||
|
t->speed.y += G * deltaTime;
|
||||||
|
float dx = t->speed.x * deltaTime;
|
||||||
|
float dy = t->speed.y * deltaTime;
|
||||||
|
t->position.x += dx;
|
||||||
|
t->position.y += dy;
|
||||||
|
|
||||||
|
float groundY = GetScreenHeight() - groundHeight - targetTex.height;
|
||||||
|
if (t->position.y >= groundY)
|
||||||
|
{
|
||||||
|
dy -= (t->position.y - groundY);
|
||||||
|
t->position.y = groundY;
|
||||||
|
t->speed.x = 0;
|
||||||
|
t->speed.y = 0;
|
||||||
|
t->grounded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move stuck arrows along with the target
|
||||||
|
ArrowNode *aEl = arrows->next;
|
||||||
|
while (aEl != NULL)
|
||||||
|
{
|
||||||
|
if (aEl->data->hitTarget == t)
|
||||||
|
{
|
||||||
|
aEl->data->position.x += dx;
|
||||||
|
aEl->data->position.y += dy;
|
||||||
|
}
|
||||||
|
aEl = aEl->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t->bleedTimer -= deltaTime;
|
||||||
|
if (t->bleedTimer > 0)
|
||||||
|
{
|
||||||
|
// during squirt phases (even indices), spawn particles
|
||||||
|
if (t->bleedPhase % 2 == 0)
|
||||||
|
{
|
||||||
|
Vector2 center = {
|
||||||
|
t->position.x + targetTex.width / 2.0f,
|
||||||
|
t->position.y + targetTex.height / 2.0f};
|
||||||
|
|
||||||
|
ArrowNode *aEl = arrows->next;
|
||||||
|
while (aEl != NULL)
|
||||||
|
{
|
||||||
|
ArrowNode *aNext = aEl->next;
|
||||||
|
if (aEl->data->hitTarget == t)
|
||||||
|
{
|
||||||
|
spawn_blood(aEl->data->position, aEl->data->direction, MAX_BLOOD_PARTICLES / 2);
|
||||||
|
}
|
||||||
|
aEl = aNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tEl = tEl->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// advance to next phase
|
||||||
|
t->bleedPhase++;
|
||||||
|
if (t->bleedPhase >= BLEED_PHASES || bleedPattern[t->bleedPhase] == 0.0f)
|
||||||
|
{
|
||||||
|
// remove arrows that hit this target
|
||||||
|
ArrowNode *aEl = arrows->next;
|
||||||
|
while (aEl != NULL)
|
||||||
|
{
|
||||||
|
ArrowNode *aNext = aEl->next;
|
||||||
|
if (aEl->data->hitTarget == t)
|
||||||
|
{
|
||||||
|
remove_arrow(aEl);
|
||||||
|
}
|
||||||
|
aEl = aNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
// animation done, remove target and spawn a new one
|
||||||
|
TargetNode *dead = tEl;
|
||||||
|
tEl = tEl->next;
|
||||||
|
remove_target(dead);
|
||||||
|
|
||||||
|
Vector2 newPos = {
|
||||||
|
(float)GetRandomValue(0, GetScreenWidth() - targetTex.width),
|
||||||
|
(float)GetRandomValue(0, GetScreenHeight() / 2)};
|
||||||
|
add_target(targets, newPos);
|
||||||
|
|
||||||
|
TraceLog(LOG_INFO, "Target removed after bleeding, new target spawned");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t->bleedTimer = bleedPattern[t->bleedPhase];
|
||||||
|
tEl = tEl->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// Tell the window to use vsync and work on high DPI displays
|
// Tell the window to use vsync and work on high DPI displays
|
||||||
SetConfigFlags(FLAG_VSYNC_HINT | FLAG_WINDOW_HIGHDPI);
|
SetConfigFlags(FLAG_VSYNC_HINT | FLAG_WINDOW_HIGHDPI | FLAG_WINDOW_RESIZABLE);
|
||||||
|
|
||||||
// Create the window and OpenGL context
|
// Create the window and OpenGL context
|
||||||
InitWindow(1280, 800, "Hello Raylib");
|
InitWindow(1280, 800, "Archer");
|
||||||
|
|
||||||
// Utility function from resource_dir.h to find the resources folder and set it as the current working directory so we can load from it
|
// Utility function from resource_dir.h to find the resources folder and set it as the current working directory so we can load from it
|
||||||
SearchAndSetResourceDir("resources");
|
SearchAndSetResourceDir("resources");
|
||||||
@@ -25,26 +464,175 @@ int main ()
|
|||||||
// Load a texture from the resources directory
|
// Load a texture from the resources directory
|
||||||
Texture wabbit = LoadTexture("wabbit_alpha.png");
|
Texture wabbit = LoadTexture("wabbit_alpha.png");
|
||||||
|
|
||||||
|
int screenWidth = GetScreenWidth();
|
||||||
|
int screenHeight = GetScreenHeight();
|
||||||
|
|
||||||
|
Vector2 mousePosition = {0, 0};
|
||||||
|
char coords[200];
|
||||||
|
|
||||||
|
bool trajectoryVisible = false;
|
||||||
|
|
||||||
|
Vector2 archerPosition = {screenWidth / 2 - archerSize / 2, screenHeight - groundHeight - archerSize};
|
||||||
|
|
||||||
|
float bowTension = 0.0;
|
||||||
|
|
||||||
|
ArrowNode *head = (ArrowNode *)calloc(1, sizeof(ArrowNode));
|
||||||
|
|
||||||
|
TargetNode *targets = (TargetNode *)calloc(3, sizeof(TargetNode));
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
// spawn targets at a random position in the top region
|
||||||
|
Vector2 targetPos = {
|
||||||
|
(float)GetRandomValue(0, screenWidth - wabbit.width),
|
||||||
|
(float)GetRandomValue(0, screenHeight / 3)};
|
||||||
|
add_target(targets, targetPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
float dt = 0;
|
||||||
|
|
||||||
// game loop
|
// game loop
|
||||||
while (!WindowShouldClose()) // run the loop until the user presses ESCAPE or presses the Close button on the window
|
while (!WindowShouldClose()) // run the loop until the user presses ESCAPE or presses the Close button on the window
|
||||||
{
|
{
|
||||||
|
dt = GetFrameTime();
|
||||||
|
|
||||||
|
if (IsWindowResized())
|
||||||
|
{
|
||||||
|
screenWidth = GetScreenWidth();
|
||||||
|
screenHeight = GetScreenHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
mousePosition = GetMousePosition();
|
||||||
|
sprintf(coords, "(x=%1.0f, y=%1.0f)", mousePosition.x, mousePosition.y);
|
||||||
|
|
||||||
|
float tensionRate = -3.0;
|
||||||
|
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
|
||||||
|
{
|
||||||
|
tensionRate = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bowTension += bowTensionSpeed * tensionRate;
|
||||||
|
if (bowTension < 0)
|
||||||
|
{
|
||||||
|
bowTension = 0;
|
||||||
|
}
|
||||||
|
if (bowTension > maxBowTension)
|
||||||
|
{
|
||||||
|
bowTension = maxBowTension;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pressing the right mouse button releases the bow istantly
|
||||||
|
if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))
|
||||||
|
{
|
||||||
|
TraceLog(LOG_INFO, "Arrow launched");
|
||||||
|
|
||||||
|
Vector2 dir = {mousePosition.x - archerPosition.x, mousePosition.y - archerPosition.y};
|
||||||
|
Vector2 direction = normalized(dir);
|
||||||
|
Vector2 force = {direction.x * bowTension, direction.y * bowTension};
|
||||||
|
fire_arrow(head, archerPosition, force);
|
||||||
|
bowTension = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsKeyPressed(KEY_T))
|
||||||
|
{
|
||||||
|
trajectoryVisible = !trajectoryVisible;
|
||||||
|
TraceLog(LOG_INFO, "Toggling trajectory indicators");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsKeyDown(KEY_A))
|
||||||
|
{
|
||||||
|
archerPosition.x -= archerSpeed * dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsKeyDown(KEY_D))
|
||||||
|
{
|
||||||
|
archerPosition.x += archerSpeed * dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_arrows(head, targets, wabbit, dt, attritionCoefficient);
|
||||||
|
update_targets(targets, head, wabbit, dt);
|
||||||
|
update_blood(dt);
|
||||||
|
|
||||||
// drawing
|
// drawing
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
|
|
||||||
// Setup the back buffer for drawing (clear color and depth buffers)
|
// Setup the back buffer for drawing (clear color and depth buffers)
|
||||||
ClearBackground(BLACK);
|
ClearBackground(BLACK);
|
||||||
|
|
||||||
// draw some text using the default font
|
// Draw ground
|
||||||
DrawText("Hello Raylib", 200,200,20,WHITE);
|
DrawRectangle(0, screenHeight - groundHeight, screenWidth, groundHeight, BROWN);
|
||||||
|
|
||||||
// draw our texture to the screen
|
// draw targets
|
||||||
DrawTexture(wabbit, 400, 200, WHITE);
|
TargetNode *drawTarget = targets->next;
|
||||||
|
while (drawTarget != NULL)
|
||||||
|
{
|
||||||
|
Target *t = drawTarget->data;
|
||||||
|
DrawTexture(wabbit, (int)t->position.x, (int)t->position.y, WHITE);
|
||||||
|
drawTarget = drawTarget->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw blood particles
|
||||||
|
draw_blood();
|
||||||
|
|
||||||
|
// for now the archer will be a simple square
|
||||||
|
DrawRectangle(archerPosition.x, archerPosition.y, archerSize, archerSize, GREEN);
|
||||||
|
|
||||||
|
// draw arrows
|
||||||
|
ArrowNode *drawEl = head->next;
|
||||||
|
while (drawEl != NULL)
|
||||||
|
{
|
||||||
|
Arrow *a = drawEl->data;
|
||||||
|
Vector2 dir = a->direction;
|
||||||
|
Vector2 tail = {a->position.x - dir.x * arrowLength, a->position.y - dir.y * arrowLength};
|
||||||
|
DrawLineEx(tail, a->position, 2.0f, YELLOW);
|
||||||
|
drawEl = drawEl->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trajectoryVisible)
|
||||||
|
{
|
||||||
|
// aim hints
|
||||||
|
Vector2 startY = {0, mousePosition.y};
|
||||||
|
DrawLineDashed(startY, mousePosition, 4, 8, GRAY);
|
||||||
|
|
||||||
|
Vector2 startX = {mousePosition.x, screenHeight};
|
||||||
|
DrawLineDashed(startX, mousePosition, 4, 8, GRAY);
|
||||||
|
|
||||||
|
Vector2 bowOrigin = {archerPosition.x + archerSize / 2, archerPosition.y + archerSize / 2};
|
||||||
|
DrawLineDashed(bowOrigin, mousePosition, 4, 8, GRAY);
|
||||||
|
|
||||||
|
// targeting coordinates
|
||||||
|
DrawText(coords, screenWidth - bowTensionIndicatorWidth - spacing, bowTensionIndicatorHeight + 2 * spacing, 24, WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tension indicator
|
||||||
|
DrawRectangleLines(screenWidth - bowTensionIndicatorWidth - spacing, spacing, bowTensionIndicatorWidth, bowTensionIndicatorHeight, WHITE);
|
||||||
|
DrawRectangle(screenWidth - bowTensionIndicatorWidth - spacing, spacing, map(bowTension, 0, maxBowTension, 0, bowTensionIndicatorWidth), bowTensionIndicatorHeight, WHITE);
|
||||||
|
|
||||||
// end the frame and get ready for the next one (display frame, poll input, etc...)
|
// end the frame and get ready for the next one (display frame, poll input, etc...)
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
|
// free arrow linked list
|
||||||
|
ArrowNode *el = head;
|
||||||
|
while (el != NULL)
|
||||||
|
{
|
||||||
|
ArrowNode *next = el->next;
|
||||||
|
free(el->data);
|
||||||
|
free(el);
|
||||||
|
el = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// free target linked list
|
||||||
|
TargetNode *tEl = targets;
|
||||||
|
while (tEl != NULL)
|
||||||
|
{
|
||||||
|
TargetNode *next = tEl->next;
|
||||||
|
free(tEl->data);
|
||||||
|
free(tEl);
|
||||||
|
tEl = next;
|
||||||
|
}
|
||||||
|
|
||||||
// unload our texture so it can be cleaned up
|
// unload our texture so it can be cleaned up
|
||||||
UnloadTexture(wabbit);
|
UnloadTexture(wabbit);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user