Compare commits

...

7 Commits

Author SHA1 Message Date
Xavier Del Campo Romero 9da37c198e camera: implement fixed cursor movement
On platforms with PERIPHERAL_TYPE_PAD, navigating through menus and
options can be cumbersome if moving the cursor freely around the
screen.

Therefore, this commit instead defines a list of (X, Y) coordinates that
the cursor can jump to. The implementation also attempts to guess which
direction the cursor should jump to for the previous/next point, and
hence determine which button should be pressed by the user.
2022-06-24 17:45:50 +02:00
Xavier Del Campo Romero 8ddea5eef5 camera.c: refactor cursor_init
So that uninitialized members are set to 0.
2022-06-24 17:45:14 +02:00
Xavier Del Campo Romero 992e7fb935 peripheral: provide common actions
Whereas some actions are context-specific (e.g.: selecting a player),
some are context-independent and can be executed for all screens
(e.g.: exiting the game).
2022-06-24 17:28:38 +02:00
Xavier Del Campo Romero 5ac01ff845 README.md: add copyright notice 2022-06-24 17:20:05 +02:00
Xavier Del Campo Romero d140dcd278 README.md: update according to current status 2022-06-24 17:19:27 +02:00
Xavier Del Campo Romero c831272f29 cmake/ps1.cmake: use add_library for libpsx.a 2022-06-19 03:51:36 +02:00
Xavier Del Campo Romero 7c1795401b Deprecate <TARGET>_BUILD in favor of CMAKE_TOOLCHAIN_FILE 2022-06-19 03:09:28 +02:00
12 changed files with 185 additions and 78 deletions

View File

@ -1,22 +1,5 @@
cmake_minimum_required(VERSION 3.13)
option(PS1_BUILD "Build for the original Sony Playstation" OFF)
option(HOST_BUILD "Build for host platform using SDL-1.2" OFF)
option(WIN9X_BUILD "Build for Win9x using SDL-1.2" OFF)
if(NOT PS1_BUILD AND NOT HOST_BUILD AND NOT WIN9X_BUILD)
message(STATUS "Assuming native build. "
"Please use one of the available options in CMakeLists.txt to "
"cross-compile for a specific target.")
set(HOST_BUILD ON)
endif()
if(PS1_BUILD)
include("cmake/ps1-toolchain.cmake")
elseif(WIN9X_BUILD)
include("cmake/win9x-toolchain.cmake")
endif()
set(TOOLS_PREFIX ${CMAKE_BINARY_DIR}/tools)
include(ExternalProject)
ExternalProject_Add(tools
@ -26,6 +9,14 @@ ExternalProject_Add(tools
project(rts)
if(CMAKE_TOOLCHAIN_FILE MATCHES "ps1")
set(PS1_BUILD 1)
elseif(CMAKE_TOOLCHAIN_FILE MATCHES "win9x")
set(WIN9X_BUILD 1)
else()
set(HOST_BUILD 1)
endif()
add_executable(${PROJECT_NAME} "src/main.c")
set(cdroot ${CMAKE_BINARY_DIR}/cdimg)
# Avoid C11 since it is not supported by the i386-mingw32 toolchain.

View File

@ -14,11 +14,15 @@ in the likes of several other entries from the mid 90's.
The following platforms are either supported or support is expected in
the future:
- Sony PlayStation 1, using a forked version of
- Sony® PlayStation® 1, using a forked version of
[PSXSDK](https://git.disroot.org/xavi92/psxsdk).
- Microsoft Win9x, using a `i386-mingw32` cross-toolchain and SDL-1.2.
- Modern Unix-like operating systems such as GNU/Linux or *BSD, using
SDL-1.2 (if available). Possibly modern Microsoft Windows versions, too.
- Microsoft® Win9x, using a `i386-mingw32` cross-toolchain and SDL-1.2.
A `i386-mingw32` cross-toolchain must be already available on the
system.
- POSIX-compliant operating systems such as GNU/Linux® or *BSD, using
SDL-1.2 (if available).
- Possibly, modern Microsoft® Windows® versions, too (currently
untested).
## Design goals
@ -29,11 +33,15 @@ between platforms without modifications.
- Use modern CMake features for a simplified build process and
availability of the `compile_commands.json` database.
- Implement any multiplayer capabilities provided by the platform.
- Support a wide range of screen resolutions, even portrait resolutions
typically used by mobile platforms.
- And, above all, provide a fun game that can run even on low-end
hardware.
## Building from source
### Native build
A native version of **RTS** can be built using the typical CMake build
process:
@ -41,21 +49,25 @@ process:
mkdir build
cd build
cmake ..
make -j$(nproc --all)
```
The following options can be otherwise defined:
### Cross-compilation
- `PS1_BUILD`: builds for Sony Playstation 1
- `HOST_BUILD`: builds for the host operating system using SDL-1.2
- `WIN9X_BUILD`: builds for Microsoft Win9x operating systems using
SDL-1.2. A `i386-mingw32` cross-toolchain must be available.
[`CMAKE_TOOLCHAIN_FILE`](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html)
can be used to set up the cross-toolchain. Files labeled as
`cmake/*-toolchain.cmake` can be used as values.
For example, the Sony PlayStation 1 version can be built using:
#### Sony® PlayStation® 1
For example, the Sony® PlayStation® 1 version can be built using:
```sh
mkdir build
cd build
cmake .. -DPS1_BUILD=1 -DVIDEO_MODE=VMODE_PAL
cmake .. \
-DCMAKE_TOOLCHAIN_FILE=../cmake/ps1-toolchain.cmake \
-DVIDEO_MODE=VMODE_PAL # VMODE_NTSC can be otherwise used
make -j$(nproc --all)
```
@ -63,6 +75,18 @@ This will generate a `.bin`/`.cue` file pair in `build` that can be
played on an emulator or burnt into a CD-r in order to play the game
on real hardware.
#### Microsoft® Win9x
```sh
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/win9x-toolchain.cmake
make -j$(nproc --all)
```
A stripped version of the executable, as well as game assets, will be
located in `build/cdimg`.
## License
Unless stated otherwise, **RTS** follows the license described by the
@ -76,3 +100,8 @@ Derivative works have been also created from these files for this
project, that are located inside the `res` directory. A `LICENSE`
file is also provided to describe the relationship between the
original and derived works.
## Copyright notice
Microsoft®, Linux®, Sony® and PlayStation® are registered trademarks of
their respective owners.

View File

@ -3,10 +3,14 @@ if("$ENV{PSXSDK_PATH}" STREQUAL "")
endif()
file(MAKE_DIRECTORY ${cdroot})
target_link_directories(${PROJECT_NAME} PUBLIC $ENV{PSXSDK_PATH}/lib)
target_link_libraries(${PROJECT_NAME} PUBLIC -lpsx -lfixmath)
add_library(psx STATIC IMPORTED)
set_property(TARGET psx PROPERTY IMPORTED_LOCATION $ENV{PSXSDK_PATH}/lib/libpsx.a)
target_include_directories(psx INTERFACE
$ENV{PSXSDK_PATH}/include)
target_link_libraries(${PROJECT_NAME} PUBLIC psx fixmath)
target_compile_definitions(${PROJECT_NAME} PUBLIC FIXMATH_FAST_SIN PSXSDK_DEBUG)
target_include_directories(${PROJECT_NAME} PRIVATE . $ENV{PSXSDK_PATH}/include)
add_custom_target(exe ALL elf2exe ${PROJECT_NAME}
${cdroot}/${PROJECT_NAME}.exe -mark="A homebrew game created with PSXSDK"
DEPENDS ${PROJECT_NAME})
@ -15,8 +19,6 @@ add_custom_target(iso ALL mkisofs -o ${PROJECT_NAME}.iso -V ${PROJECT_NAME}
set(license $ENV{PSXSDK_PATH}/share/licenses/infoeur.dat)
add_custom_target(bin_cue ALL mkpsxiso ${PROJECT_NAME}.iso ${PROJECT_NAME}.bin
${license} -s DEPENDS iso)
# add_custom_target(libpsx ALL DEPENDS $ENV{PSXSDK_PATH}/lib/libpsx.a)
# add_dependencies(${PROJECT_NAME} libpsx)
if(NOT EXISTS "${cdroot}/system.cnf")
file(COPY "src/system.cnf" DESTINATION "${cdroot}")

View File

@ -35,6 +35,16 @@ struct camera
{
int last_w, last_h;
} screen;
struct cursor_pos_rt
{
const struct cursor_pos
{
unsigned x, y;
} *list;
size_t i, n;
} rt;
} cursor;
};
@ -45,6 +55,7 @@ bool camera_translate(const struct camera *cam, const struct util_rect *dim, sho
void cursor_init(struct cursor *c);
bool cursor_collision(const struct camera *cam, const struct util_rect *d);
void cursor_pos(const struct camera *cam, unsigned long *x, unsigned long *y);
void cursor_set_pos_list(struct cursor *c, const struct cursor_pos *pos, size_t n);
int cursor_render(const struct cursor *c);
#ifdef __cplusplus

View File

@ -63,10 +63,21 @@ int cursor_render(const struct cursor *const c)
void cursor_init(struct cursor *const c)
{
c->x = c->x_init = (screen_w / 2) - CAMERA_CURSOR_WIDTH;
c->y = c->y_init = (screen_h / 2) - CAMERA_CURSOR_HEIGHT;
c->screen.last_w = screen_w;
c->screen.last_h = screen_h;
const unsigned int x = (screen_w / 2) - CAMERA_CURSOR_WIDTH,
y = (screen_h / 2) - CAMERA_CURSOR_HEIGHT;
*c = (const struct cursor)
{
.x = x,
.x_init = x,
.y = y,
.y_init = y,
.screen =
{
.last_w = screen_w,
.last_h = screen_h
}
};
}
void camera_update_pos(struct camera *const cam)
@ -103,6 +114,16 @@ bool camera_translate(const struct camera *const cam, const struct util_rect *co
return true;
}
void cursor_set_pos_list(struct cursor *const c,
const struct cursor_pos *const pos, const size_t n)
{
c->rt = (const struct cursor_pos_rt)
{
.list = pos,
.n = n
};
}
void camera_update(struct camera *const cam, const union peripheral *const p)
{
switch (p->common.type)

View File

@ -5,6 +5,7 @@
#include <gfx.h>
#include <util.h>
#include <stdbool.h>
#include <stdlib.h>
static void cursor_update(struct camera *const cam, const struct pad *const p)
{
@ -99,9 +100,61 @@ static void update_speed(struct camera *const cam, const struct pad *const p)
cam->y_speed = 0;
}
static enum pad_key get_ref_key(struct cursor_pos_rt *const rt,
const size_t ref)
{
enum pad_key key;
const struct cursor_pos *const cur = &rt->list[rt->i],
*const next = &rt->list[ref];
const short nx = next->x - cur->x,
ny = next->y - cur->y;
if (abs(nx) > abs(ny))
key = nx > 0 ? PAD_KEY_RIGHT : PAD_KEY_LEFT;
else
key = ny > 0 ? PAD_KEY_DOWN : PAD_KEY_UP;
return key;
}
static void cursor_update_fixed(struct camera *const cam,
const struct pad *const p)
{
struct cursor *const c = &cam->cursor;
struct cursor_pos_rt *const rt = &c->rt;
const size_t next = rt->i + 1;
if (next < rt->n)
{
const enum pad_key key = get_ref_key(rt, next);
if (pad_justpressed(p, key))
rt->i = next;
}
else if (rt->i)
{
const size_t prev = rt->i - 1;
const enum pad_key key = get_ref_key(rt, prev);
if (pad_justpressed(p, key))
rt->i = prev;
}
const struct cursor_pos *const cur = &rt->list[rt->i];
c->x = cur->x;
c->y = cur->y;
}
void camera_update_pad(struct camera *const cam, const struct pad *const p)
{
cursor_update(cam, p);
update_speed(cam, p);
camera_update_pos(cam);
if (cam->cursor.rt.list)
cursor_update_fixed(cam, p);
else
{
cursor_update(cam, p);
update_speed(cam, p);
camera_update_pos(cam);
}
}

View File

@ -88,7 +88,8 @@ int game(void)
.n_res = sizeof res / sizeof *res
};
exit |= human_player_update(&humans[i], &o);
human_player_update(&humans[i], &o);
exit |= humans[i].periph.common.exit;
terrain_update(&map);
if (terrain_render(&map, &h->cam)

View File

@ -1,3 +1,3 @@
add_library(peripheral "src/peripheral.c")
target_include_directories(peripheral PUBLIC "inc")
target_link_libraries(peripheral PUBLIC pad mouse keyboard util)
target_link_libraries(peripheral PUBLIC pad mouse keyboard util PRIVATE gfx)

View File

@ -25,6 +25,7 @@ union peripheral
struct peripheral_common
{
enum peripheral_type type;
bool exit;
} common;
struct peripheral_pad

View File

@ -1,8 +1,26 @@
#include <peripheral.h>
#include <gfx.h>
#include <keyboard.h>
#include <mouse.h>
#include <pad.h>
static void update_pad_common(union peripheral *const p)
{
if (pad_justpressed(&p->pad.pad, PAD_KEY_EXIT))
p->common.exit = true;
}
static void update_kbm_common(union peripheral *const p)
{
struct peripheral_kbm *const kbm = &p->kbm;
struct keyboard *const k = &kbm->keyboard;
if (keyboard_justpressed(k, &KEYBOARD_COMBO(KEYBOARD_KEY_EXIT)))
p->common.exit = true;
else if (keyboard_justreleased(k, &KEYBOARD_COMBO(KEYBOARD_KEY_F11)))
gfx_toggle_fullscreen();
}
void peripheral_update(union peripheral *const p)
{
switch (p->common.type)
@ -12,10 +30,12 @@ void peripheral_update(union peripheral *const p)
case PERIPHERAL_TYPE_KEYBOARD_MOUSE:
mouse_update(&p->kbm.mouse);
keyboard_update(&p->kbm.keyboard);
update_kbm_common(p);
break;
case PERIPHERAL_TYPE_PAD:
pad_update(&p->pad.pad);
update_pad_common(p);
break;
}
}
@ -39,3 +59,8 @@ void peripheral_init(const struct peripheral_cfg *const cfg,
break;
}
}
int peripheral_get_default(struct peripheral_cfg *const cfg)
{
return -1;
}

View File

@ -64,7 +64,7 @@ struct human_player
};
int human_player_init(const struct human_player_cfg *cfg, struct human_player *h);
bool human_player_update(struct human_player *h, struct player_others *o);
void human_player_update(struct human_player *h, struct player_others *o);
int human_player_render(const struct human_player *h, const struct player_others *o);
#ifdef __cplusplus

View File

@ -524,16 +524,12 @@ static void update_target(struct human_player *const h)
}
}
static bool update_from_pad(struct human_player *const h,
static void update_from_pad(struct human_player *const h,
struct player_others *const o)
{
bool ret = false;
struct pad *const p = &h->periph.pad.pad;
if (pad_justpressed(p, PAD_KEY_OPTIONS)
|| pad_justpressed(p, PAD_KEY_EXIT))
ret = true;
else if (pad_justpressed(p, PAD_KEY_A))
if (pad_justpressed(p, PAD_KEY_A))
select_instances(h, o, false, false);
else if (pad_justpressed(p, PAD_KEY_B))
move_units(h, o);
@ -541,28 +537,12 @@ static bool update_from_pad(struct human_player *const h,
deselect_instances(h);
else if (pad_justpressed(p, PAD_KEY_E))
h->top_gui ^= true;
return ret;
}
static bool update_keyboard_mouse_common(const struct mouse *const m,
const struct keyboard *const k)
{
bool ret = false;
if (keyboard_justpressed(k, &KEYBOARD_COMBO(KEYBOARD_KEY_EXIT)))
ret = true;
else if (keyboard_justreleased(k, &KEYBOARD_COMBO(KEYBOARD_KEY_F11)))
gfx_toggle_fullscreen();
return ret;
}
static bool update_from_touch(struct human_player *const h,
static void update_from_touch(struct human_player *const h,
struct player_others *const o)
{
struct mouse *const m = &h->periph.kbm.mouse;
struct keyboard *const k = &h->periph.kbm.keyboard;
struct peripheral_kbm *const kbm = &h->periph.kbm;
bool *const pan = &h->cam.pan;
@ -587,11 +567,9 @@ static bool update_from_touch(struct human_player *const h,
kbm->long_press = false;
kbm->lp_t = 0;
}
return update_keyboard_mouse_common(m, k);
}
static bool update_from_keyboard_mouse(struct human_player *const h,
static void update_from_keyboard_mouse(struct human_player *const h,
struct player_others *const o)
{
struct mouse *const m = &h->periph.kbm.mouse;
@ -608,14 +586,11 @@ static bool update_from_keyboard_mouse(struct human_player *const h,
}
else if (mouse_justreleased(m, MOUSE_BUTTON_RIGHT))
move_units(h, o);
return update_keyboard_mouse_common(m, k);
}
bool human_player_update(struct human_player *const h,
void human_player_update(struct human_player *const h,
struct player_others *const o)
{
bool ret = false;
struct player *const p = &h->pl;
if (p->alive)
@ -628,23 +603,21 @@ bool human_player_update(struct human_player *const h,
switch (h->periph.common.type)
{
case PERIPHERAL_TYPE_PAD:
ret = update_from_pad(h, o);
update_from_pad(h, o);
break;
case PERIPHERAL_TYPE_TOUCH:
ret = update_from_touch(h, o);
update_from_touch(h, o);
break;
case PERIPHERAL_TYPE_KEYBOARD_MOUSE:
ret = update_from_keyboard_mouse(h, o);
update_from_keyboard_mouse(h, o);
break;
}
camera_update(&h->cam, &h->periph);
player_update(p);
}
return ret;
}
static int render_target(const struct human_player *const h)