Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
Darks | 242439c05b | |
Darks | bd16f89b30 | |
Darks | 48a1c0a121 |
2
Makefile
|
@ -145,7 +145,7 @@ build-cg/assets/img/%.o: assets-cg/img/%
|
|||
@ mkdir -p $(dir $@)
|
||||
fxconv --image $< -o $@ $(FXCONVCG) name:img_$(notdir $(basename $*)) $(IMG.$*) profile:p4
|
||||
|
||||
# Images
|
||||
# Images libimg
|
||||
build-fx/assets/limg/%.o: assets-fx/limg/%
|
||||
@ mkdir -p $(dir $@)
|
||||
fxconv --libimg-image $< -o $@ $(FXCONVFX) name:limg_$(notdir $(basename $*)) $(IMG.$*)
|
||||
|
|
|
@ -20,7 +20,7 @@ Speeds are in px/s (play area is 270×224).
|
|||
| 1 | Marisa Kirisame | 208 | 130 | Attract bonus when on top of the screen |
|
||||
| 2 | Sariel | 241 | | Do not attract bullets when not focused |
|
||||
| 3 | Koakuma | | | +15% chance of getting a powerup |
|
||||
| 4 | Iku Nagae | | | Start with 4 bombs instead of 3 |
|
||||
| 4 | Iku Nagae | | | Start with 4 spells instead of 3 |
|
||||
| 5 | Sumireko Usami | | | Wider fire angle when not focused |
|
||||
| 6 | Hina Kagiyama | | | +10% power when not focused |
|
||||
| 7 | Kurumi | | | |
|
||||
|
@ -53,3 +53,7 @@ There are 2 kinds of powerups: small ones and big ones. Small ones increase powe
|
|||
### Object updater
|
||||
|
||||
An object updater is a function applied to an object. Its protoype must follow this: `void f(object_t*)`. Due to the complexity of the game, it may use `frame`, `us_elapsed`; but it could also use `player`, `difficulty` or other global variables.
|
||||
|
||||
## Graphics
|
||||
|
||||
Graphics are designed for color calcs. However, I try to be the most responsible possible to facilitate a port to monochrome calcs. Thus, code may be quite verbose when displaying sprites.
|
||||
|
|
BIN
Touhou.g3a
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1 @@
|
|||
hina
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef _ANIMATION_H
|
||||
#define _ANIMATION_H
|
||||
|
||||
// Objects
|
||||
typedef struct {
|
||||
uint16_t x, y, r; // Position and radius
|
||||
struct {
|
||||
uint8_t enabled :1; // Object enabled (visible, updated)
|
||||
uint8_t col_nor :1; // Collisions in normal mode
|
||||
uint8_t col_foc :1; // Collisions in focused mode
|
||||
uint8_t color :4; // Color. 3 bits used, maybe 4 in the future
|
||||
uint8_t type :8; // Type of object. Linked to image_t
|
||||
} meta;
|
||||
} object_t;
|
||||
|
||||
// Animations
|
||||
typedef struct animation_s animation_t;
|
||||
typedef struct {
|
||||
uint32_t timestamp; // Timestamp of the animation
|
||||
uint32_t n_objects; // Number of objects
|
||||
object_t *objects; // Table of object_t
|
||||
void (*updater)(animation_t*, uint32_t); // Updater function
|
||||
} animation_s;
|
||||
|
||||
// Utils
|
||||
animation_t *new_animation(uint32_t objects, void (*updater)(animation_t*));
|
||||
void del_animation(animation_t *animation);
|
||||
|
||||
// List of animation functions
|
||||
void dummy_updater(animation_t *animation, uint32_t us_elapsed);
|
||||
|
||||
#endif // _ANIMATION_H
|
|
@ -1,6 +1,14 @@
|
|||
#ifndef _CONFIG_H
|
||||
#define _CONFIG_H
|
||||
|
||||
// #define DWIDTH 396
|
||||
// #define DHEIGHT 224
|
||||
|
||||
#define SCREEN_X 270
|
||||
#define SCREEN_Y 224
|
||||
|
||||
#define NULL (void*)(0)
|
||||
|
||||
enum {
|
||||
WITCH_1 = 0,
|
||||
WITCH_2,
|
||||
|
@ -13,4 +21,12 @@ enum {
|
|||
N_WITCHES
|
||||
};
|
||||
|
||||
// Profiling
|
||||
enum {
|
||||
PROFCTX_FPS = 0,
|
||||
PROFCTX_ENGINE,
|
||||
PROFCTX_DISPLAY,
|
||||
PROFCTX_COUNT
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef _DIALOG_H
|
||||
#define _DIALOG_H
|
||||
|
||||
/* Some requirements for dialogs */
|
||||
|
||||
#include <gint/display.h>
|
||||
#include <gint/keyboard.h>
|
||||
#include <libimg.h>
|
||||
#include <libprof.h>
|
||||
#include <openlibm.h>
|
||||
|
||||
/* Define some structures used for dialogs */
|
||||
typedef struct {
|
||||
img_t *characters[2];
|
||||
char **text;
|
||||
} dialog_t;
|
||||
|
||||
void dialog(dialog_t *dialog);
|
||||
void display_text(const char *src, int chars);
|
||||
|
||||
#endif // _DIALOG_H
|
|
@ -1,9 +1,10 @@
|
|||
#ifndef _ENGINE_H
|
||||
#define _ENGINE_H
|
||||
|
||||
struct player_s {
|
||||
char witch_id;
|
||||
#include <stdint.h>
|
||||
|
||||
};
|
||||
void engine(void);
|
||||
void physics(void);
|
||||
void graphics(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef _PLAYER_H
|
||||
#define _PLAYER_H
|
||||
|
||||
#include "utils/vector.h"
|
||||
|
||||
typedef struct {
|
||||
float x, y;
|
||||
char power;
|
||||
char lives;
|
||||
char bombs;
|
||||
} player_t;
|
||||
|
||||
#endif
|
|
@ -60,8 +60,8 @@ INCLUDE := -I include
|
|||
# Libraries. Add one -l option for each library you are using, and also
|
||||
# suitable -L options if you have library files in custom folders. To use
|
||||
# fxlib, add libfx.a to the project directory and use "-L . -lfx".
|
||||
LIBS_FX := -lprof -limg-fx
|
||||
LIBS_CG := -lprof -limg-cg
|
||||
LIBS_FX := -lprof -limg-fx -lopenlibm
|
||||
LIBS_CG := -lprof -limg-cg -lopenlibm
|
||||
|
||||
# Base linker flags for the fxSDK, you usually want to keep these.
|
||||
LDFLAGS_FX := -T fx9860g.ld -lgint-fx $(LIBS_FX) -lgint-fx -lgcc
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
#include "dialog.h"
|
||||
|
||||
// Debug purposes
|
||||
int _strlen(const char *str) { int i = 0; while (str[i++]); return i; }
|
||||
|
||||
void dialog(dialog_t *dialog)
|
||||
{
|
||||
// Init profiler
|
||||
prof_t prof_fps = prof_make();
|
||||
uint32_t time = 0;
|
||||
uint32_t us_elapsed = 1;
|
||||
|
||||
// Init key event
|
||||
key_event_t e;
|
||||
|
||||
// Init characters metadata (who's talking, etc.)
|
||||
int talking = 0;
|
||||
|
||||
// Init dialog characters pictures
|
||||
img_t char_flipped = img_hflip_create(*(dialog->characters[1]));
|
||||
img_t chars_img[2][2] = {
|
||||
{ img_copy(*(dialog->characters[0])),
|
||||
img_darken_create(*(dialog->characters[0])) },
|
||||
{ char_flipped,
|
||||
img_darken_create(char_flipped)}
|
||||
};
|
||||
|
||||
// Init text properties
|
||||
int current_replica = 0;
|
||||
float current_chars = 0, text_speed = 0.000012; // Chars per us
|
||||
char wait_change = 0;
|
||||
|
||||
// Entering main loop
|
||||
prof_enter(prof_fps);
|
||||
|
||||
while(dialog->text[current_replica])
|
||||
{
|
||||
// Get last keyboard event
|
||||
clearevents();
|
||||
|
||||
// Manage events
|
||||
if(keydown(KEY_EXIT)) break;
|
||||
if(keydown(KEY_SHIFT) && wait_change == 1)
|
||||
{
|
||||
// Reset current chars
|
||||
current_chars = 0;
|
||||
// Waiting for key up
|
||||
wait_change = 2;
|
||||
}
|
||||
if(!keydown(KEY_SHIFT) && wait_change == 2)
|
||||
{
|
||||
// Change who's speaking
|
||||
talking = !talking;
|
||||
// Update current replica
|
||||
current_replica++;
|
||||
// We're not waiting anymore
|
||||
wait_change = 0;
|
||||
}
|
||||
|
||||
// Manage states
|
||||
if(keydown(KEY_SHIFT))
|
||||
{
|
||||
text_speed = 0.000028;
|
||||
} else {
|
||||
text_speed = 0.000012;
|
||||
}
|
||||
|
||||
// Move text
|
||||
if(!wait_change) current_chars += us_elapsed * text_speed;
|
||||
if(current_chars > _strlen(dialog->text[current_replica]))
|
||||
{
|
||||
current_chars = _strlen(dialog->text[current_replica]);
|
||||
// This may be dirty (mixing engine and inputs) but who cares?
|
||||
if(!keydown(KEY_SHIFT)) wait_change = 1;
|
||||
}
|
||||
|
||||
// Clear vram before drawing
|
||||
dclear(C_BLACK);
|
||||
|
||||
// Display everything
|
||||
img_render_vram(chars_img[0][talking],
|
||||
DWIDTH * 0.15,
|
||||
DHEIGHT * (0.15 + 0.04 * fcos(time * 0.000005)));
|
||||
img_render_vram(chars_img[1][!talking],
|
||||
DWIDTH * 0.85 - chars_img[1][0].width,
|
||||
DHEIGHT * (0.15 + 0.04 * fsin(time * 0.000005)));
|
||||
|
||||
display_text(dialog->text[current_replica], (int)current_chars);
|
||||
|
||||
dprint_opt(DWIDTH-1, DHEIGHT-1, C_WHITE, C_NONE, DTEXT_RIGHT,
|
||||
DTEXT_BOTTOM, "%i FPS", 1000000 / us_elapsed);
|
||||
|
||||
// Update VRAM
|
||||
dupdate();
|
||||
|
||||
// Get elapsed time, reset profiler and start it again
|
||||
prof_leave(prof_fps);
|
||||
us_elapsed = prof_time(prof_fps);
|
||||
prof_fps.elapsed = 0;
|
||||
prof_enter(prof_fps);
|
||||
|
||||
// Update global time
|
||||
time += us_elapsed;
|
||||
}
|
||||
|
||||
/* Destroy temporary images */
|
||||
for(int i = 0; i < 2; i++)
|
||||
img_destroy(chars_img[i][i]);
|
||||
}
|
||||
|
||||
|
||||
// TODO: optimize this naive algorithm. Soft breaks are not implemented
|
||||
void display_text(const char *src, int chars)
|
||||
{
|
||||
// If no chars, nothing to do here
|
||||
if(chars == 0) return;
|
||||
|
||||
// Init font
|
||||
const font_t *current_font = dfont(NULL);
|
||||
|
||||
// Init dialog text buffers
|
||||
static char lines[4][50]; // TODO: do not hardcode these sizes
|
||||
|
||||
// Init width, height for line size
|
||||
int w, h;
|
||||
|
||||
// Init line and position on line
|
||||
int l = 0, p = 0;
|
||||
|
||||
// For each char to be displayed
|
||||
for(int i = 0; i < chars; i++)
|
||||
{
|
||||
// Copy char into a line
|
||||
lines[l][p] = src[i];
|
||||
lines[l][p+1] = '\0';
|
||||
|
||||
// Get line size
|
||||
dsize(lines[l], current_font, &w, &h);
|
||||
|
||||
// If it's too long, move to a new line
|
||||
if(w > 0.7 * DWIDTH)
|
||||
{
|
||||
lines[l][p] = '\0';
|
||||
l++; p = 0;
|
||||
lines[l][p] = src[i];
|
||||
lines[l][p+1] = '\0';
|
||||
}
|
||||
|
||||
// Move to next char on the line
|
||||
p++;
|
||||
}
|
||||
|
||||
// Draw all lines
|
||||
for(int i = 0; i < l+1; i++)
|
||||
{
|
||||
dtext(0.15 * DWIDTH,
|
||||
0.6 * DHEIGHT + (current_font->line_height + 4) * i,
|
||||
C_WHITE, lines[i]);
|
||||
}
|
||||
}
|
111
src/engine.c
|
@ -1,3 +1,114 @@
|
|||
#include <gint/display.h>
|
||||
#include <gint/keyboard.h>
|
||||
#include <libprof.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "engine.h"
|
||||
|
||||
// Speed in px/us
|
||||
#define SPEED_FAST 0.000208
|
||||
#define SPEED_SLOW 0.000104
|
||||
|
||||
|
||||
extern image_t *img_witches[8];
|
||||
extern image_t *img_battlefields[3];
|
||||
|
||||
|
||||
float speeds[2] = {SPEED_FAST, SPEED_SLOW};
|
||||
|
||||
void engine(void)
|
||||
{
|
||||
uint32_t frame = 0;
|
||||
uint32_t us_elapsed = 1;
|
||||
|
||||
// Profiling
|
||||
prof_t prof_fps = prof_make();
|
||||
prof_t prof_engine = prof_make();
|
||||
prof_t prof_display = prof_make();
|
||||
|
||||
char witch = 0;
|
||||
char mode = 0; // 0 = normal, 1 = focused
|
||||
float px = SCREEN_X / 2;
|
||||
float py = SCREEN_Y / 2;
|
||||
|
||||
char battlefield = 0;
|
||||
|
||||
while(!keydown(KEY_EXIT))
|
||||
{
|
||||
// Profiling
|
||||
prof_enter(prof_fps);
|
||||
|
||||
// Get keyboard events
|
||||
clearevents();
|
||||
|
||||
|
||||
//*** Some debug inputs ***********************************************
|
||||
if(keydown(KEY_F1)) battlefield = 0;
|
||||
if(keydown(KEY_F2)) battlefield = 1;
|
||||
if(keydown(KEY_F3)) battlefield = 2;
|
||||
if(keydown(KEY_1)) witch = 0;
|
||||
if(keydown(KEY_2)) witch = 1;
|
||||
if(keydown(KEY_3)) witch = 2;
|
||||
if(keydown(KEY_4)) witch = 3;
|
||||
//*** End debug *******************************************************
|
||||
|
||||
prof_enter(prof_engine);
|
||||
// Fast or slow witch
|
||||
mode = keydown(KEY_OPTN);
|
||||
float base_speed = us_elapsed * speeds[mode] * (
|
||||
keydown_any(KEY_LEFT, KEY_RIGHT, 0) &&
|
||||
keydown_any(KEY_UP, KEY_DOWN, 0) ? 0.707 : 1.0);
|
||||
|
||||
// Move witch
|
||||
if(keydown(KEY_UP))
|
||||
py -= base_speed;
|
||||
if(keydown(KEY_DOWN))
|
||||
py += base_speed;
|
||||
if(keydown(KEY_LEFT))
|
||||
px -= base_speed;
|
||||
if(keydown(KEY_RIGHT))
|
||||
px += base_speed;
|
||||
|
||||
prof_leave(prof_engine);
|
||||
|
||||
|
||||
prof_enter(prof_display);
|
||||
// Clear VRAM
|
||||
dclear(C_BLACK);
|
||||
|
||||
// Draw battlefield
|
||||
dimage(0, 0, img_battlefields[battlefield]);
|
||||
|
||||
// Draw our cute witch
|
||||
image_t *w = img_witches[witch * 2 + mode];
|
||||
dimage(px - w->width / 2, py - w->height / 2, w);
|
||||
|
||||
prof_leave(prof_display);
|
||||
|
||||
// Print FPS
|
||||
dprint(273, 184, C_WHITE, "%i FPS", 1000000 / us_elapsed);
|
||||
dprint(273, 194, C_WHITE, "Tot: %i us", us_elapsed);
|
||||
dprint(273, 204, C_WHITE, "Eng: %i us", prof_time(prof_engine));
|
||||
dprint(273, 214, C_WHITE, "Dsp: %i us", prof_time(prof_display));
|
||||
|
||||
// Update VRAM
|
||||
dupdate();
|
||||
|
||||
// Update FPS & frame
|
||||
prof_leave(prof_fps);
|
||||
us_elapsed = prof_time(prof_fps);
|
||||
prof_fps.elapsed = 0;
|
||||
frame++;
|
||||
}
|
||||
}
|
||||
|
||||
void physics(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void graphics(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#include <gint/display.h>
|
||||
|
||||
// TODO: generate those tables in `fxconv` instead of this dirty file
|
||||
|
||||
extern image_t img_witch1_back;
|
||||
extern image_t img_witch2_back;
|
||||
extern image_t img_witch3_back;
|
||||
extern image_t img_witch4_back;
|
||||
extern image_t img_witch5_back;
|
||||
extern image_t img_witch6_back;
|
||||
extern image_t img_witch7_back;
|
||||
extern image_t img_witch8_back;
|
||||
image_t *img_witches[8] = {
|
||||
&img_witch1_back, &img_witch2_back, &img_witch3_back, &img_witch4_back,
|
||||
&img_witch5_back, &img_witch6_back, &img_witch7_back, &img_witch8_back
|
||||
};
|
||||
|
||||
|
||||
extern image_t img_battlefield1;
|
||||
extern image_t img_battlefield2;
|
||||
extern image_t img_battlefield3;
|
||||
image_t *img_battlefields[3] = {
|
||||
&img_battlefield1, &img_battlefield2, &img_battlefield3
|
||||
};
|
||||
|
||||
|
||||
extern image_t img_bullet1_1;
|
||||
extern image_t img_bullet1_2;
|
||||
extern image_t img_bullet1_3;
|
||||
extern image_t img_bullet2_1;
|
||||
extern image_t img_bullet2_2;
|
||||
extern image_t img_bullet2_3;
|
||||
extern image_t img_bullet3_1;
|
||||
extern image_t img_bullet3_2;
|
||||
extern image_t img_bullet3_3;
|
||||
image_t *img_bullets[3][3] = {
|
||||
{&img_bullet1_1, &img_bullet1_2, &img_bullet1_3},
|
||||
{&img_bullet2_1, &img_bullet2_2, &img_bullet2_3},
|
||||
{&img_bullet3_1, &img_bullet3_2, &img_bullet3_3},
|
||||
};
|
56
src/main.c
|
@ -2,41 +2,39 @@
|
|||
#include <gint/keyboard.h>
|
||||
#include <libprof.h>
|
||||
|
||||
void draw() {
|
||||
extern image_t img_battlefield1;
|
||||
extern image_t img_battlefield2;
|
||||
extern image_t img_battlefield3;
|
||||
extern image_t img_bullet3_2;
|
||||
#include "config.h"
|
||||
#include "engine.h"
|
||||
|
||||
prof_clear(0);
|
||||
prof_enter(0);
|
||||
#include "dialog.h"
|
||||
|
||||
dclear(C_BLACK);
|
||||
// dimage(0, 0, &img_battlefield1);
|
||||
// dimage(0, 0, &img_battlefield2);
|
||||
dimage(0, 0, &img_battlefield3);
|
||||
|
||||
for(int i = 0; i < 2000; i++) {
|
||||
dimage((i*79) % 216, (i*41) % 216, &img_bullet3_2);
|
||||
}
|
||||
|
||||
prof_leave(0);
|
||||
|
||||
dprint(300, 10, C_NONE, C_NONE, "%i us", prof_time(0));
|
||||
dprint(300, 40, C_WHITE, C_NONE, "%i us", prof_time(0));
|
||||
dprint(300, 70, C_BLACK, C_NONE, "%i us", prof_time(0));
|
||||
dupdate();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
prof_init(1, 0);
|
||||
{
|
||||
extern img_t limg_hina, limg_sariel;
|
||||
|
||||
for(int i = 0; i < 100; i++) {
|
||||
draw();
|
||||
}
|
||||
prof_init();
|
||||
|
||||
getkey();
|
||||
char *text[] = {
|
||||
"Hey Sariel! How are you?",
|
||||
"I was fine until an unpleasant witche entered my garden.",
|
||||
"You're not pleased to see me tonight?",
|
||||
"Should I recall you stole my scepter yesterday?",
|
||||
"It was a joke...",
|
||||
"But I did not enjoy it!",
|
||||
"You're not fun, Sariel.",
|
||||
"Are you kidding me?",
|
||||
"Hey, if you're so sensitive, go home!",
|
||||
"You've gone too far. I'll take you down!",
|
||||
"Ok, let's have a fight then. But I won't do you any favours.",
|
||||
NULL
|
||||
};
|
||||
|
||||
dialog_t d = {
|
||||
{&limg_hina, &limg_sariel},
|
||||
text
|
||||
};
|
||||
|
||||
dialog(&d);
|
||||
|
||||
prof_quit();
|
||||
|
||||
|
|