feat: initial revision
This commit is contained in:
624
src/main.c
624
src/main.c
@@ -1,50 +1,638 @@
|
||||
/*
|
||||
Raylib example file.
|
||||
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/
|
||||
|
||||
Shooting arrows - ya know
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "raylib.h"
|
||||
#include "resource_dir.h"
|
||||
|
||||
#include "resource_dir.h" // utility header for SearchAndSetResourceDir
|
||||
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;
|
||||
|
||||
int main ()
|
||||
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()
|
||||
{
|
||||
// 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
|
||||
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
|
||||
SearchAndSetResourceDir("resources");
|
||||
|
||||
// Load a texture from the resources directory
|
||||
Texture wabbit = LoadTexture("wabbit_alpha.png");
|
||||
|
||||
// game loop
|
||||
while (!WindowShouldClose()) // run the loop until the user presses ESCAPE or presses the Close button on the window
|
||||
|
||||
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
|
||||
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
|
||||
BeginDrawing();
|
||||
|
||||
// Setup the back buffer for drawing (clear color and depth buffers)
|
||||
ClearBackground(BLACK);
|
||||
|
||||
// draw some text using the default font
|
||||
DrawText("Hello Raylib", 200,200,20,WHITE);
|
||||
// Draw ground
|
||||
DrawRectangle(0, screenHeight - groundHeight, screenWidth, groundHeight, BROWN);
|
||||
|
||||
// draw targets
|
||||
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);
|
||||
|
||||
// draw our texture to the screen
|
||||
DrawTexture(wabbit, 400, 200, WHITE);
|
||||
|
||||
// end the frame and get ready for the next one (display frame, poll input, etc...)
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
// 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
|
||||
UnloadTexture(wabbit);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user