Compare commits
11 Commits
acc91fd5a8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| be888397e4 | |||
|
|
4cf96e771f | ||
|
|
759d3ff61a | ||
|
|
3f4dfdc483 | ||
|
|
c8e40f3845 | ||
|
|
8853ece16a | ||
|
|
75d4e7fc9f | ||
|
|
31630dded8 | ||
|
|
c880765945 | ||
|
|
e5415bcb6e | ||
|
|
a6c1b96d0a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -405,3 +405,4 @@ Makefile
|
||||
*.swp
|
||||
*.swo
|
||||
*.swn
|
||||
/build/compile_commands.json
|
||||
|
||||
38
.vscode/launch.json
vendored
38
.vscode/launch.json
vendored
@@ -8,7 +8,6 @@
|
||||
"name": "Debug",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bin/Debug/${workspaceFolderBasename}.exe",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
@@ -23,7 +22,8 @@
|
||||
}
|
||||
],
|
||||
"windows": {
|
||||
"miDebuggerPath": "gdb.exe",
|
||||
"program": "${workspaceFolder}/bin/Debug/${workspaceFolderBasename}.exe",
|
||||
"miDebuggerPath": "gdb.exe"
|
||||
},
|
||||
"osx": {
|
||||
"program": "${workspaceFolder}/bin/Debug/${workspaceFolderBasename}",
|
||||
@@ -36,7 +36,38 @@
|
||||
"preLaunchTask": "build debug"
|
||||
},
|
||||
{
|
||||
"name": "Run",
|
||||
"name": "Debug NoPremake",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": false
|
||||
}
|
||||
],
|
||||
"windows": {
|
||||
"program": "${workspaceFolder}/bin/Debug/${workspaceFolderBasename}.exe",
|
||||
"miDebuggerPath": "gdb.exe"
|
||||
},
|
||||
"osx": {
|
||||
"program": "${workspaceFolder}/bin/Debug/${workspaceFolderBasename}",
|
||||
"MIMode": "lldb"
|
||||
},
|
||||
"linux": {
|
||||
"program": "${workspaceFolder}/bin/Debug/${workspaceFolderBasename}",
|
||||
"miDebuggerPath": "/usr/bin/gdb",
|
||||
},
|
||||
"preLaunchTask": "build debug no premake"
|
||||
},
|
||||
{
|
||||
"name": "Run Release",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"args": [],
|
||||
@@ -44,7 +75,6 @@
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"program": "${workspaceFolder}/bin/Release/${workspaceFolderBasename}.exe",
|
||||
"MIMode": "gdb",
|
||||
"windows": {
|
||||
"program": "${workspaceFolder}/bin/Release/${workspaceFolderBasename}.exe",
|
||||
|
||||
45
.vscode/tasks.json
vendored
45
.vscode/tasks.json
vendored
@@ -24,6 +24,26 @@
|
||||
],
|
||||
"dependsOn":["UpdateMake"]
|
||||
},
|
||||
{
|
||||
"label": "build debug no premake",
|
||||
"type": "process",
|
||||
"command": "make",
|
||||
"windows": {
|
||||
"command": "mingw32-make.exe",
|
||||
},
|
||||
"osx": {
|
||||
"args": [
|
||||
"config=debug_arm64"
|
||||
],
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "build release",
|
||||
"type": "process",
|
||||
@@ -95,7 +115,30 @@
|
||||
"osx": {
|
||||
"command": "premake5.osx"
|
||||
},
|
||||
"group": "build"
|
||||
"group": "build",
|
||||
"dependsOn": ["Generate compile_commands.json"]
|
||||
},
|
||||
{
|
||||
"label": "Generate compile_commands.json",
|
||||
"type": "process",
|
||||
"command": "./premake5",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/build/"
|
||||
},
|
||||
"args": [
|
||||
"ecc"
|
||||
],
|
||||
"windows": {
|
||||
"command": "./premake5.exe"
|
||||
},
|
||||
"linux": {
|
||||
"command": "./premake5"
|
||||
},
|
||||
"osx": {
|
||||
"command": "premake5.osx"
|
||||
},
|
||||
"group": "build",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
58
README.md
58
README.md
@@ -2,15 +2,36 @@
|
||||
A simple cross platform template for setting up a project with the bleeding edge raylib code.
|
||||
Works with C or C++.
|
||||
|
||||
# Basic Setup
|
||||
Download this repository to get started.
|
||||
|
||||
You can download the zip file of the repository from the Green Code button on github. This is the simplest way to get the template to start from.
|
||||
Once you have downloaded the template, rename it to your project name.
|
||||
|
||||
or
|
||||
|
||||
Clone the repository with git, from the url
|
||||
```
|
||||
https://github.com/raylib-extras/raylib-quickstart.git
|
||||
```
|
||||
|
||||
If you are using a command line git client you can use the command below to download and rename the template in one step
|
||||
```
|
||||
git clone https://github.com/raylib-extras/raylib-quickstart.git [name-for-your-project-here]
|
||||
```
|
||||
|
||||
# Naming projects
|
||||
* Replace the placeholder with your desired project name when running the git clone command above.
|
||||
* __Do not name your game project 'raylib', it will conflict with the raylib library.__
|
||||
* If you have used custom game name with __git clone__, there is no need to rename it again.
|
||||
|
||||
|
||||
## Supported Platforms
|
||||
Quickstart supports the main 3 desktop platforms:
|
||||
* Windows
|
||||
* Linux
|
||||
* MacOS
|
||||
|
||||
# Naming projects
|
||||
Do not name your game project 'raylib', it will conflict with the raylib library.
|
||||
|
||||
# VSCode Users (all platforms)
|
||||
*Note* You must have a compiler toolchain installed in addition to vscode.
|
||||
|
||||
@@ -23,9 +44,10 @@ Do not name your game project 'raylib', it will conflict with the raylib library
|
||||
# Windows Users
|
||||
There are two compiler toolchains available for windows, MinGW-W64 (a free compiler using GCC), and Microsoft Visual Studio
|
||||
## Using MinGW-W64
|
||||
* Rename the folder to your game name
|
||||
* Double click the `build-MinGW-W64.bat` file
|
||||
* CD into the folder in your terminal
|
||||
* if you are usiing the W64devkit and have not added it to your system path environment variable, you must use the W64devkit.exe terminal, not CMD.exe
|
||||
* if you are using the W64devkit and have not added it to your system path environment variable, you must use the W64devkit.exe terminal, not CMD.exe
|
||||
* If you want to use cmd.exe or any other terminal, please make sure that gcc/mingw-W64 is in your path environment variable.
|
||||
* run `make`
|
||||
* You are good to go
|
||||
@@ -34,7 +56,9 @@ There are two compiler toolchains available for windows, MinGW-W64 (a free compi
|
||||
Make sure you have a modern version of MinGW-W64 (not mingw).
|
||||
The best place to get it is from the W64devkit from
|
||||
https://github.com/skeeto/w64devkit/releases
|
||||
|
||||
or the version installed with the raylib installer
|
||||
|
||||
#### If you have installed raylib from the installer
|
||||
Make sure you have added the path
|
||||
|
||||
@@ -45,12 +69,14 @@ To your path environment variable so that the compiler that came with raylib can
|
||||
DO NOT INSTALL ANOTHER MinGW-W64 from another source such as msys2, you don't need it.
|
||||
|
||||
## Microsoft Visual Studio
|
||||
* Rename the folder to your game name
|
||||
* Run `build-VisualStudio2022.bat`
|
||||
* double click the `.sln` file that is generated
|
||||
* develop your game
|
||||
* you are good to go
|
||||
|
||||
# Linux Users
|
||||
* Rename the folder to your game name
|
||||
* CD into the build folder
|
||||
* run `./premake5 gmake`
|
||||
* CD back to the root
|
||||
@@ -58,6 +84,7 @@ DO NOT INSTALL ANOTHER MinGW-W64 from another source such as msys2, you don't ne
|
||||
* you are good to go
|
||||
|
||||
# MacOS Users
|
||||
* Rename the folder to your game name
|
||||
* CD into the build folder
|
||||
* run `./premake5.osx gmake`
|
||||
* CD back to the root
|
||||
@@ -94,6 +121,13 @@ If you need to build for a different OpenGL version than the default (OpenGL 3.3
|
||||
## For OpenGLES 3.0
|
||||
`--graphics=opengles3`
|
||||
|
||||
## For Software Rendering
|
||||
`--graphics=software`
|
||||
|
||||
*Note*
|
||||
Sofware rendering does not work with glfw, use Win32 or SDL platforms
|
||||
`--backend=win32`
|
||||
|
||||
# Adding External Libraries
|
||||
|
||||
Quickstart is intentionally minimal — it only includes what is required to compile and run a basic raylib project.
|
||||
@@ -123,19 +157,5 @@ Different libraries will have different dependencies on different platforms.
|
||||
|
||||
|
||||
# License
|
||||
Copyright (c) 2020-2025 Jeffery Myers
|
||||
Raylib-Quickstart by Jeffery Myers is marked with CC0 1.0. To view a copy of this license, visit https://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
This software is provided "as-is", without any express or implied warranty. In no event
|
||||
will the authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial
|
||||
applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you
|
||||
wrote the original software. If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented
|
||||
as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
29
build/ecc/LICENSE.md
Normal file
29
build/ecc/LICENSE.md
Normal file
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2022, Matvey Bystrin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
49
build/ecc/README.md
Normal file
49
build/ecc/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Export Compile Commands - ECC
|
||||
Module implementing support for [JSON Compilation Database Format
|
||||
Specification](https://clang.llvm.org/docs/JSONCompilationDatabase.html).
|
||||
|
||||
This is an alternative to
|
||||
[tarruda's](https://github.com/tarruda/premake-export-compile-commands) module,
|
||||
which does one simple thing - generates one `compile_commands.json` file for
|
||||
your project.
|
||||
|
||||
Tested with clangd-13.
|
||||
|
||||
## Requirements
|
||||
Premake 5.0.0 or later.
|
||||
|
||||
## How to use
|
||||
Download premake-ecc and place it near your premake5.lua script. Then require it
|
||||
from your premake script
|
||||
|
||||
```lua
|
||||
require "ecc/ecc"
|
||||
```
|
||||
|
||||
After you simply can call:
|
||||
|
||||
```
|
||||
premake5 ecc
|
||||
```
|
||||
|
||||
Moldule will generate **one** `compile_commands.json` file near your main
|
||||
premake script. During generation it will use the default config (the first one
|
||||
you have specified in script). If you want to select specific config just pass
|
||||
it's name with command line option:
|
||||
|
||||
```
|
||||
premake5 --config=release ecc
|
||||
```
|
||||
|
||||
Careful! `config` option case sensitive! If there is no config passed via
|
||||
command line, module will choose the default one.
|
||||
|
||||
Note: if you want to embed this module into your premake build follow
|
||||
the [manual](https://premake.github.io/docs/Embedding-Modules/)
|
||||
|
||||
## Future plans
|
||||
- Add unit tests
|
||||
|
||||
## Alternatives
|
||||
- [export-compile-commands](https://github.com/tarruda/premake-export-compile-commands)
|
||||
- [bear](https://github.com/rizsotto/Bear)
|
||||
4
build/ecc/_manifest.lua
Normal file
4
build/ecc/_manifest.lua
Normal file
@@ -0,0 +1,4 @@
|
||||
return {
|
||||
"_preload.lua",
|
||||
"ecc.lua"
|
||||
}
|
||||
35
build/ecc/_preload.lua
Normal file
35
build/ecc/_preload.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
local p = premake
|
||||
|
||||
newoption {
|
||||
trigger = "config",
|
||||
value = "CFG",
|
||||
description = "Select config for export compile_commands.json"
|
||||
}
|
||||
|
||||
newaction {
|
||||
trigger = "ecc",
|
||||
shortname = "Export compile commands",
|
||||
description = "Export compile_commands.json for language server",
|
||||
toolset = "gcc",
|
||||
|
||||
valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib" },
|
||||
valid_languages = { "C", "C++" },
|
||||
valid_tools = {
|
||||
cc = { "clang", "gcc" }
|
||||
},
|
||||
|
||||
onStart = function()
|
||||
p.indent(" ")
|
||||
end,
|
||||
|
||||
execute = function()
|
||||
local dir = {}
|
||||
dir.location = _MAIN_SCRIPT_DIR
|
||||
p.generate(dir, "compile_commands.json", p.modules.ecc.generateFile)
|
||||
end
|
||||
}
|
||||
|
||||
return function(cfg)
|
||||
return (_ACTION == "ecc")
|
||||
end
|
||||
122
build/ecc/ecc.lua
Normal file
122
build/ecc/ecc.lua
Normal file
@@ -0,0 +1,122 @@
|
||||
|
||||
-- Include module if it is not embedded
|
||||
if premake.modules.ecc == nil then
|
||||
include ( "_preload.lua" )
|
||||
end
|
||||
|
||||
local p = premake
|
||||
local project = p.project
|
||||
|
||||
p.modules.ecc = {}
|
||||
local m = p.modules.ecc
|
||||
|
||||
m._VERSION = "1.0.1-alpha"
|
||||
|
||||
function m.generateFile()
|
||||
p.push("[")
|
||||
for wks in p.global.eachWorkspace() do
|
||||
for prj in p.workspace.eachproject(wks) do
|
||||
m.onProject(prj)
|
||||
end
|
||||
end
|
||||
p.pop("]")
|
||||
end
|
||||
|
||||
function m.onProject(prj)
|
||||
if project.isc(prj) or project.iscpp(prj) then
|
||||
local cfg = m.getConfig(prj)
|
||||
local args = m.getArguments(prj, cfg)
|
||||
local files = table.shallowcopy(prj._.files)
|
||||
for i,node in ipairs(files) do
|
||||
local output = cfg.objdir .. "/" .. node.objname .. ".o"
|
||||
local obj = path.getrelative(prj.location, output)
|
||||
p.push("{")
|
||||
p.push("\"arguments\": [")
|
||||
m.writeArgs(args, obj, node.relpath)
|
||||
p.pop("],")
|
||||
p.w("\"directory\": \"%s\",", prj.location)
|
||||
p.w("\"file\": \"%s\",", node.abspath)
|
||||
p.w("\"output\": \"%s\"", output)
|
||||
p.pop("},")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function m.writeArgs(args, obj, src)
|
||||
for _,arg in ipairs(args) do
|
||||
-- Defines like the following will break JSON format, quotes need to be escaped
|
||||
-- -DEXPORT_API=__attribute__((visibility("default")))
|
||||
local escaped = arg:gsub("\"", "\\\"")
|
||||
p.w("\"%s\",", escaped)
|
||||
end
|
||||
p.w("\"-c\",")
|
||||
p.w("\"-o\",")
|
||||
p.w("\"%s\",", obj)
|
||||
p.w("\"%s\"", src)
|
||||
end
|
||||
|
||||
function m.getConfig(prj)
|
||||
local ocfg = _OPTIONS.config
|
||||
local cfg = {}
|
||||
if ocfg and prj.configs[ocfg] then
|
||||
cfg = prj.configs[ocfg]
|
||||
else
|
||||
cfg = m.defaultconfig(prj)
|
||||
end
|
||||
return cfg
|
||||
end
|
||||
|
||||
function m.getArguments(prj, cfg)
|
||||
local toolset = m.getToolSet(cfg)
|
||||
local args = {}
|
||||
local tool = iif(project.iscpp(prj), "cxx", "cc")
|
||||
local toolname = iif(cfg.prefix, toolset.gettoolname(cfg, tool), toolset.tools[tool])
|
||||
args = table.join(args, toolname)
|
||||
args = table.join(args, toolset.getcppflags(cfg)) -- Preprocessor
|
||||
args = table.join(args, toolset.getdefines(cfg.defines))
|
||||
args = table.join(args, toolset.getundefines(cfg.undefines))
|
||||
args = table.join(args, toolset.getincludedirs(cfg, cfg.includedirs, cfg.sysincludedirs))
|
||||
if project.iscpp(prj) then
|
||||
args = table.join(args, toolset.getcxxflags(cfg))
|
||||
else
|
||||
args = table.join(args, toolset.getcflags(cfg))
|
||||
end
|
||||
args = table.join(args, cfg.buildoptions)
|
||||
return args
|
||||
end
|
||||
|
||||
-- Copied from gmake2 module
|
||||
-- Return default toolset of given config or system default toolset
|
||||
function m.getToolSet(cfg)
|
||||
local default = iif(cfg.system == p.MACOSX, "clang", "gcc")
|
||||
local toolset = p.tools[_OPTIONS.cc or cfg.toolset or default]
|
||||
if not toolset then
|
||||
error("Invalid toolset '" .. cfg.toolset .. "'")
|
||||
end
|
||||
return toolset
|
||||
end
|
||||
|
||||
-- Copied from gmake2 module
|
||||
function m.defaultconfig(target)
|
||||
-- find the right configuration iterator function for this object
|
||||
local eachconfig = iif(target.project, project.eachconfig, p.workspace.eachconfig)
|
||||
local defaultconfig = nil
|
||||
|
||||
-- find the right default configuration platform, grab first configuration that matches
|
||||
if target.defaultplatform then
|
||||
for cfg in eachconfig(target) do
|
||||
if cfg.platform == target.defaultplatform then
|
||||
defaultconfig = cfg
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- grab the first configuration and write the block
|
||||
if not defaultconfig then
|
||||
local iter = eachconfig(target)
|
||||
defaultconfig = iter()
|
||||
end
|
||||
|
||||
return defaultconfig
|
||||
end
|
||||
@@ -1,3 +1,6 @@
|
||||
-- Export Compile Commands module for clangd support
|
||||
require "ecc/ecc"
|
||||
|
||||
newoption
|
||||
{
|
||||
trigger = "graphics",
|
||||
@@ -189,13 +192,15 @@ if (downloadRaylib) then
|
||||
{
|
||||
["Header Files/*"] = { "../include/**.h", "../include/**.hpp", "../src/**.h", "../src/**.hpp"},
|
||||
["Source Files/*"] = {"../src/**.c", "src/**.cpp"},
|
||||
["Windows Resource Files/*"] = {"../src/**.rc", "src/**.ico"},
|
||||
["Windows Resource Files/*"] = {"../src/**.rc", "../src/**.ico"},
|
||||
["Game Resource Files/*"] = {"../resources/**"},
|
||||
}
|
||||
|
||||
files {"../src/**.c", "../src/**.cpp", "../src/**.h", "../src/**.hpp", "../include/**.h", "../include/**.hpp"}
|
||||
|
||||
filter {"system:windows", "action:vs*"}
|
||||
files {"../src/*.rc", "../src/*.ico"}
|
||||
files {"../resources/**"}
|
||||
|
||||
filter{}
|
||||
|
||||
|
||||
616
src/main.c
616
src/main.c
@@ -1,23 +1,462 @@
|
||||
/*
|
||||
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");
|
||||
@@ -25,26 +464,175 @@ int main ()
|
||||
// Load a texture from the resources directory
|
||||
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
|
||||
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 our texture to the screen
|
||||
DrawTexture(wabbit, 400, 200, WHITE);
|
||||
// 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);
|
||||
|
||||
// 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