mirror of
https://github.com/thepigeongenerator/tetris_clone.git
synced 2025-12-18 14:35:45 +01:00
Compare commits
10 Commits
18766645fd
...
fef5885293
| Author | SHA1 | Date | |
|---|---|---|---|
| fef5885293 | |||
| 2309bd86eb | |||
| 2e36c53d4e | |||
| baf680ee3c | |||
| a827c84223 | |||
| f8eb814a4a | |||
| 42b9e9b10c | |||
| 7ccc679d54 | |||
| e9226e4a39 | |||
| d471c42388 |
311
lib/glad/include/KHR/khrplatform.h
Normal file
311
lib/glad/include/KHR/khrplatform.h
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
#ifndef __khrplatform_h_
|
||||||
|
#define __khrplatform_h_
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
** copy of this software and/or associated documentation files (the
|
||||||
|
** "Materials"), to deal in the Materials without restriction, including
|
||||||
|
** without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||||
|
** permit persons to whom the Materials are furnished to do so, subject to
|
||||||
|
** the following conditions:
|
||||||
|
**
|
||||||
|
** The above copyright notice and this permission notice shall be included
|
||||||
|
** in all copies or substantial portions of the Materials.
|
||||||
|
**
|
||||||
|
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Khronos platform-specific types and definitions.
|
||||||
|
*
|
||||||
|
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||||
|
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||||
|
* The last semantic modification to khrplatform.h was at commit ID:
|
||||||
|
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||||
|
*
|
||||||
|
* Adopters may modify this file to suit their platform. Adopters are
|
||||||
|
* encouraged to submit platform specific modifications to the Khronos
|
||||||
|
* group so that they can be included in future versions of this file.
|
||||||
|
* Please submit changes by filing pull requests or issues on
|
||||||
|
* the EGL Registry repository linked above.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* See the Implementer's Guidelines for information about where this file
|
||||||
|
* should be located on your system and for more details of its use:
|
||||||
|
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||||
|
*
|
||||||
|
* This file should be included as
|
||||||
|
* #include <KHR/khrplatform.h>
|
||||||
|
* by Khronos client API header files that use its types and defines.
|
||||||
|
*
|
||||||
|
* The types in khrplatform.h should only be used to define API-specific types.
|
||||||
|
*
|
||||||
|
* Types defined in khrplatform.h:
|
||||||
|
* khronos_int8_t signed 8 bit
|
||||||
|
* khronos_uint8_t unsigned 8 bit
|
||||||
|
* khronos_int16_t signed 16 bit
|
||||||
|
* khronos_uint16_t unsigned 16 bit
|
||||||
|
* khronos_int32_t signed 32 bit
|
||||||
|
* khronos_uint32_t unsigned 32 bit
|
||||||
|
* khronos_int64_t signed 64 bit
|
||||||
|
* khronos_uint64_t unsigned 64 bit
|
||||||
|
* khronos_intptr_t signed same number of bits as a pointer
|
||||||
|
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||||
|
* khronos_ssize_t signed size
|
||||||
|
* khronos_usize_t unsigned size
|
||||||
|
* khronos_float_t signed 32 bit floating point
|
||||||
|
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||||
|
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||||
|
* nanoseconds
|
||||||
|
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||||
|
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||||
|
* only be used as a base type when a client API's boolean type is
|
||||||
|
* an enum. Client APIs which use an integer or other type for
|
||||||
|
* booleans cannot use this as the base type for their boolean.
|
||||||
|
*
|
||||||
|
* Tokens defined in khrplatform.h:
|
||||||
|
*
|
||||||
|
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||||
|
*
|
||||||
|
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||||
|
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||||
|
*
|
||||||
|
* Calling convention macros defined in this file:
|
||||||
|
* KHRONOS_APICALL
|
||||||
|
* KHRONOS_APIENTRY
|
||||||
|
* KHRONOS_APIATTRIBUTES
|
||||||
|
*
|
||||||
|
* These may be used in function prototypes as:
|
||||||
|
*
|
||||||
|
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||||
|
* int arg1,
|
||||||
|
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||||
|
# define KHRONOS_STATIC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* Definition of KHRONOS_APICALL
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
* This precedes the return type of the function in the function prototype.
|
||||||
|
*/
|
||||||
|
#if defined(KHRONOS_STATIC)
|
||||||
|
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||||
|
* header compatible with static linking. */
|
||||||
|
# define KHRONOS_APICALL
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
# define KHRONOS_APICALL __declspec(dllimport)
|
||||||
|
#elif defined (__SYMBIAN32__)
|
||||||
|
# define KHRONOS_APICALL IMPORT_C
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
# define KHRONOS_APICALL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* Definition of KHRONOS_APIENTRY
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
* This follows the return type of the function and precedes the function
|
||||||
|
* name in the function prototype.
|
||||||
|
*/
|
||||||
|
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||||
|
/* Win32 but not WinCE */
|
||||||
|
# define KHRONOS_APIENTRY __stdcall
|
||||||
|
#else
|
||||||
|
# define KHRONOS_APIENTRY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* Definition of KHRONOS_APIATTRIBUTES
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
* This follows the closing parenthesis of the function prototype arguments.
|
||||||
|
*/
|
||||||
|
#if defined (__ARMCC_2__)
|
||||||
|
#define KHRONOS_APIATTRIBUTES __softfp
|
||||||
|
#else
|
||||||
|
#define KHRONOS_APIATTRIBUTES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* basic type definitions
|
||||||
|
*-----------------------------------------------------------------------*/
|
||||||
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using <stdint.h>
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef int32_t khronos_int32_t;
|
||||||
|
typedef uint32_t khronos_uint32_t;
|
||||||
|
typedef int64_t khronos_int64_t;
|
||||||
|
typedef uint64_t khronos_uint64_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
/*
|
||||||
|
* To support platform where unsigned long cannot be used interchangeably with
|
||||||
|
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||||
|
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||||
|
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||||
|
* unsigned long long or similar (this results in different C++ name mangling).
|
||||||
|
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||||
|
* platforms where the size of a pointer is larger than the size of long.
|
||||||
|
*/
|
||||||
|
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||||
|
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||||
|
#define KHRONOS_USE_INTPTR_T
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(__VMS ) || defined(__sgi)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using <inttypes.h>
|
||||||
|
*/
|
||||||
|
#include <inttypes.h>
|
||||||
|
typedef int32_t khronos_int32_t;
|
||||||
|
typedef uint32_t khronos_uint32_t;
|
||||||
|
typedef int64_t khronos_int64_t;
|
||||||
|
typedef uint64_t khronos_uint64_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
|
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Win32
|
||||||
|
*/
|
||||||
|
typedef __int32 khronos_int32_t;
|
||||||
|
typedef unsigned __int32 khronos_uint32_t;
|
||||||
|
typedef __int64 khronos_int64_t;
|
||||||
|
typedef unsigned __int64 khronos_uint64_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
|
#elif defined(__sun__) || defined(__digital__)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sun or Digital
|
||||||
|
*/
|
||||||
|
typedef int khronos_int32_t;
|
||||||
|
typedef unsigned int khronos_uint32_t;
|
||||||
|
#if defined(__arch64__) || defined(_LP64)
|
||||||
|
typedef long int khronos_int64_t;
|
||||||
|
typedef unsigned long int khronos_uint64_t;
|
||||||
|
#else
|
||||||
|
typedef long long int khronos_int64_t;
|
||||||
|
typedef unsigned long long int khronos_uint64_t;
|
||||||
|
#endif /* __arch64__ */
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
|
#elif 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hypothetical platform with no float or int64 support
|
||||||
|
*/
|
||||||
|
typedef int khronos_int32_t;
|
||||||
|
typedef unsigned int khronos_uint32_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 0
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 0
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic fallback
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef int32_t khronos_int32_t;
|
||||||
|
typedef uint32_t khronos_uint32_t;
|
||||||
|
typedef int64_t khronos_int64_t;
|
||||||
|
typedef uint64_t khronos_uint64_t;
|
||||||
|
#define KHRONOS_SUPPORT_INT64 1
|
||||||
|
#define KHRONOS_SUPPORT_FLOAT 1
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Types that are (so far) the same on all platforms
|
||||||
|
*/
|
||||||
|
typedef signed char khronos_int8_t;
|
||||||
|
typedef unsigned char khronos_uint8_t;
|
||||||
|
typedef signed short int khronos_int16_t;
|
||||||
|
typedef unsigned short int khronos_uint16_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||||
|
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||||
|
* to be the only LLP64 architecture in current use.
|
||||||
|
*/
|
||||||
|
#ifdef KHRONOS_USE_INTPTR_T
|
||||||
|
typedef intptr_t khronos_intptr_t;
|
||||||
|
typedef uintptr_t khronos_uintptr_t;
|
||||||
|
#elif defined(_WIN64)
|
||||||
|
typedef signed long long int khronos_intptr_t;
|
||||||
|
typedef unsigned long long int khronos_uintptr_t;
|
||||||
|
#else
|
||||||
|
typedef signed long int khronos_intptr_t;
|
||||||
|
typedef unsigned long int khronos_uintptr_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
|
typedef signed long long int khronos_ssize_t;
|
||||||
|
typedef unsigned long long int khronos_usize_t;
|
||||||
|
#else
|
||||||
|
typedef signed long int khronos_ssize_t;
|
||||||
|
typedef unsigned long int khronos_usize_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if KHRONOS_SUPPORT_FLOAT
|
||||||
|
/*
|
||||||
|
* Float type
|
||||||
|
*/
|
||||||
|
typedef float khronos_float_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if KHRONOS_SUPPORT_INT64
|
||||||
|
/* Time types
|
||||||
|
*
|
||||||
|
* These types can be used to represent a time interval in nanoseconds or
|
||||||
|
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||||
|
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||||
|
* time the system booted). The Unadjusted System Time is an unsigned
|
||||||
|
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||||
|
* may be either signed or unsigned.
|
||||||
|
*/
|
||||||
|
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||||
|
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dummy value used to pad enum types to 32 bits.
|
||||||
|
*/
|
||||||
|
#ifndef KHRONOS_MAX_ENUM
|
||||||
|
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enumerated boolean type
|
||||||
|
*
|
||||||
|
* Values other than zero should be considered to be true. Therefore
|
||||||
|
* comparisons should not be made against KHRONOS_TRUE.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
KHRONOS_FALSE = 0,
|
||||||
|
KHRONOS_TRUE = 1,
|
||||||
|
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||||
|
} khronos_boolean_enum_t;
|
||||||
|
|
||||||
|
#endif /* __khrplatform_h_ */
|
||||||
13568
lib/glad/include/glad/gl.h
Normal file
13568
lib/glad/include/glad/gl.h
Normal file
File diff suppressed because one or more lines are too long
8012
lib/glad/src/gl.c
Normal file
8012
lib/glad/src/gl.c
Normal file
File diff suppressed because it is too large
Load Diff
159
makefile
159
makefile
@@ -1,115 +1,86 @@
|
|||||||
# dependencies:
|
SHELL = bash
|
||||||
# - make
|
.SHELLFLAGS = -O globstar -c
|
||||||
# - clang
|
NAME = tetris
|
||||||
# - bear (debug)
|
VERSION = 0.0.3
|
||||||
# - sdl2
|
DEBUG ?= 0
|
||||||
# - git bash (windows)
|
CC ?= cc
|
||||||
#
|
LD ?= ld
|
||||||
# project name = the workspace directory name
|
MARCH ?= $(shell uname -m)
|
||||||
NAME := $(shell basename $(PWD))
|
KERNEL ?= $(shell uname -s)
|
||||||
DEBUG ?= 0
|
|
||||||
ARCH ?= 0
|
|
||||||
|
|
||||||
# compiler settings
|
CFLAGS += -c -std=gnu99 -Wall -Wextra -Wpedantic -MMD -MP
|
||||||
CC := clang
|
LDFLAGS += -flto
|
||||||
STD := c17
|
|
||||||
CFLAGS := -m32 -Wall -Wextra -Wpedantic -Wno-pointer-arith
|
|
||||||
LDFLAGS := -m32 -lm
|
|
||||||
|
|
||||||
ifneq ($(DEBUG),0)
|
ifeq ($(KERNEL),)
|
||||||
CFLAGS += -g -Og -fsanitize=address,undefined
|
ISWIN := $(if $(filter $(OS),Windows_NT),1,0)
|
||||||
LDFLAGS += -fsanitize=address,undefined
|
ifeq ($(ISWIN),1)
|
||||||
PROF := dbg
|
KERNEL = mingw
|
||||||
|
MARCH = x86_64
|
||||||
else
|
else
|
||||||
CFLAGS += -DNDEBUG -O2 -Werror
|
MARCH := $(shell uname -m)
|
||||||
PROF := rel
|
KERNEL := $(shell uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(MAKECMDGOALS),clean)
|
|
||||||
ifeq ($(ARCH),linux-x86)
|
|
||||||
CFLAGS += -target x86_64-pc-linux-gnu $(shell pkg-config --cflags sdl2 SDL2_ttf)
|
|
||||||
LDFLAGS += -target x86_64-pc-linux-gnu $(shell pkg-config --libs sdl2 SDL2_ttf)
|
|
||||||
else ifeq ($(ARCH),win-x86)
|
|
||||||
CFLAGS += -target x86_64-pc-windows-gnu
|
|
||||||
LDFLAGS += -target x86_64-pc-windows-gnu -fuse-ld=lld
|
|
||||||
EXT := .exe
|
|
||||||
else
|
else
|
||||||
$(error you must set the ARCH environment variable to one of these: 'linux-x86' 'win-x86')
|
ISWIN := $(if $(filter $(KERNEL),mingw),1,0)
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(MARCH),)
|
||||||
|
$(error must also set MARCH when manually setting KERNEL)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(ARCH),0)
|
ifeq ($(DEBUG),1)
|
||||||
# dirs
|
PROF = dbg
|
||||||
DIR_BIN := bin/$(ARCH)/$(PROF)
|
CFLAGS += -UNDEBUG -Og -g -Wextra -Wpedantic
|
||||||
DIR_OBJ := obj/$(ARCH)/$(PROF)
|
CFLAGS += $(if $(filter 1,$(ISWIN)),,-fsanitize=address,undefined) -ftrapv
|
||||||
TARGET := $(DIR_BIN)/$(NAME)$(EXT)
|
LDFLAGS += $(if $(filter 1,$(ISWIN)),,-fsanitize=address,undefined) -ftrapv
|
||||||
|
else
|
||||||
# source files
|
PROF = rel
|
||||||
SRC := $(wildcard src/*.c) $(wildcard src/**/*.c) $(wildcard src/**/**/*.c) $(wildcard src/**/**/**/*.c) $(wildcard src/**/**/**/**/*.c)
|
CFLAGS += -DNDEBUG -O3
|
||||||
OBJ := $(patsubst src/%,$(DIR_OBJ)/%,$(SRC:.c=.o))
|
|
||||||
DEP := $(OBJ:.o=.d)
|
|
||||||
SRC_ASSETS := $(wildcard assets/*)
|
|
||||||
ASSETS := $(patsubst assets/%,$(DIR_BIN)/%,$(SRC_ASSETS))
|
|
||||||
|
|
||||||
COMPILE_COMMANDS := $(DIR_OBJ)/compile_commands.json
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
define wr_colour
|
CFLAGS += $(shell pkg-config --cflags glfw3) -Ilib/glad/include
|
||||||
@printf '\033[%sm%s\033[0m\n' $(2) $(1)
|
LDFLAGS += $(shell pkg-config --libs glfw3) -lm
|
||||||
endef
|
|
||||||
|
|
||||||
# compiles and execute the binary
|
SRC := $(shell echo src/**/*.c) lib/glad/src/gl.c
|
||||||
|
RES := $(shell echo res/**.glsl)
|
||||||
|
|
||||||
|
NAME += $(if $(filter 1,$(ISWIN)),.exe,)
|
||||||
|
DIR_BIN := bin/$(MARCH)-$(KERNEL)/$(VERSION)/$(PROF)
|
||||||
|
DIR_OBJ := obj/$(MARCH)-$(KERNEL)/$(VERSION)/$(PROF)
|
||||||
|
|
||||||
|
BIN := $(DIR_BIN)/$(NAME)
|
||||||
|
OBJ := $(SRC:%.c=$(DIR_OBJ)/%.o) $(RES:%=$(DIR_OBJ)/%.o)
|
||||||
|
DEP := $(OBJ:%.o=%.d)
|
||||||
|
|
||||||
|
.PHONY:
|
||||||
run: compile
|
run: compile
|
||||||
cd $(dir $(TARGET)) && ./$(notdir $(TARGET))
|
$(BIN)
|
||||||
compile: compile_commands $(TARGET) $(ASSETS)
|
|
||||||
|
|
||||||
.NOTPARALLEL:
|
.PHONY:
|
||||||
|
compile: $(BIN)
|
||||||
|
|
||||||
|
.PHONY .NOTPARALLEL:
|
||||||
clean:
|
clean:
|
||||||
rm -rf obj/ bin/ compile_commands.json
|
@[ -d obj/ ] && rm -rv obj/ || true
|
||||||
|
@[ -d bin/ ] && rm -rv bin/ || true
|
||||||
|
|
||||||
# create the binary (linking step)
|
|
||||||
$(TARGET): $(OBJ)
|
|
||||||
@$(call wr_colour,"CC: '$(CC)'",94)
|
|
||||||
@$(call wr_colour,"CFLAGS: '$(CFLAGS)'",94)
|
|
||||||
@$(call wr_colour,"LDFLAGS: '$(LDFLAGS)'",94)
|
|
||||||
@$(call wr_colour,"linking to: '$@'",92)
|
|
||||||
|
|
||||||
@mkdir -p ${@D}
|
$(BIN): $(OBJ)
|
||||||
@$(CC) -o $(TARGET) $^ $(LDFLAGS)
|
$(info [CC/LD] $@)
|
||||||
@$(call wr_colour,"current profile: '$(PROF)'",93)
|
@mkdir -p $(@D)
|
||||||
|
@$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
# create .o and .d files
|
$(DIR_OBJ)/%.o: %.c
|
||||||
$(DIR_OBJ)/%.o: src/%.c
|
$(info [CC] $@)
|
||||||
@$(call wr_colour,"compiling $(notdir $@) from $(notdir $<)",92)
|
@mkdir -p $(@D)
|
||||||
@mkdir -p ${@D}
|
@$(CC) $(CFLAGS) -o $@ $<
|
||||||
@$(CC) $(CFLAGS) -c -MD -MP -std=$(STD) -x c -o $@ $<
|
|
||||||
|
|
||||||
# copy assets
|
$(DIR_OBJ)/res/%.o: res/%
|
||||||
$(DIR_BIN)/%: assets/%
|
$(info [LD] $@)
|
||||||
@mkdir -p ${@D}
|
@mkdir -p $(@D)
|
||||||
@cp -v $< $@
|
@$(LD) -r -b binary -o $@ $<
|
||||||
|
|
||||||
# update compile commands if the makefile has been updated (for linting)
|
.PHONY: x86_64-linux-gnu-gcc x86_64-w64-mingw32-gcc
|
||||||
compile_commands: # default, empty rule
|
x86_64-linux-gnu-gcc:; $(MAKE) $(CALL) $(MAKEFLAGS) CC=$@ MARCH=x86_64 KERNEL=linux
|
||||||
ifneq ($(shell which bear),)
|
x86_64-w64-mingw32-gcc:; $(MAKE) $(CALL) $(MAKEFLAGS) CC=$@ MARCH=x86_64 KERNEL=mingw
|
||||||
ifneq ($(COMPILE_COMMANDS),)
|
|
||||||
ifeq ($(NOCMDS),)
|
|
||||||
.NOTPARALLEL .PHONY:
|
|
||||||
compile_commands: $(COMPILE_COMMANDS)
|
|
||||||
@[ "$(readlink compile_commands.json)" != "$<" ] && ln -sf $< compile_commands.json
|
|
||||||
|
|
||||||
.NOTPARALLEL:
|
|
||||||
$(COMPILE_COMMANDS): makefile
|
|
||||||
@$(call wr_colour,"regenerating compile_commands.json thus recompiling.",93)
|
|
||||||
@mkdir -p ${@D} # ensure the target directory exists
|
|
||||||
@touch $@ # create the file so it isn't retriggered (will just change modification time if already exists)
|
|
||||||
@bear --output $@ -- make -B compile NOCMDS=1 # rebuild the current target using bear, to create the compile commands
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
# disable implicit rules
|
|
||||||
.SUFFIXES:
|
|
||||||
|
|
||||||
# include the dependencies
|
|
||||||
-include $(DEP)
|
-include $(DEP)
|
||||||
|
|||||||
1
res/sh.frag.glsl
Normal file
1
res/sh.frag.glsl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#version 330 core
|
||||||
1
res/sh.vert.glsl
Normal file
1
res/sh.vert.glsl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#version 330 core
|
||||||
50
src/error.h
50
src/error.h
@@ -1,50 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdnoreturn.h>
|
|
||||||
|
|
||||||
/* defines statuses in the 0..127, any higher/negative values are POSIX-reserved.
|
|
||||||
* The max value (or -1) shall mean the application is running, anything else shall mean an exit code of some kind */
|
|
||||||
enum gamestatus {
|
|
||||||
// clang-format off
|
|
||||||
STATUS_SUCCESS = 0, // 0; successful exit
|
|
||||||
STATUS_ERROR = 1, // miscellaneous error
|
|
||||||
ERROR_INIT = STATUS_ERROR | 2, // initialisation error
|
|
||||||
|
|
||||||
ERROR_STD = STATUS_ERROR | 64, // standard library error
|
|
||||||
ERROR_STD_INIT = ERROR_INIT | 64, // standard library initialisation error
|
|
||||||
ERROR_STD_MEMORY = ERROR_STD | 32, // memory error
|
|
||||||
ERROR_STD_MEMORY_INIT = ERROR_STD_INIT | 32, // memory initialization error
|
|
||||||
|
|
||||||
ERROR_SDL = STATUS_ERROR | 32, // SDL error
|
|
||||||
ERROR_SDL_INIT = ERROR_INIT | 32, // SDL initialization error
|
|
||||||
ERROR_SDL_RENDERING = ERROR_SDL | 16, // rendering error
|
|
||||||
ERROR_SDL_RENDERING_INIT = ERROR_SDL_INIT | 16, // rendering initialization error
|
|
||||||
ERROR_SDL_AUDIO = ERROR_SDL | 8, // audio error
|
|
||||||
ERROR_SDL_AUDIO_INIT = ERROR_SDL_INIT | 8, // audio initialization error
|
|
||||||
ERROR_SDL_FONT = ERROR_SDL | 4, // font error
|
|
||||||
ERROR_SDL_FONT_INIT = ERROR_SDL_INIT | 4, // font initialization error
|
|
||||||
|
|
||||||
STATUS_RUNNING = -1,
|
|
||||||
// clang-format on
|
|
||||||
};
|
|
||||||
|
|
||||||
#if __INCLUDE_LEVEL__ > 0
|
|
||||||
#include <SDL_messagebox.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "util/macro.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define debug(s, ...) printf("\033[95m" __FILE__ ":" MACRO_STR2(__LINE__) ": [DBG]: " s "\033[0m\n" __VA_OPT__(, __VA_ARGS__))
|
|
||||||
#define info(s, ...) printf(__FILE__ ":" MACRO_STR2(__LINE__) ": [INF]: " s "\n", __VA_OPT__(, __VA_ARGS__))
|
|
||||||
#define warn(s, ...) fprintf(stderr, "\033[93m" __FILE__ ":" MACRO_STR2(__LINE__) ": [WAR]: " s "\033[0m\n" __VA_OPT__(, __VA_ARGS__))
|
|
||||||
#define error(s, ...) fprintf(stderr, "\033[91m" __FILE__ ":" MACRO_STR2(__LINE__) ": [ERR]: " s "\033[0m\n" __VA_OPT__(, __VA_ARGS__))
|
|
||||||
|
|
||||||
#define fatal(c, s, ...) \
|
|
||||||
do { \
|
|
||||||
printf("\033[101m" __FILE__ ":" MACRO_STR2(__LINE__) ": [FAT]: " s "\033[0m\n" __VA_OPT__(, __VA_ARGS__)); \
|
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "something went wrong! :O", "view stderr for full details: \n" s, NULL); \
|
|
||||||
exit(c); \
|
|
||||||
} while (0)
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#include "game.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "../io/colour/colour8.h"
|
|
||||||
#include "../io/input.h"
|
|
||||||
#include "../io/window.h"
|
|
||||||
#include "../util/types.h"
|
|
||||||
#include "../util/vec.h"
|
|
||||||
#include "./tetromino/shapes.h"
|
|
||||||
#include "tetromino/placing.h"
|
|
||||||
#include "time.h"
|
|
||||||
|
|
||||||
static colour8 rowdat[COLUMNS * ROWS] = {0}; // contains the raw data of the rows, in no particular order
|
|
||||||
static struct gamedata dat = {0};
|
|
||||||
|
|
||||||
/* shuffle an array using the Fisher–Yates shuffle algorithm.
|
|
||||||
* `nmemb` is the number of members.
|
|
||||||
* `membs` is the byte size of each member */
|
|
||||||
static void shuffle(void *restrict ptr, size_t nmemb, size_t membs) {
|
|
||||||
u8 dat[membs];
|
|
||||||
|
|
||||||
for (size_t i = 0; i < nmemb; i++) {
|
|
||||||
size_t j = i + rand() % (nmemb - i);
|
|
||||||
void *ptri = (u8 *)ptr + i * membs;
|
|
||||||
void *ptrj = (u8 *)ptr + j * membs;
|
|
||||||
memcpy(dat, ptri, membs);
|
|
||||||
memcpy(ptri, ptrj, membs);
|
|
||||||
memcpy(ptrj, dat, membs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void next_shape(void) {
|
|
||||||
// as long as we're not at the last shape, we can just increment
|
|
||||||
dat.pdat.sel = (i8vec2){COLUMNS / 2 - SHAPE_WIDTH / 2, 0};
|
|
||||||
dat.pdat.cur = dat.pdat.nxt[dat.pdat.idx];
|
|
||||||
dat.pdat.idx++;
|
|
||||||
if (dat.pdat.idx < TETC) return;
|
|
||||||
|
|
||||||
// shuffle all next shapes, preserving the last
|
|
||||||
dat.pdat.idx = 0;
|
|
||||||
shuffle(dat.pdat.nxt, TETC, sizeof(u8));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct gamedata *game_init(void) {
|
|
||||||
srand(time(NULL));
|
|
||||||
|
|
||||||
// populate the data arrays
|
|
||||||
for (int i = 0; i < ROWS; i++)
|
|
||||||
dat.rows[i] = rowdat + i * COLUMNS;
|
|
||||||
for (int i = 0; i < TETC; i++)
|
|
||||||
dat.pdat.nxt[i] = i;
|
|
||||||
|
|
||||||
// initialise the placing data correctly
|
|
||||||
dat.pdat.sel = (i8vec2){COLUMNS / 2 - SHAPE_WIDTH / 2, 0};
|
|
||||||
shuffle(dat.pdat.nxt, TETC, sizeof(u8));
|
|
||||||
dat.pdat.cur = dat.pdat.nxt[dat.pdat.idx];
|
|
||||||
dat.pdat.idx++;
|
|
||||||
return &dat;
|
|
||||||
}
|
|
||||||
|
|
||||||
// called every time the game's state is updated
|
|
||||||
void game_update(int movdat, size_t time) {
|
|
||||||
static time_t drop_timeout = 0;
|
|
||||||
movdat |= MOVD & -!!time_poll(time, 200, &drop_timeout);
|
|
||||||
|
|
||||||
if (place_update(&dat, movdat))
|
|
||||||
window_close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void game_free(void) {
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "../io/colour/colour8.h"
|
|
||||||
#include "../util/types.h"
|
|
||||||
#include "../util/vec.h"
|
|
||||||
#include "tetromino/shapes.h"
|
|
||||||
|
|
||||||
// constants for pi(π) and tau(τ)
|
|
||||||
#define PI (M_PI) // π constant
|
|
||||||
#define TAU (M_PI * 2.0) // τ constant
|
|
||||||
#define PIf (M_PIf) // π constant as a 32-bit floating point
|
|
||||||
#define TAUf (M_PIf * 2.0F) // τ constant as a 32-bit floating point
|
|
||||||
|
|
||||||
// stores the data used in the game
|
|
||||||
#define COLUMNS 10
|
|
||||||
#define ROWS 24
|
|
||||||
|
|
||||||
/* contains the placement data */
|
|
||||||
struct pdat {
|
|
||||||
u8 nxt[TETC]; // shuffled data representing the next shapes
|
|
||||||
i8vec2 sel; // position of the current shape
|
|
||||||
u8 idx; // the index of the current shape
|
|
||||||
u8 cur; // the current id of the shape
|
|
||||||
};
|
|
||||||
|
|
||||||
/* contains game data that's commonly shared */
|
|
||||||
struct gamedata {
|
|
||||||
struct pdat pdat;
|
|
||||||
colour8 *rows[ROWS];
|
|
||||||
u16 pnts;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* increments to the next shape, shuffling the next shapes, if there isn't a next shape immediately after the current one. */
|
|
||||||
void next_shape(void);
|
|
||||||
|
|
||||||
struct gamedata *game_init(void);
|
|
||||||
void game_update(int movdat, size_t time);
|
|
||||||
void game_free(void);
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
#include "placing.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/cdefs.h>
|
|
||||||
|
|
||||||
#include "../../io/audio.h"
|
|
||||||
#include "../../io/input.h"
|
|
||||||
#include "../../util/types.h"
|
|
||||||
#include "../../util/vec.h"
|
|
||||||
#include "../game.h"
|
|
||||||
#include "shapes.h"
|
|
||||||
|
|
||||||
|
|
||||||
static int clear_rows(u8 *restrict *restrict rows) {
|
|
||||||
int count = 0;
|
|
||||||
u8 *cache[4]; /* the maximum amount of rows the user can clear at once is four */
|
|
||||||
|
|
||||||
for (int y = ROWS - 1; y >= 0; y--) {
|
|
||||||
int x = 0;
|
|
||||||
while (x < COLUMNS && rows[y][x] && count < 4) x++;
|
|
||||||
if (x >= COLUMNS) cache[count++] = rows[y];
|
|
||||||
else rows[y + count] = rows[y];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count) {
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
memset(cache[i], 0, COLUMNS);
|
|
||||||
rows[i] = cache[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* writes a shape to the screen */
|
|
||||||
static void plcmnt_place(u8 *restrict const *restrict row, u8 id, i8vec2 pos) {
|
|
||||||
u8 colour = colour_from_id(id);
|
|
||||||
|
|
||||||
i8vec2 bpos[4];
|
|
||||||
shape_getblocks(id, bpos);
|
|
||||||
bpos[0] += pos;
|
|
||||||
bpos[1] += pos;
|
|
||||||
bpos[2] += pos;
|
|
||||||
bpos[3] += pos;
|
|
||||||
|
|
||||||
row[bpos[0][VY]][bpos[0][VX]] = colour;
|
|
||||||
row[bpos[1][VY]][bpos[1][VX]] = colour;
|
|
||||||
row[bpos[2][VY]][bpos[2][VX]] = colour;
|
|
||||||
row[bpos[3][VY]][bpos[3][VX]] = colour;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int plcmnt_valid(u8 *restrict const *restrict const rows, i8vec2 pos) {
|
|
||||||
return pos[VX] >= 0 && pos[VX] < COLUMNS &&
|
|
||||||
pos[VY] >= 0 && pos[VY] < ROWS &&
|
|
||||||
!rows[pos[VY]][pos[VX]];
|
|
||||||
}
|
|
||||||
|
|
||||||
static int plcmnt_intersect(u8 *restrict const *restrict const rows, const u8 id, i8vec2 pos) {
|
|
||||||
i8vec2 bpos[4];
|
|
||||||
shape_getblocks(id, bpos);
|
|
||||||
return !(plcmnt_valid(rows, pos + bpos[0]) &&
|
|
||||||
plcmnt_valid(rows, pos + bpos[1]) &&
|
|
||||||
plcmnt_valid(rows, pos + bpos[2]) &&
|
|
||||||
plcmnt_valid(rows, pos + bpos[3]));
|
|
||||||
}
|
|
||||||
|
|
||||||
int place_update(struct gamedata *gdat, int movdat) {
|
|
||||||
// store the current index and ID, only changes when placed (which yields no movement) and rotation (which occurs last)
|
|
||||||
int tmp;
|
|
||||||
u8 id = gdat->pdat.cur;
|
|
||||||
|
|
||||||
// update Y axis
|
|
||||||
tmp = !!(movdat & MOVD);
|
|
||||||
gdat->pdat.sel[VY] += tmp;
|
|
||||||
tmp = tmp && plcmnt_intersect(gdat->rows, id, gdat->pdat.sel);
|
|
||||||
if (tmp) {
|
|
||||||
gdat->pdat.sel[VY]--;
|
|
||||||
plcmnt_place(gdat->rows, id, gdat->pdat.sel);
|
|
||||||
gdat->pnts += clear_rows(gdat->rows) << 4; // clear the rows that have been completed
|
|
||||||
next_shape();
|
|
||||||
audio_play(AUDIO_ID_PLACE);
|
|
||||||
|
|
||||||
if (plcmnt_intersect(gdat->rows, gdat->pdat.cur, gdat->pdat.sel))
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update X axis
|
|
||||||
tmp = !!(movdat & MOVR) - !!(movdat & MOVL);
|
|
||||||
gdat->pdat.sel[VX] += (tmp && !plcmnt_intersect(gdat->rows, id, (i8vec2){gdat->pdat.sel[VX] + tmp, gdat->pdat.sel[VY]})) * tmp;
|
|
||||||
|
|
||||||
// update roll
|
|
||||||
tmp = id ^ (((!!(movdat & MOVRR) - !!(movdat & MOVRL)) * 8 + id) & 31);
|
|
||||||
gdat->pdat.cur ^= (tmp && !plcmnt_intersect(gdat->rows, id ^ tmp, gdat->pdat.sel)) * tmp;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "../game.h"
|
|
||||||
|
|
||||||
/* updates the movement of the `pdat` structure, updating the rows when colliding downwards.
|
|
||||||
* returns `0` if we successfully updated. Returns 1 if we couldn't update. (e.g. when a next block immediately collides) */
|
|
||||||
int place_update(struct gamedata *gdat, int movdat);
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
#include "shapes.h"
|
|
||||||
|
|
||||||
#include "../../io/colour/colour8.h"
|
|
||||||
#include "../../util/types.h"
|
|
||||||
#include "../../util/vec.h"
|
|
||||||
#include "../../error.h"
|
|
||||||
|
|
||||||
void shape_getblocks(u8 id, i8vec2 *restrict out) {
|
|
||||||
struct blockdat {
|
|
||||||
u8 ax : 2, ay : 2;
|
|
||||||
u8 bx : 2, by : 2;
|
|
||||||
u8 cx : 2, cy : 2;
|
|
||||||
u8 dx : 2, dy : 2;
|
|
||||||
} dat;
|
|
||||||
|
|
||||||
switch (id) {
|
|
||||||
// O tetromino
|
|
||||||
case TET_O | TET_R0:
|
|
||||||
case TET_O | TET_R90:
|
|
||||||
case TET_O | TET_R180:
|
|
||||||
case TET_O | TET_R270: dat = (struct blockdat){1, 1, 2, 1, 1, 2, 2, 2}; break;
|
|
||||||
|
|
||||||
// I tetromino
|
|
||||||
case TET_I | TET_R0: dat = (struct blockdat){0, 1, 1, 1, 2, 1, 3, 1}; break;
|
|
||||||
case TET_I | TET_R90: dat = (struct blockdat){2, 0, 2, 1, 2, 2, 2, 3}; break;
|
|
||||||
case TET_I | TET_R180: dat = (struct blockdat){0, 2, 1, 2, 2, 2, 3, 2}; break;
|
|
||||||
case TET_I | TET_R270: dat = (struct blockdat){1, 0, 1, 1, 1, 2, 1, 3}; break;
|
|
||||||
|
|
||||||
// S tetromino
|
|
||||||
case TET_S | TET_R0: dat = (struct blockdat){1, 0, 2, 0, 0, 1, 1, 1}; break;
|
|
||||||
case TET_S | TET_R90: dat = (struct blockdat){1, 0, 1, 1, 2, 1, 2, 2}; break;
|
|
||||||
case TET_S | TET_R180: dat = (struct blockdat){1, 1, 2, 1, 0, 2, 1, 2}; break;
|
|
||||||
case TET_S | TET_R270: dat = (struct blockdat){0, 0, 0, 1, 1, 1, 1, 2}; break;
|
|
||||||
|
|
||||||
// Z tetromino
|
|
||||||
case TET_Z | TET_R0: dat = (struct blockdat){0, 0, 1, 0, 1, 1, 2, 1}; break;
|
|
||||||
case TET_Z | TET_R90: dat = (struct blockdat){2, 0, 1, 1, 2, 1, 1, 2}; break;
|
|
||||||
case TET_Z | TET_R180: dat = (struct blockdat){0, 1, 1, 1, 1, 2, 2, 2}; break;
|
|
||||||
case TET_Z | TET_R270: dat = (struct blockdat){1, 0, 0, 1, 1, 1, 0, 2}; break;
|
|
||||||
|
|
||||||
// T tetromino
|
|
||||||
case TET_T | TET_R0: dat = (struct blockdat){0, 1, 1, 1, 2, 1, 1, 2}; break;
|
|
||||||
case TET_T | TET_R90: dat = (struct blockdat){1, 0, 0, 1, 1, 1, 1, 2}; break;
|
|
||||||
case TET_T | TET_R180: dat = (struct blockdat){1, 0, 0, 1, 1, 1, 2, 1}; break;
|
|
||||||
case TET_T | TET_R270: dat = (struct blockdat){1, 0, 1, 1, 2, 1, 1, 2}; break;
|
|
||||||
|
|
||||||
// L tetromino
|
|
||||||
case TET_L | TET_R0: dat = (struct blockdat){1, 0, 1, 1, 1, 2, 2, 2}; break;
|
|
||||||
case TET_L | TET_R90: dat = (struct blockdat){0, 1, 1, 1, 2, 1, 0, 2}; break;
|
|
||||||
case TET_L | TET_R180: dat = (struct blockdat){0, 0, 1, 0, 1, 1, 1, 2}; break;
|
|
||||||
case TET_L | TET_R270: dat = (struct blockdat){2, 0, 0, 1, 1, 1, 2, 1}; break;
|
|
||||||
|
|
||||||
// J tetromino
|
|
||||||
case TET_J | TET_R0: dat = (struct blockdat){1, 0, 1, 1, 0, 2, 1, 2}; break;
|
|
||||||
case TET_J | TET_R90: dat = (struct blockdat){0, 0, 0, 1, 1, 1, 2, 1}; break;
|
|
||||||
case TET_J | TET_R180: dat = (struct blockdat){1, 0, 2, 0, 1, 1, 1, 2}; break;
|
|
||||||
case TET_J | TET_R270: dat = (struct blockdat){0, 1, 1, 1, 2, 1, 2, 2}; break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
#ifndef NDEBUG
|
|
||||||
fatal(1, "something went wrong; couldn't reconise the ID.", );
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
out[0] = (i8vec2){dat.ax, dat.ay};
|
|
||||||
out[1] = (i8vec2){dat.bx, dat.by};
|
|
||||||
out[2] = (i8vec2){dat.cx, dat.cy};
|
|
||||||
out[3] = (i8vec2){dat.dx, dat.dy};
|
|
||||||
}
|
|
||||||
|
|
||||||
colour8 colour_from_id(u8 id) {
|
|
||||||
switch (id & 7) {
|
|
||||||
case TET_O: return COLOUR8_YELLOW;
|
|
||||||
case TET_I: return COLOUR8_CYAN;
|
|
||||||
case TET_S: return COLOUR8_GREEN;
|
|
||||||
case TET_Z: return COLOUR8_RED;
|
|
||||||
case TET_T: return COLOUR8_MAGENTA;
|
|
||||||
case TET_L: return COLOUR8_ORANGE;
|
|
||||||
case TET_J: return COLOUR8_BLUE;
|
|
||||||
default: return COLOUR8_BLACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "../../io/colour/colour8.h"
|
|
||||||
#include "../../util/types.h"
|
|
||||||
#include "../../util/vec.h"
|
|
||||||
|
|
||||||
enum tetromino {
|
|
||||||
TET_O = 0,
|
|
||||||
TET_I = 1,
|
|
||||||
TET_S = 2,
|
|
||||||
TET_Z = 3,
|
|
||||||
TET_T = 4,
|
|
||||||
TET_L = 5,
|
|
||||||
TET_J = 6,
|
|
||||||
TET_R0 = 0,
|
|
||||||
TET_R90 = 8,
|
|
||||||
TET_R180 = 16,
|
|
||||||
TET_R270 = 24,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SHAPE_WIDTH 4
|
|
||||||
#define SHAPE_HEIGHT 4
|
|
||||||
#define TETC 7
|
|
||||||
|
|
||||||
void shape_getblocks(u8 id, i8vec2 *out);
|
|
||||||
colour8 colour_from_id(u8 id);
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#include "time.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#if __has_include(<features.h>)
|
|
||||||
#include <features.h>
|
|
||||||
#endif
|
|
||||||
#if __has_include(<features.h>) && _POSIX_C_SOURCE >= 199309L
|
|
||||||
#include <bits/time.h>
|
|
||||||
static void gettime(struct timespec *ts) {
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, ts);
|
|
||||||
}
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include <profileapi.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#include <winnt.h>
|
|
||||||
static void gettime(struct timespec *ts) {
|
|
||||||
LARGE_INTEGER cnt, frq;
|
|
||||||
QueryPerformanceCounter(&cnt);
|
|
||||||
QueryPerformanceFrequency(&frq);
|
|
||||||
ts->tv_sec = (time_t)(cnt.QuadPart / frq.QuadPart);
|
|
||||||
ts->tv_nsec = (time_t)((cnt.QuadPart % frq.QuadPart) * 1000000000 / frq.QuadPart);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#error no implementation of a monotonic clock was available
|
|
||||||
#endif
|
|
||||||
|
|
||||||
time_t time_pull(void) {
|
|
||||||
struct timespec ts;
|
|
||||||
gettime(&ts);
|
|
||||||
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
int time_poll(time_t curr, time_t delta, time_t *restrict proj) {
|
|
||||||
bool tpass = curr >= *proj;
|
|
||||||
*proj += tpass * ((curr + delta) - *proj); // adds 0, or the difference to proj
|
|
||||||
return tpass;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
/* gets the current time in milliseconds */
|
|
||||||
time_t time_pull(void);
|
|
||||||
|
|
||||||
/* Polls the time whether a given timeout has passed, comparing against `curr` as the current time.
|
|
||||||
* if `curr` ≥ `*proj`, `curr` + `delta` is written to `*proj`. `1` is returned.
|
|
||||||
* otherwise, we just return `0`. */
|
|
||||||
__nonnull((3)) int time_poll(time_t curr, time_t delta, time_t *restrict proj);
|
|
||||||
150
src/io/audio.c
150
src/io/audio.c
@@ -1,150 +0,0 @@
|
|||||||
#include "audio.h"
|
|
||||||
|
|
||||||
#include <SDL_audio.h>
|
|
||||||
#include <SDL_error.h>
|
|
||||||
#include <SDL_stdinc.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "../error.h"
|
|
||||||
#include "../util/compat.h"
|
|
||||||
#include "../util/types.h"
|
|
||||||
|
|
||||||
struct audioplayer {
|
|
||||||
const u8 *buf;
|
|
||||||
int len;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audiodevice {
|
|
||||||
struct audioplayer audio[AUDIO_MAX];
|
|
||||||
u32 id;
|
|
||||||
SDL_AudioSpec spec;
|
|
||||||
} dev;
|
|
||||||
|
|
||||||
struct audiodata audio_dat[AUDIO_ID_COUNT] = {0}; // contains pointers to audio buffers.
|
|
||||||
|
|
||||||
static const char *const audio_path[AUDIO_ID_COUNT] = {
|
|
||||||
"korobeiniki.wav",
|
|
||||||
"place.wav",
|
|
||||||
};
|
|
||||||
|
|
||||||
/* mixes the audio output stream, using the different audio as sources */
|
|
||||||
static void audiomixer(void *const userdata, u8 *const stream, const int len) {
|
|
||||||
(void)userdata;
|
|
||||||
memset(stream, 0, len); // clear the playing audio
|
|
||||||
|
|
||||||
// update the counts for the audio array
|
|
||||||
for (unsigned i = 0; i < AUDIO_MAX; i++) {
|
|
||||||
if (dev.audio[i].len > 0) {
|
|
||||||
unsigned mixlen = SDL_min(len, dev.audio[i].len);
|
|
||||||
SDL_MixAudioFormat(stream, dev.audio[i].buf, dev.spec.format, mixlen, SDL_MIX_MAXVOLUME);
|
|
||||||
dev.audio[i].len -= mixlen;
|
|
||||||
dev.audio[i].buf += mixlen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* converts input audio buffer to one that matches the buffer of dev.
|
|
||||||
* `len` is a pointer to the current size, the new size will be written to this location.
|
|
||||||
* returns the pointer to the audio buffer to use, or NULL, when something went wrong.
|
|
||||||
* NULL will never be returned after the conversion */
|
|
||||||
static u8 *audio_cvt(const SDL_AudioSpec *spec, u8 *bufptr, unsigned *len) {
|
|
||||||
if (!bufptr) return NULL;
|
|
||||||
|
|
||||||
// init the converter
|
|
||||||
SDL_AudioCVT cvt;
|
|
||||||
if (SDL_BuildAudioCVT(&cvt, spec->format, spec->channels, spec->freq, dev.spec.format, dev.spec.channels, dev.spec.freq) < 0) {
|
|
||||||
error("could not build the audio converter! SDL Error: %s", SDL_GetError());
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!cvt.needed) return bufptr; // ensure the conversion is necessary
|
|
||||||
|
|
||||||
// grow the inputted buffer for the conversion
|
|
||||||
cvt.len = *len;
|
|
||||||
cvt.buf = realloc(bufptr, cvt.len * cvt.len_mult);
|
|
||||||
if (!cvt.buf) {
|
|
||||||
warn("failed to grow the audio buffer to the new size of %u bytes! audio may be bugged", cvt.len);
|
|
||||||
return bufptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the audio to the new format
|
|
||||||
if (SDL_ConvertAudio(&cvt) < 0) {
|
|
||||||
warn("couldn't convert an audio buffer to the set format, audio may be bugged! SDL Error: %s", SDL_GetError());
|
|
||||||
assert(cvt.buf);
|
|
||||||
return cvt.buf;
|
|
||||||
}
|
|
||||||
assert(cvt.buf);
|
|
||||||
|
|
||||||
// shrink to reduce memory footprint
|
|
||||||
*len = cvt.len_cvt;
|
|
||||||
bufptr = realloc(cvt.buf, *len);
|
|
||||||
return bufptr ? bufptr : cvt.buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* computes the time in milliseconds of the audio fragment by dividing
|
|
||||||
* the audio byte length by the format's bit size, which is divided by
|
|
||||||
* the audio device's channels and frequency */
|
|
||||||
static inline u32 audio_btoms(u32 len) {
|
|
||||||
return (((1000 * len) / (SDL_AUDIO_BITSIZE(dev.spec.format) / 8)) / dev.spec.channels / dev.spec.freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loads a `struct audiodata` from `fpat` to `out`. */
|
|
||||||
static void audio_wav_load(const char *restrict fpat, struct audiodata *restrict out) {
|
|
||||||
debug("loading audio file '%s'...", fpat);
|
|
||||||
if (faccess(fpat, FA_R)) {
|
|
||||||
error("audio file either isn't readable or doesn't exist. path: '%s'!", fpat);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// load the audio
|
|
||||||
u32 len;
|
|
||||||
u8 *ptr, *tmp;
|
|
||||||
SDL_AudioSpec spec;
|
|
||||||
SDL_LoadWAV(fpat, &spec, &ptr, &len);
|
|
||||||
|
|
||||||
// convert the audio data to the format reflecting dev
|
|
||||||
tmp = audio_cvt(&spec, ptr, &len);
|
|
||||||
if (!tmp) free(ptr); // free the buffer if NULL was returned; failure
|
|
||||||
|
|
||||||
*out = (struct audiodata){tmp, len, audio_btoms(len)};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loads the audio data into the buffer */
|
|
||||||
static inline void audio_load(void) {
|
|
||||||
for (size_t i = 0; i < AUDIO_ID_COUNT; i++)
|
|
||||||
audio_wav_load(audio_path[i], &audio_dat[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_play(u32 audio_id) {
|
|
||||||
if (!dev.id) return;
|
|
||||||
size_t i = 0;
|
|
||||||
while (i < AUDIO_MAX && dev.audio[i].len > 0) i++;
|
|
||||||
if (i >= AUDIO_MAX) return;
|
|
||||||
|
|
||||||
dev.audio[i] = (struct audioplayer){
|
|
||||||
audio_dat[audio_id].buf,
|
|
||||||
audio_dat[audio_id].len,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
int audio_init(int freq, SDL_AudioFormat fmt, u8 ch, u16 samples) {
|
|
||||||
// initialise the audio device + specification
|
|
||||||
SDL_AudioSpec spec = {freq, fmt, ch, 0, samples, 0, 0, audiomixer, NULL};
|
|
||||||
unsigned id = SDL_OpenAudioDevice(NULL, 0, &spec, &spec, 0);
|
|
||||||
dev = (struct audiodevice){{0}, id, spec};
|
|
||||||
|
|
||||||
if (!id) return 1;
|
|
||||||
SDL_PauseAudioDevice(id, 0);
|
|
||||||
audio_load();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_free(void) {
|
|
||||||
SDL_CloseAudioDevice(dev.id);
|
|
||||||
dev = (struct audiodevice){0};
|
|
||||||
|
|
||||||
for (size_t i = 0; i < AUDIO_ID_COUNT; i++)
|
|
||||||
free((void *)audio_dat[i].buf);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <SDL_audio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "../util/types.h"
|
|
||||||
|
|
||||||
#define AUDIO_MAX 4 // maximum number of sound effects that are allowed to play at once
|
|
||||||
|
|
||||||
struct audiodata {
|
|
||||||
const u8 *buf; // pointer to the audio buffer
|
|
||||||
u32 len; // length in bytes of the audio buffer
|
|
||||||
u32 ms; // length in miliseconds of the audio buffer
|
|
||||||
};
|
|
||||||
|
|
||||||
enum audio_id {
|
|
||||||
AUDIO_ID_MUSIC,
|
|
||||||
AUDIO_ID_PLACE,
|
|
||||||
|
|
||||||
// leave at end, will contain count
|
|
||||||
AUDIO_ID_COUNT,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct audiodata audio_dat[AUDIO_ID_COUNT];
|
|
||||||
|
|
||||||
/* loads the audio to be played, unless `AUDIO_MAX` has been reached, then this call will fail */
|
|
||||||
void audio_play(u32);
|
|
||||||
|
|
||||||
/* initialises the audio device, then loads the audio data */
|
|
||||||
int audio_init(int freq, SDL_AudioFormat fmt, u8 ch, u16 samples);
|
|
||||||
|
|
||||||
/* frees up resources held by the audio device and audio buffers */
|
|
||||||
void audio_free(void);
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "SDL_render.h"
|
|
||||||
|
|
||||||
// stores colour in a rgba format, each channel being a 8 bits wide.
|
|
||||||
typedef union {
|
|
||||||
uint32_t packed;
|
|
||||||
struct {
|
|
||||||
uint8_t a;
|
|
||||||
uint8_t b;
|
|
||||||
uint8_t g;
|
|
||||||
uint8_t r;
|
|
||||||
};
|
|
||||||
} colour32;
|
|
||||||
|
|
||||||
#define COLOUR32_BLACK ((colour32){0x000000FF})
|
|
||||||
#define COLOUR32_RED ((colour32){0xFF0000FF})
|
|
||||||
#define COLOUR32_YELLOW ((colour32){0xFFFF00FF})
|
|
||||||
#define COLOUR32_ORANGE ((colour32){0xFF6D00FF})
|
|
||||||
#define COLOUR32_GREEN ((colour32){0x00FF00FF})
|
|
||||||
#define COLOUR32_CYAN ((colour32){0x00FFFFFF})
|
|
||||||
#define COLOUR32_BLUE ((colour32){0x0000FFFF})
|
|
||||||
#define COLOUR32_MAGENTA ((colour32){0xFF00FFFF})
|
|
||||||
#define COLOUR32_WHITE ((colour32){0xFFFFFFFF})
|
|
||||||
|
|
||||||
// sets the render colour to a colour32 value
|
|
||||||
static inline void set_colour32(SDL_Renderer *const renderer, const colour32 c) {
|
|
||||||
(void)SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// american macros:
|
|
||||||
#define color32 colour32
|
|
||||||
#define COLOR32_BLACK COLOUR32_BLACK
|
|
||||||
#define COLOR32_RED COLOUR32_RED
|
|
||||||
#define COLOR32_YELLOW COLOUR32_YELLOW
|
|
||||||
#define COLOR32_ORANGE COLOUR32_ORANGE
|
|
||||||
#define COLOR32_GREEN COLOUR32_GREEN
|
|
||||||
#define COLOR32_CYAN COLOUR32_CYAN
|
|
||||||
#define COLOR32_BLUE COLOUR32_BLUE
|
|
||||||
#define COLOR32_MAGENTA COLOUR32_MAGENTA
|
|
||||||
#define COLOR32_WHITE COLOUR32_WHITE
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "SDL_render.h"
|
|
||||||
|
|
||||||
// stores colour in a rrrgggbb format, which maps exactly to 8 bits
|
|
||||||
typedef uint8_t colour8;
|
|
||||||
|
|
||||||
/* rrrg ggbb */
|
|
||||||
#define COLOUR8_BLACK ((colour8)0x00) // 0000 0000
|
|
||||||
#define COLOUR8_RED ((colour8)0xE0) // 1110 0000
|
|
||||||
#define COLOUR8_YELLOW ((colour8)0xFC) // 1111 1100
|
|
||||||
#define COLOUR8_ORANGE ((colour8)0xEC) // 1111 1100
|
|
||||||
#define COLOUR8_GREEN ((colour8)0x1C) // 0001 1100
|
|
||||||
#define COLOUR8_CYAN ((colour8)0x1F) // 0001 1111
|
|
||||||
#define COLOUR8_BLUE ((colour8)0x03) // 0000 0011
|
|
||||||
#define COLOUR8_MAGENTA ((colour8)0xE3) // 1110 0011
|
|
||||||
#define COLOUR8_WHITE ((colour8)0xFF) // 1111 1111
|
|
||||||
|
|
||||||
// gets the red channel in 32 bit colour space
|
|
||||||
static inline uint8_t colour8_red32(const colour8 colour) {
|
|
||||||
return (colour >> 5) * (255 / 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
// gets the green channel in 32 bit colour space
|
|
||||||
static inline uint8_t colour8_green32(const colour8 colour) {
|
|
||||||
return ((colour >> 2) & 7) * (255 / 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
// gets the blue channel in 32 bit colour space
|
|
||||||
static inline uint8_t colour8_blue32(const colour8 colour) {
|
|
||||||
return (colour & 3) * (255 / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sets the render colour to a colour8 value
|
|
||||||
static inline void set_colour8(SDL_Renderer *const renderer, const colour8 c) {
|
|
||||||
(void)SDL_SetRenderDrawColor(renderer, colour8_red32(c), colour8_green32(c), colour8_blue32(c), 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
// american macros:
|
|
||||||
#define color8 colour8
|
|
||||||
#define color8_red32 colour8_red32
|
|
||||||
#define color8_green32 colour8_green32
|
|
||||||
#define color8_blue32 colour8_blue32
|
|
||||||
#define COLOR8_BLACK COLOUR8_BLACK
|
|
||||||
#define COLOR8_RED COLOUR8_RED
|
|
||||||
#define COLOR8_YELLOW COLOUR8_YELLOW
|
|
||||||
#define COLOR8_ORANGE COLOUR8_ORANGE
|
|
||||||
#define COLOR8_GREEN COLOUR8_GREEN
|
|
||||||
#define COLOR8_CYAN COLOUR8_CYAN
|
|
||||||
#define COLOR8_BLUE COLOUR8_BLUE
|
|
||||||
#define COLOR8_MAGENTA COLOUR8_MAGENTA
|
|
||||||
#define COLOR8_WHITE COLOUR8_WHITE
|
|
||||||
@@ -1,82 +1,8 @@
|
|||||||
#include "input.h"
|
#include <GLFW/glfw3.h>
|
||||||
|
void key_callback(GLFWwindow *win, int key, int scancode, int action, int mods) {
|
||||||
#include <SDL_events.h>
|
(void)win, (void)key, (void)scancode, (void)action, (void)mods;
|
||||||
#include <SDL_scancode.h>
|
#ifndef NDEBUG
|
||||||
#include <time.h>
|
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
||||||
|
glfwSetWindowShouldClose(win, 1);
|
||||||
#include "../game/time.h"
|
#endif
|
||||||
#include "../util/types.h"
|
|
||||||
#include "window.h"
|
|
||||||
|
|
||||||
/* processes an incoming scancode, returns the associated movement data, or performs the close action directly
|
|
||||||
* NOTE: if the action is mapped to multiple keys, pressing both and then releasing one, disables the action. Minor issue, won't fix. */
|
|
||||||
__attribute__((const)) static int procscancode(SDL_Scancode code) {
|
|
||||||
switch (code) {
|
|
||||||
case SDL_SCANCODE_Q:
|
|
||||||
return MOVRL;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_E:
|
|
||||||
return MOVRR;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_LEFT:
|
|
||||||
case SDL_SCANCODE_A:
|
|
||||||
case SDL_SCANCODE_H:
|
|
||||||
return MOVL;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_RIGHT:
|
|
||||||
case SDL_SCANCODE_D:
|
|
||||||
case SDL_SCANCODE_L:
|
|
||||||
return MOVR;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_DOWN:
|
|
||||||
case SDL_SCANCODE_S:
|
|
||||||
case SDL_SCANCODE_J:
|
|
||||||
case SDL_SCANCODE_SPACE:
|
|
||||||
return MOVD;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_ESCAPE:
|
|
||||||
window_close();
|
|
||||||
return 0;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int timeout_mask(time_t time) {
|
|
||||||
static time_t timeout = 0, timeout_roll = 0;
|
|
||||||
int msk = 0;
|
|
||||||
// only add to the mask if time_poll returns `1`, negating becomes `-1`; 0b1111...
|
|
||||||
// this is masked with the desired movement action.
|
|
||||||
msk |= ((MOVR | MOVL | MOVD) & -!!time_poll(time, 64, &timeout));
|
|
||||||
msk |= ((MOVRL | MOVRR) & -!!time_poll(time, 100, &timeout_roll));
|
|
||||||
return msk;
|
|
||||||
}
|
|
||||||
|
|
||||||
int input_getdat(time_t time) {
|
|
||||||
static u8 movdat = 0, nmovdat = 0, lmovdat = 0; // stores the static movement data
|
|
||||||
int mov = movdat, nmov = nmovdat, lmov = lmovdat; // stores the runtime movement data for easy register access
|
|
||||||
|
|
||||||
// process the events
|
|
||||||
SDL_Event e;
|
|
||||||
while (SDL_PollEvent(&e)) {
|
|
||||||
switch (e.type) {
|
|
||||||
case SDL_QUIT: window_close(); break;
|
|
||||||
case SDL_KEYDOWN: mov |= procscancode(e.key.keysym.scancode); break;
|
|
||||||
case SDL_KEYUP: nmov |= procscancode(e.key.keysym.scancode); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute the current movement
|
|
||||||
int mask = timeout_mask(time);
|
|
||||||
|
|
||||||
// handle releasing of keys
|
|
||||||
mov &= ~(nmov & lmov & mask); // only remove the keys that have been pressed since lmov
|
|
||||||
lmov = (mov & mask) | (lmov & ~mask); // set the value of lmov to the new value mov
|
|
||||||
nmov &= mov; // set nmov to only those in mov
|
|
||||||
int cmov = mov & mask;
|
|
||||||
|
|
||||||
// write to static variables (shrinking the values, and memory usage)
|
|
||||||
movdat = mov;
|
|
||||||
lmovdat = lmov;
|
|
||||||
nmovdat = nmov;
|
|
||||||
return cmov;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,5 @@
|
|||||||
#pragma once
|
#ifndef INPUT_H
|
||||||
|
#define INPUT_H 1
|
||||||
#include <time.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
void key_callback(GLFWwindow *win, int key, int scancode, int action, int mods);
|
||||||
/* 8 bit enumeration storing the movement data */
|
#endif
|
||||||
enum movdat {
|
|
||||||
MOVL = 1, // move left
|
|
||||||
MOVR = 2, // move right
|
|
||||||
MOVD = 4, // move down
|
|
||||||
MOVRL = 8, // move roll left
|
|
||||||
MOVRR = 16, // move roll right
|
|
||||||
MOVPL = 32, // move place
|
|
||||||
};
|
|
||||||
|
|
||||||
/* returns an OR'd string from `enum movdat`, containing the movement data.
|
|
||||||
* assumes that SDL has been initialized and a window has successfully been created. */
|
|
||||||
__attribute__((__pure__)) int input_getdat(time_t time);
|
|
||||||
|
|||||||
133
src/io/render.c
133
src/io/render.c
@@ -1,125 +1,30 @@
|
|||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
|
||||||
#include <SDL_error.h>
|
#include <glad/gl.h>
|
||||||
#include <SDL_pixels.h>
|
|
||||||
#include <SDL_rect.h>
|
|
||||||
#include <SDL_render.h>
|
|
||||||
#include <SDL_surface.h>
|
|
||||||
#include <SDL_ttf.h>
|
|
||||||
#include <SDL_video.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "../error.h"
|
#include <GLFW/glfw3.h>
|
||||||
#include "../game/game.h"
|
|
||||||
#include "../game/tetromino/shapes.h"
|
|
||||||
#include "../util/types.h"
|
|
||||||
#include "../util/vec.h"
|
|
||||||
#include "colour/colour32.h"
|
|
||||||
#include "colour/colour8.h"
|
|
||||||
|
|
||||||
#define COLOUR_SCORE COLOUR32_YELLOW
|
#include "shader.h"
|
||||||
|
#include "../util/error.h"
|
||||||
|
|
||||||
SDL_Renderer *rend = NULL;
|
GLuint pipe;
|
||||||
TTF_Font *font = NULL;
|
|
||||||
struct gamedata const *gdat = NULL;
|
|
||||||
|
|
||||||
static SDL_Surface *score_surface = NULL;
|
int render_init(void) {
|
||||||
static SDL_Texture *score_texture = NULL;
|
pipe = glCreateProgram();
|
||||||
|
shader_init(pipe);
|
||||||
|
glLinkProgram(pipe);
|
||||||
|
glValidateProgram(pipe);
|
||||||
|
|
||||||
static inline i32 colpos(uint column) {
|
int len;
|
||||||
return column * BLOCK_WIDTH + 1 + TET_PADDING;
|
glGetProgramiv(pipe, GL_INFO_LOG_LENGTH, &len);
|
||||||
}
|
if (len > 0) {
|
||||||
|
char log[len];
|
||||||
static inline i32 rowpos(uint row) {
|
glGetProgramInfoLog(pipe, len, &len, log);
|
||||||
return row * BLOCK_HEIGHT + 1 + TET_PADDING;
|
log[len - 1] = '\0'; // terminate the string one character sooner since the log includes a newline
|
||||||
}
|
fatal("error whilst linking the pipe: '%s'", log);
|
||||||
|
|
||||||
static void draw_score_text(void) {
|
|
||||||
static u16 prev_pts = 0xFFFF; // initialise to maximum, so the code below is triggered on first run
|
|
||||||
|
|
||||||
if (prev_pts ^ gdat->pnts) {
|
|
||||||
char score_text[6] = "0"; // max (base 10) digits of a u16 + null terminator
|
|
||||||
prev_pts = gdat->pnts;
|
|
||||||
if (gdat->pnts) sprintf(score_text, "%hu0", gdat->pnts);
|
|
||||||
|
|
||||||
SDL_FreeSurface(score_surface);
|
|
||||||
SDL_DestroyTexture(score_texture);
|
|
||||||
score_surface = TTF_RenderText_Solid(font, score_text, (SDL_Colour){COLOUR_SCORE.r, COLOUR_SCORE.g, COLOUR_SCORE.b, COLOUR_SCORE.a});
|
|
||||||
score_texture = SDL_CreateTextureFromSurface(rend, score_surface);
|
|
||||||
}
|
}
|
||||||
|
return 1;
|
||||||
assert(score_surface && score_texture);
|
|
||||||
SDL_Rect text_rect = {colpos(COLUMNS + 1), rowpos(0), score_surface->w, score_surface->h};
|
|
||||||
SDL_RenderCopy(rend, score_texture, NULL, &text_rect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int draw_block(SDL_Renderer *const renderer, i8vec2 pos) {
|
void render_update(GLFWwindow *win) {
|
||||||
const SDL_Rect block = {colpos(pos[VX]), rowpos(pos[VY]), BLOCK_WIDTH - 1, BLOCK_HEIGHT - 1};
|
|
||||||
return SDL_RenderFillRect(renderer, &block);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw_shape(const u8 id, i8vec2 pos) {
|
|
||||||
set_colour8(rend, colour_from_id(id));
|
|
||||||
i8vec2 bpos[4];
|
|
||||||
shape_getblocks(id, bpos);
|
|
||||||
draw_block(rend, pos + bpos[0]);
|
|
||||||
draw_block(rend, pos + bpos[1]);
|
|
||||||
draw_block(rend, pos + bpos[2]);
|
|
||||||
draw_block(rend, pos + bpos[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void render_level(void) {
|
|
||||||
for (int y = 0; y < ROWS; y++) {
|
|
||||||
const u8 *row = gdat->rows[y];
|
|
||||||
|
|
||||||
for (int x = 0; x < COLUMNS; x++) {
|
|
||||||
if (row[x] != 0) {
|
|
||||||
set_colour8(rend, row[x]);
|
|
||||||
draw_block(rend, (i8vec2){x, y});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void render_init(SDL_Window *win, struct gamedata const *game_data) {
|
|
||||||
rend = SDL_CreateRenderer(win, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
|
||||||
if (rend == NULL) fatal(ERROR_SDL_RENDERING_INIT, "Renderer failed to be created! SDL Error: %s", SDL_GetError());
|
|
||||||
|
|
||||||
font = TTF_OpenFont("pixeldroid_botic-regular.ttf", PX_DENS);
|
|
||||||
if (font == NULL) error("Failed to open font! TTF Error: %s", TTF_GetError());
|
|
||||||
|
|
||||||
gdat = game_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void render_update(void) {
|
|
||||||
set_colour32(rend, COLOUR32_BLACK);
|
|
||||||
SDL_RenderClear(rend);
|
|
||||||
set_colour32(rend, COLOUR32_WHITE);
|
|
||||||
|
|
||||||
static const SDL_Rect field_size = {TET_PADDING, TET_PADDING, TET_WIDTH + 1, TET_HEIGHT + 1};
|
|
||||||
SDL_RenderDrawRect(rend, &field_size);
|
|
||||||
|
|
||||||
if (font) draw_score_text();
|
|
||||||
|
|
||||||
render_level();
|
|
||||||
draw_shape(gdat->pdat.cur, gdat->pdat.sel);
|
|
||||||
draw_shape(gdat->pdat.nxt[gdat->pdat.idx], (i8vec2){COLUMNS + 1, 3});
|
|
||||||
|
|
||||||
SDL_RenderPresent(rend);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render_free(void) {
|
|
||||||
assert(rend);
|
|
||||||
SDL_DestroyRenderer(rend);
|
|
||||||
rend = NULL;
|
|
||||||
|
|
||||||
TTF_CloseFont(font);
|
|
||||||
SDL_FreeSurface(score_surface);
|
|
||||||
SDL_DestroyTexture(score_texture);
|
|
||||||
font = NULL;
|
|
||||||
score_surface = NULL;
|
|
||||||
score_texture = NULL;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,8 @@
|
|||||||
#pragma once
|
#ifndef RENDER_H
|
||||||
|
#define RENDER_H 1
|
||||||
|
#include <glad/gl.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
#include <SDL_video.h>
|
int render_init(void);
|
||||||
#include <stdint.h>
|
void render_update(GLFWwindow *win);
|
||||||
#include <sys/cdefs.h>
|
#endif
|
||||||
|
|
||||||
#include "../game/game.h"
|
|
||||||
|
|
||||||
#define PX_DENS 25 // pixel density; pixels per block
|
|
||||||
#define TET_PADDING 10 // padding around the tetris playing field
|
|
||||||
#define TET_WIDTH (COLUMNS * PX_DENS - TET_PADDING) // tetris playing field width
|
|
||||||
#define TET_HEIGHT (TET_WIDTH / COLUMNS * ROWS) // tetris playing field height
|
|
||||||
#define SCREEN_WIDTH ((COLUMNS + 6) * PX_DENS) // window width
|
|
||||||
#define SCREEN_HEIGHT ((COLUMNS) * PX_DENS / COLUMNS * ROWS) // window height
|
|
||||||
#define BLOCK_WIDTH (TET_WIDTH / COLUMNS) // width of a block
|
|
||||||
#define BLOCK_HEIGHT (TET_HEIGHT / ROWS) // height of a block
|
|
||||||
|
|
||||||
__nonnull((1, 2)) void render_init(SDL_Window *, struct gamedata const *);
|
|
||||||
void render_update(void); // causes a draw to occur, will also determine update rate
|
|
||||||
void render_free(void); // frees the memory allocated to the renderer in render_data
|
|
||||||
|
|||||||
50
src/io/shader.c
Normal file
50
src/io/shader.c
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include "shader.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../util/error.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* for compiling a shader */
|
||||||
|
static GLuint shader_compile(GLenum type, const char *src, size_t len) {
|
||||||
|
int ilen = len;
|
||||||
|
GLuint shader = glCreateShader(type);
|
||||||
|
glShaderSource(shader, 1, &src, &ilen);
|
||||||
|
glCompileShader(shader);
|
||||||
|
|
||||||
|
/* Repurposing ilen for the max length of the shader log */
|
||||||
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &ilen);
|
||||||
|
if (ilen > 0) {
|
||||||
|
char log[ilen];
|
||||||
|
glGetShaderInfoLog(shader, ilen, &ilen, log);
|
||||||
|
log[ilen - 1] = '\0'; // terminate the string one character sooner since the log includes a newline
|
||||||
|
error("error whilst compiling shader type '0X%X': '%s'", type, log);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NAME_START(name) _binary_res_##name##_start /* name of a start variable */
|
||||||
|
#define NAME_END(name) _binary_res_##name##_end /* name of an end variable */
|
||||||
|
|
||||||
|
/* wrapper for calling `shader_compile`, but with some more sane parameters. */
|
||||||
|
#define ADD_SHADER(_type, _name, _store) \
|
||||||
|
do { \
|
||||||
|
extern const char NAME_START(_name)[]; \
|
||||||
|
extern const char NAME_END(_name)[]; \
|
||||||
|
(_store) = shader_compile(_type, NAME_START(_name), (size_t)NAME_END(_name) - (size_t)NAME_START(_name)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
int shader_init(GLuint pipe) {
|
||||||
|
GLuint vs, fs;
|
||||||
|
ADD_SHADER(GL_VERTEX_SHADER, sh_vert_glsl, vs);
|
||||||
|
ADD_SHADER(GL_FRAGMENT_SHADER, sh_frag_glsl, fs);
|
||||||
|
|
||||||
|
glAttachShader(pipe, vs);
|
||||||
|
glAttachShader(pipe, fs);
|
||||||
|
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
5
src/io/shader.h
Normal file
5
src/io/shader.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#ifndef SHADER_H
|
||||||
|
#define SHADER_H 1
|
||||||
|
#include <glad/gl.h>
|
||||||
|
int shader_init(GLuint pipe);
|
||||||
|
#endif
|
||||||
@@ -1,60 +1,64 @@
|
|||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
|
||||||
#include <SDL.h>
|
|
||||||
#include <SDL_audio.h>
|
|
||||||
#include <SDL_error.h>
|
|
||||||
#include <SDL_video.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <time.h>
|
#include <glad/gl.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
#include "../error.h"
|
#include "../tetris.h"
|
||||||
#include "../game/game.h"
|
#include "../util/error.h"
|
||||||
#include "../game/time.h"
|
|
||||||
#include "SDL_ttf.h"
|
|
||||||
#include "audio.h"
|
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
|
||||||
static SDL_Window *win = NULL;
|
#define WIN_DPI 32
|
||||||
static bool close = false;
|
#define WIN_WIDTH (WIN_DPI * (TET_WIDTH + 3))
|
||||||
|
#define WIN_HEIGHT (WIN_DPI * TET_HEIGHT)
|
||||||
|
|
||||||
void window_init(struct gamedata const *gdat) {
|
static GLFWwindow *win = NULL;
|
||||||
assert(!win && !close);
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
|
|
||||||
fatal(ERROR_SDL_INIT, "SDL could not initialize! SDL Error: %s", SDL_GetError());
|
|
||||||
if (TTF_Init() < 0)
|
|
||||||
fatal(ERROR_SDL_INIT, "TTF could not initialize! TTF Error: %s", TTF_GetError());
|
|
||||||
|
|
||||||
win = SDL_CreateWindow("tetris clone", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
|
int window_init(void) {
|
||||||
if (win == NULL)
|
#ifndef NDEBUG
|
||||||
fatal(ERROR_SDL_RENDERING_INIT, "Window failed to be created! SDL Error: %s", SDL_GetError());
|
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
|
||||||
|
#endif
|
||||||
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||||
|
glfwWindowHint(GLFW_RED_BITS, 3);
|
||||||
|
glfwWindowHint(GLFW_GREEN_BITS, 3);
|
||||||
|
glfwWindowHint(GLFW_BLUE_BITS, 2);
|
||||||
|
glfwWindowHint(GLFW_ALPHA_BITS, 0);
|
||||||
|
win = glfwCreateWindow(WIN_WIDTH, WIN_HEIGHT, "Quinn's tetris", NULL, NULL);
|
||||||
|
if (!win) return 1;
|
||||||
|
|
||||||
render_init(win, gdat);
|
glfwMakeContextCurrent(win);
|
||||||
audio_init(32000, AUDIO_S16, 1, 4096);
|
if (!gladLoadGL(glfwGetProcAddress)) return 1;
|
||||||
|
glfwSwapInterval(1); /* Wait one screen update for redraw. A.k.a. "vsync" */
|
||||||
|
|
||||||
|
glfwSetKeyCallback(win, key_callback);
|
||||||
|
|
||||||
|
debug(
|
||||||
|
"version info:\n"
|
||||||
|
"\tvendor: %s\n"
|
||||||
|
"\trenderer: %s\n"
|
||||||
|
"\tversion: %s\n"
|
||||||
|
"\tshading lang: %s\n",
|
||||||
|
glGetString(GL_VENDOR),
|
||||||
|
glGetString(GL_RENDERER),
|
||||||
|
glGetString(GL_VERSION),
|
||||||
|
glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||||
|
|
||||||
|
return 0; //render_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void window_free(void) {
|
void window_loop(void) {
|
||||||
assert(win);
|
assert(win != NULL);
|
||||||
render_free();
|
while (!glfwWindowShouldClose(win)) {
|
||||||
SDL_DestroyWindow(win);
|
glfwWaitEventsTimeout(1.0 / 60.0);
|
||||||
win = NULL;
|
|
||||||
close = false;
|
|
||||||
|
|
||||||
audio_free();
|
// render_update(win);
|
||||||
}
|
glfwSwapBuffers(win);
|
||||||
|
|
||||||
void window_open(void) {
|
|
||||||
while (!close) {
|
|
||||||
size_t time = time_pull();
|
|
||||||
game_update(input_getdat(time), time);
|
|
||||||
render_update();
|
|
||||||
|
|
||||||
static time_t timeout = 0;
|
|
||||||
if (time_poll(time, audio_dat[AUDIO_ID_MUSIC].ms, &timeout))
|
|
||||||
audio_play(AUDIO_ID_MUSIC);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void window_close(void) {
|
glfwDestroyWindow(win);
|
||||||
close = true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
#pragma once
|
#ifndef WINDOW_H
|
||||||
|
#define WINDOW_H 1
|
||||||
#include "../game/game.h"
|
int window_init(void);
|
||||||
|
void window_loop(void);
|
||||||
#define PX_DENS 25 // pixel density; pixels per block
|
#endif
|
||||||
#define SCREEN_WIDTH ((COLUMNS + 6) * PX_DENS) // window width
|
|
||||||
#define SCREEN_HEIGHT ((COLUMNS) * PX_DENS / COLUMNS * ROWS) // window height
|
|
||||||
|
|
||||||
void window_init(struct gamedata const *);
|
|
||||||
void window_open(void);
|
|
||||||
void window_close(void);
|
|
||||||
void window_free(void);
|
|
||||||
|
|||||||
42
src/main.c
42
src/main.c
@@ -1,25 +1,33 @@
|
|||||||
#include <SDL.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "error.h"
|
|
||||||
#include "game/game.h"
|
|
||||||
#include "io/window.h"
|
#include "io/window.h"
|
||||||
|
#include "util/error.h"
|
||||||
|
|
||||||
static void stop(void) {
|
/* callback for GLFW errors */
|
||||||
debug("stopping...", );
|
static void error_callback(int err, const char *const msg) {
|
||||||
window_close();
|
error("glfw (%i); \"%s\"", err, msg);
|
||||||
window_free();
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// entry-point of the application
|
static int init(void) {
|
||||||
int main(int argc, char **argv) {
|
glfwSetErrorCallback(error_callback);
|
||||||
(void)argc, (void)argv;
|
glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); // disable joystick buttons
|
||||||
// register stop as exit function
|
|
||||||
atexit(stop);
|
|
||||||
|
|
||||||
window_init(game_init());
|
|
||||||
window_open();
|
|
||||||
|
|
||||||
|
if (!glfwInit()) return 1;
|
||||||
|
if (window_init()) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void quit(void) {
|
||||||
|
glfwTerminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
(void)argc, (void)argv;
|
||||||
|
printf("debug: [DBG], info: [INF], warning: [WAR], error: [ERR], fatal: [FAT]\n");
|
||||||
|
atexit(quit);
|
||||||
|
if (init()) fatal("failed to initialise the programme!");
|
||||||
|
window_loop();
|
||||||
|
quit();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|||||||
81
src/tetris.c
Normal file
81
src/tetris.c
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "tetris.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "util/intdef.h"
|
||||||
|
|
||||||
|
/* Board co-ordinates:
|
||||||
|
* 0 1 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
* 8 9 A B
|
||||||
|
* C D E F */
|
||||||
|
const u16 tetromino_shapes[7][4] = {
|
||||||
|
/* NONE CW_90 CW_180 CW_270 */
|
||||||
|
[TET_I] = {0x159D, 0x89AB, 0x26AE, 0x4567},
|
||||||
|
[TET_O] = {0x569A, 0x569A, 0x569A, 0x569A},
|
||||||
|
[TET_T] = {0x1456, 0x1569, 0x4569, 0x1459},
|
||||||
|
[TET_J] = {0x0456, 0x1259, 0x456A, 0x1589},
|
||||||
|
[TET_L] = {0x4536, 0x159A, 0x4568, 0x0159},
|
||||||
|
[TET_S] = {0x1245, 0x156A, 0x5689, 0x0459},
|
||||||
|
[TET_Z] = {0x0156, 0x2569, 0x459A, 0x1458},
|
||||||
|
};
|
||||||
|
|
||||||
|
void tetris_get_blocks(u8 tetromino, uint *restrict positions) {
|
||||||
|
uint shape = tetromino_shapes[tetromino & 7][tetromino >> 3];
|
||||||
|
uint *end = positions + 8;
|
||||||
|
while (positions < end) {
|
||||||
|
*positions += shape & 3;
|
||||||
|
shape >>= 2;
|
||||||
|
positions++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tetris_init_rows(const u8 *restrict data, const u8 *restrict *restrict out) {
|
||||||
|
const u8 *restrict *end = out + TET_HEIGHT;
|
||||||
|
while (out < end) {
|
||||||
|
*out = data;
|
||||||
|
data += TET_WIDTH;
|
||||||
|
out++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int tetris_clear_rows(u8 *restrict *restrict rows) {
|
||||||
|
int count = 0;
|
||||||
|
u8 *cache[4]; /* the maximum amount of rows the user can clear at once is four */
|
||||||
|
|
||||||
|
for (int y = 0; y < TET_HEIGHT; y++) {
|
||||||
|
int x = 0;
|
||||||
|
while (x < TET_WIDTH && rows[y][x] && count < 4) x++;
|
||||||
|
if (x >= TET_WIDTH) cache[count++] = rows[y];
|
||||||
|
else rows[y - count] = rows[y];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
memset(cache[i], 0, TET_WIDTH);
|
||||||
|
rows[TET_HEIGHT - 1 - i] = cache[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tetris_intersect(const u8 *restrict const *restrict rows, u8 tetromino, uint x, uint y) {
|
||||||
|
uint *pos = (uint[8]){x, y, x, y, x, y, x, y};
|
||||||
|
uint *end = pos + 8;
|
||||||
|
tetris_get_blocks(tetromino, pos);
|
||||||
|
for (; pos < end; pos += 2) {
|
||||||
|
if (*(pos + 0) >= TET_WIDTH) return 2;
|
||||||
|
if (*(pos + 1) >= TET_HEIGHT) return 2;
|
||||||
|
if (rows[*(pos + 1)][*pos]) break;
|
||||||
|
}
|
||||||
|
return pos < end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tetris_place(u8 *restrict *restrict rows, u8 tetromino, uint x, uint y) {
|
||||||
|
uint *pos = (uint[8]){x, y, x, y, x, y, x, y};
|
||||||
|
uint *end = pos + 8;
|
||||||
|
tetris_get_blocks(tetromino, pos);
|
||||||
|
tetromino &= 7; /* strip rotation data */
|
||||||
|
tetromino |= TET_NON_EMPTY;
|
||||||
|
for (; pos < end; pos += 2)
|
||||||
|
rows[*(pos + 1)][*pos] = tetromino;
|
||||||
|
}
|
||||||
59
src/tetris.h
Normal file
59
src/tetris.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef TETRIS_H
|
||||||
|
#define TETRIS_H 1
|
||||||
|
#include "util/intdef.h"
|
||||||
|
#include "util/atrb.h"
|
||||||
|
|
||||||
|
#define TET_WIDTH 10
|
||||||
|
#define TET_HEIGHT 24 /* height may be 16—24 */
|
||||||
|
|
||||||
|
/* Defines tetromino indices.
|
||||||
|
* The `TET_R*` definitions specify various rotations.
|
||||||
|
* This is designed to be OR'd with the tetromino shape index.
|
||||||
|
* Shape index can be obtained via `val & 7`, and rotation index is
|
||||||
|
* obtained via `val >> 3`. */
|
||||||
|
enum tetromino {
|
||||||
|
TET_I = 0x00,
|
||||||
|
TET_O = 0x01,
|
||||||
|
TET_T = 0x02,
|
||||||
|
TET_J = 0x03,
|
||||||
|
TET_L = 0x04,
|
||||||
|
TET_S = 0x05,
|
||||||
|
TET_Z = 0x06,
|
||||||
|
TET_R0 = 0x00,
|
||||||
|
TET_R90 = 0x08,
|
||||||
|
TET_R180 = 0x10,
|
||||||
|
TET_R270 = 0x18,
|
||||||
|
|
||||||
|
/* Used when storing blocks to the board;
|
||||||
|
* allows to identify non-empty indices from empty ones.
|
||||||
|
* This value should not be used during general computation. */
|
||||||
|
TET_NON_EMPTY = 0x80,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Stores the co-ordinates of the four blocks in a 4x4 plane.
|
||||||
|
* Each co-ordinate is 4 bits wide in total. (2 bits for each axis).
|
||||||
|
* This is done over encoding the shape directly within the 16 bits (4²),
|
||||||
|
* since this is more easily parsed in practice. */
|
||||||
|
extern const u16 tetromino_shapes[7][4];
|
||||||
|
|
||||||
|
/* Adds the block position data to the numbers in `positions`.
|
||||||
|
* it is assumed `positions` has at least eight members and is initialised. `(X,Y)*4 = 8` */
|
||||||
|
void tetris_get_blocks(u8 tetromino, uint *restrict positions) NONNULL((2));
|
||||||
|
|
||||||
|
/* Initialises the rows of the Tetris play field.
|
||||||
|
* `data` is assumed to point to data of at least `TET_WIDTH * TET_HEIGHT` members.
|
||||||
|
* `out` is assumed to point to data of at least `TET_HEIGHT` members; the row pointers shall be outputted here. */
|
||||||
|
void tetris_init_rows(const u8 *restrict data, const u8 *restrict *restrict out) NONNULL((1, 2));
|
||||||
|
|
||||||
|
/* Finds up to 4 filled rows in `rows`, moving succeeding rows down and adding the cleared row on top.
|
||||||
|
* It is assumed `rows[0]` is the bottom row and `rows[TET_WIDTH-1]` is the top row.
|
||||||
|
* Returns the amount of rows cleared. */
|
||||||
|
int tetris_clear_rows(u8 *restrict *restrict rows) NONNULL((1));
|
||||||
|
|
||||||
|
/* Checks if `tetromino` intersects at (`x`, `y`) in the data within `rows` or is out-of-bounds,
|
||||||
|
* returns `1` if it overlaps within `rows`, `2` if it is out-of-bounds, `0` if no intersection is found. */
|
||||||
|
int tetris_intersect(const u8 *restrict const *restrict rows, u8 tetromino, uint x, uint y) NONNULL((1));
|
||||||
|
|
||||||
|
/* Writes the blocks of `tetromino` to `rows` at position `x` and `y`. */
|
||||||
|
void tetris_place(u8 *restrict *restrict rows, u8 tetromino, uint x, uint y) NONNULL((1));
|
||||||
|
#endif
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#if defined __unix__
|
|
||||||
#include <features.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#elif defined _WIN32
|
|
||||||
#include <io.h>
|
|
||||||
#else
|
|
||||||
#error platform not supported!
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define PATH_SEP '\\' // contains the path separator as a character. Yes it is extremely annoying that this has to exist.
|
|
||||||
#define PATH_SEP_STR "\\" // contains the path separator as a string, useful for concatenation. Yes it is extremely annoying that this has to exist.
|
|
||||||
|
|
||||||
#define unixonly(_exec) // (no-op) executes inline code when __unix__ is defined, otherwise is no-op
|
|
||||||
#define winonly(_exec) _exec // executes inline code when _WIN32 is defined, otherwise is no-op
|
|
||||||
#else
|
|
||||||
#define PATH_SEP '/' // contains the path separator as a character. Yes it is extremely annoying that this has to exist.
|
|
||||||
#define PATH_SEP_STR "/" // contains the path separator as a string, useful for concatenation. Yes it is extremely annoying that this has to exist.
|
|
||||||
|
|
||||||
#define unixonly(_exec) _exec // executes inline code when __unix__ is defined, otherwise is no-op
|
|
||||||
#define winonly(_exec) // (no-op) executes inline code when _WIN32 is defined, otherwise is no-op
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// define the constants if they haven't been
|
|
||||||
#ifndef F_OK
|
|
||||||
#define F_OK 0
|
|
||||||
#endif
|
|
||||||
#ifndef X_OK
|
|
||||||
#define X_OK 1
|
|
||||||
#endif
|
|
||||||
#ifndef W_OK
|
|
||||||
#define W_OK 2
|
|
||||||
#endif
|
|
||||||
#ifndef R_OK
|
|
||||||
#define R_OK 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum faccess_perms {
|
|
||||||
FA_F = F_OK, // test for file's existence
|
|
||||||
FA_X = X_OK, // test for executing permission
|
|
||||||
FA_W = W_OK, // test for write permissions
|
|
||||||
FA_R = R_OK, // test for read permissions
|
|
||||||
};
|
|
||||||
|
|
||||||
/* tests a files access with F_OK, X_OK, R_OK, W_OK OR'd together
|
|
||||||
returns 0 upon success. -1 when errno is set and anything else when one or more of the permissions isn't set */
|
|
||||||
static inline int faccess(const char *restrict fname, int perms) {
|
|
||||||
#if defined __unix__ && _POSIX_C_SOURCE >= 200809L
|
|
||||||
return access(fname, perms);
|
|
||||||
#elif defined _WIN32
|
|
||||||
return _access(fname, perms);
|
|
||||||
#else
|
|
||||||
#error platform unsupported!
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
51
src/util/error.c
Normal file
51
src/util/error.c
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "intdef.h"
|
||||||
|
|
||||||
|
static void error_log(FILE *restrict stream, const char *restrict pfx, uint ln, const char *restrict file, const char *restrict fmt, va_list ap) {
|
||||||
|
fprintf(stream, "(%s:%u) [%s] '", file, ln, pfx);
|
||||||
|
|
||||||
|
vfprintf(stream, fmt, ap);
|
||||||
|
|
||||||
|
fprintf(stream, "'\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_dbg(uint ln, const char *restrict file, const char *restrict fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
error_log(stdout, "\033[95mDBG\033[0m", ln, file, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_inf(uint ln, const char *restrict file, const char *restrict fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
error_log(stdout, "\033[93mINF\033[0m", ln, file, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_war(uint ln, const char *restrict file, const char *restrict fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
error_log(stdout, "\033[91mWAR\033[0m", ln, file, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_err(uint ln, const char *restrict file, const char *restrict fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
error_log(stdout, "\033[mFAT\033[0m", ln, file, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_fat(uint ln, const char *restrict file, const char *restrict fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
error_log(stdout, "\033[mFAT\033[0m", ln, file, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
22
src/util/error.h
Normal file
22
src/util/error.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
/* Copyright (c) 2025 Quinn
|
||||||
|
* Licensed under the MIT Licence. See LICENSE for details */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "atrb.h"
|
||||||
|
#include "intdef.h"
|
||||||
|
|
||||||
|
void error_dbg(uint ln, const char *restrict file, const char *restrict fmt, ...);
|
||||||
|
void error_inf(uint ln, const char *restrict file, const char *restrict fmt, ...);
|
||||||
|
void error_war(uint ln, const char *restrict file, const char *restrict fmt, ...);
|
||||||
|
void error_err(uint ln, const char *restrict file, const char *restrict fmt, ...);
|
||||||
|
void error_fat(uint ln, const char *restrict file, const char *restrict fmt, ...) NORET;
|
||||||
|
|
||||||
|
#define debug(s, ...) error_dbg(__LINE__, __FILE__, s, ##__VA_ARGS__)
|
||||||
|
#define info(s, ...) error_inf(__LINE__, __FILE__, s, ##__VA_ARGS__)
|
||||||
|
#define warn(s, ...) error_war(__LINE__, __FILE__, s, ##__VA_ARGS__)
|
||||||
|
#define error(s, ...) error_err(__LINE__, __FILE__, s, ##__VA_ARGS__)
|
||||||
|
#define fatal(s, ...) error_fat(__LINE__, __FILE__, s, ##__VA_ARGS__)
|
||||||
Reference in New Issue
Block a user