Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c62cfc950 | ||
|
|
c1632590c6 | ||
|
|
c7ef3c0047 | ||
|
|
7a5a4a1276 | ||
|
|
6e7d8a8399 | ||
|
|
d24b0b5619 | ||
|
|
369c680cb7 | ||
|
|
da16e6a486 | ||
|
|
9a4aaca399 | ||
|
|
4d26f13c26 |
@@ -46,6 +46,7 @@ GIT_C_FLAGS := -DGIT_HASH=\"$(GIT_HASH)\" -DGIT_DIRTY=\"$(GIT_DIRTY)\"
|
|||||||
CFLAGS := -g -O3 -Wall -Werror -std=gnu23 \
|
CFLAGS := -g -O3 -Wall -Werror -std=gnu23 \
|
||||||
-mcpu=arm7tdmi -mtune=arm7tdmi \
|
-mcpu=arm7tdmi -mtune=arm7tdmi \
|
||||||
-ffast-math -fomit-frame-pointer -funroll-loops \
|
-ffast-math -fomit-frame-pointer -funroll-loops \
|
||||||
|
-fms-extensions \
|
||||||
$(ARCH)
|
$(ARCH)
|
||||||
|
|
||||||
CFLAGS += $(GIT_C_FLAGS)
|
CFLAGS += $(GIT_C_FLAGS)
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
Fusion Pixel Font
|
||||||
|
https://github.com/TakWolf/fusion-pixel-font
|
||||||
|
|
||||||
|
Copyright (c) 2022, TakWolf (https://takwolf.com).
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://openfontlicense.org
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created using
|
||||||
|
the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1 @@
|
|||||||
|
-gB8 -mR8 -mLs -mRtf -pn 64
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -2,6 +2,7 @@
|
|||||||
* @file button.h
|
* @file button.h
|
||||||
*
|
*
|
||||||
* @brief A button structure containing the common button functionalities
|
* @brief A button structure containing the common button functionalities
|
||||||
|
* Including highlight and pressing functionalities.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef BUTTON_H
|
#ifndef BUTTON_H
|
||||||
|
|||||||
+6
-3
@@ -2,12 +2,12 @@
|
|||||||
#define CARD_H
|
#define CARD_H
|
||||||
|
|
||||||
#include "deck_types.h"
|
#include "deck_types.h"
|
||||||
#include "sprite.h"
|
#include "item.h"
|
||||||
|
|
||||||
#include <maxmod.h>
|
#include <maxmod.h>
|
||||||
#include <tonc.h>
|
#include <tonc.h>
|
||||||
|
|
||||||
#define MAX_CARDS (NUM_SUITS * NUM_RANKS)
|
#define MAX_CARDS 128 // Arbitrary high value so we don't have issues
|
||||||
#define MAX_CARDS_ON_SCREEN 16
|
#define MAX_CARDS_ON_SCREEN 16
|
||||||
|
|
||||||
#define CARD_TID 0
|
#define CARD_TID 0
|
||||||
@@ -54,8 +54,8 @@ typedef struct Card
|
|||||||
|
|
||||||
typedef struct CardObject
|
typedef struct CardObject
|
||||||
{
|
{
|
||||||
|
Item; // First member struct inheritance
|
||||||
Card* card;
|
Card* card;
|
||||||
SpriteObject* sprite_object;
|
|
||||||
bool selected;
|
bool selected;
|
||||||
} CardObject;
|
} CardObject;
|
||||||
|
|
||||||
@@ -83,4 +83,7 @@ void card_object_set_selected(CardObject* card_object, bool selected);
|
|||||||
bool card_object_is_selected(CardObject* card_object);
|
bool card_object_is_selected(CardObject* card_object);
|
||||||
Sprite* card_object_get_sprite(CardObject* card_object);
|
Sprite* card_object_get_sprite(CardObject* card_object);
|
||||||
|
|
||||||
|
int card_object_get_buy_price(Item* card_object);
|
||||||
|
void card_object_dispose(Item** card_object);
|
||||||
|
|
||||||
#endif // CARD_H
|
#endif // CARD_H
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @file chinese_text.h
|
||||||
|
* @brief UTF-8 conversion helpers for the extended TTE font.
|
||||||
|
*/
|
||||||
|
#ifndef CHINESE_TEXT_H
|
||||||
|
#define CHINESE_TEXT_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Map supported UTF-8 Chinese text to valid UTF-8 sequences for custom TTE glyphs.
|
||||||
|
*
|
||||||
|
* ASCII bytes, including TTE formatting tags, are copied unchanged. Unsupported Unicode
|
||||||
|
* characters are replaced with '?'. Custom glyph IDs are emitted as UTF-8 because Tonc TTE
|
||||||
|
* decodes every byte above 0x7F as part of a UTF-8 sequence.
|
||||||
|
*
|
||||||
|
* @return Number of encoded bytes, excluding the null terminator.
|
||||||
|
*/
|
||||||
|
size_t chinese_text_encode(const char* utf8, char* output, size_t output_size);
|
||||||
|
|
||||||
|
#endif // CHINESE_TEXT_H
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
|
|
||||||
POOL_ENTRY(Sprite, MAX_SPRITES);
|
POOL_ENTRY(Sprite, MAX_SPRITES);
|
||||||
POOL_ENTRY(SpriteObject, MAX_SPRITE_OBJECTS);
|
|
||||||
POOL_ENTRY(Joker, MAX_ACTIVE_JOKERS);
|
POOL_ENTRY(Joker, MAX_ACTIVE_JOKERS);
|
||||||
POOL_ENTRY(JokerObject, MAX_ACTIVE_JOKERS);
|
POOL_ENTRY(JokerObject, MAX_ACTIVE_JOKERS);
|
||||||
POOL_ENTRY(Card, MAX_CARDS);
|
POOL_ENTRY(Card, MAX_CARDS);
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
// Ordered glyphs appended after ASCII code point 127 in gbalatro_sys8.png.
|
||||||
|
CHINESE_GLYPH(0x5C0F) // 小
|
||||||
|
CHINESE_GLYPH(0x4E11) // 丑
|
||||||
|
CHINESE_GLYPH(0x724C) // 牌
|
||||||
|
CHINESE_GLYPH(0x6C49) // 汉
|
||||||
|
CHINESE_GLYPH(0x5316) // 化
|
||||||
|
CHINESE_GLYPH(0x9884) // 预
|
||||||
|
CHINESE_GLYPH(0x89C8) // 览
|
||||||
|
CHINESE_GLYPH(0x534A) // 半
|
||||||
|
CHINESE_GLYPH(0x5F20) // 张
|
||||||
|
CHINESE_GLYPH(0x6050) // 恐
|
||||||
|
CHINESE_GLYPH(0x6016) // 怖
|
||||||
|
CHINESE_GLYPH(0x9762) // 面
|
||||||
|
CHINESE_GLYPH(0x5B54) // 孔
|
||||||
|
CHINESE_GLYPH(0x5982) // 如
|
||||||
|
CHINESE_GLYPH(0x679C) // 果
|
||||||
|
CHINESE_GLYPH(0x51FA) // 出
|
||||||
|
CHINESE_GLYPH(0x4E0D) // 不
|
||||||
|
CHINESE_GLYPH(0x8D85) // 超
|
||||||
|
CHINESE_GLYPH(0x8FC7) // 过
|
||||||
|
CHINESE_GLYPH(0x6253) // 打
|
||||||
|
CHINESE_GLYPH(0x7684) // 的
|
||||||
|
CHINESE_GLYPH(0x4EBA) // 人
|
||||||
|
CHINESE_GLYPH(0x5934) // 头
|
||||||
|
CHINESE_GLYPH(0x8BA1) // 计
|
||||||
|
CHINESE_GLYPH(0x5206) // 分
|
||||||
|
CHINESE_GLYPH(0x65F6) // 时
|
||||||
|
CHINESE_GLYPH(0x7ED9) // 给
|
||||||
|
CHINESE_GLYPH(0x4E88) // 予
|
||||||
|
CHINESE_GLYPH(0x500D) // 倍
|
||||||
|
CHINESE_GLYPH(0x7387) // 率
|
||||||
|
CHINESE_GLYPH(0x7B79) // 筹
|
||||||
|
CHINESE_GLYPH(0x7801) // 码
|
||||||
|
CHINESE_GLYPH(0x5DE6) // 左
|
||||||
|
CHINESE_GLYPH(0x53F3) // 右
|
||||||
|
CHINESE_GLYPH(0x5207) // 切
|
||||||
|
CHINESE_GLYPH(0x6362) // 换
|
||||||
|
CHINESE_GLYPH(0x52A8) // 动
|
||||||
|
CHINESE_GLYPH(0x753B) // 画
|
||||||
|
CHINESE_GLYPH(0x4E3B) // 主
|
||||||
|
CHINESE_GLYPH(0x83DC) // 菜
|
||||||
|
CHINESE_GLYPH(0x5355) // 单
|
||||||
|
CHINESE_GLYPH(0x4E00) // 一
|
||||||
|
CHINESE_GLYPH(0x4E09) // 三
|
||||||
|
CHINESE_GLYPH(0x4E0B) // 下
|
||||||
|
CHINESE_GLYPH(0x4E24) // 两
|
||||||
|
CHINESE_GLYPH(0x4E2A) // 个
|
||||||
|
CHINESE_GLYPH(0x4E2D) // 中
|
||||||
|
CHINESE_GLYPH(0x4E3A) // 为
|
||||||
|
CHINESE_GLYPH(0x4E8E) // 于
|
||||||
|
CHINESE_GLYPH(0x4F4D) // 位
|
||||||
|
CHINESE_GLYPH(0x4F4E) // 低
|
||||||
|
CHINESE_GLYPH(0x4F59) // 余
|
||||||
|
CHINESE_GLYPH(0x4F8B) // 例
|
||||||
|
CHINESE_GLYPH(0x4FA7) // 侧
|
||||||
|
CHINESE_GLYPH(0x5076) // 偶
|
||||||
|
CHINESE_GLYPH(0x518D) // 再
|
||||||
|
CHINESE_GLYPH(0x51E0) // 几
|
||||||
|
CHINESE_GLYPH(0x5230) // 到
|
||||||
|
CHINESE_GLYPH(0x5236) // 制
|
||||||
|
CHINESE_GLYPH(0x524D) // 前
|
||||||
|
CHINESE_GLYPH(0x5269) // 剩
|
||||||
|
CHINESE_GLYPH(0x529B) // 力
|
||||||
|
CHINESE_GLYPH(0x52A0) // 加
|
||||||
|
CHINESE_GLYPH(0x5305) // 包
|
||||||
|
CHINESE_GLYPH(0x53D1) // 发
|
||||||
|
CHINESE_GLYPH(0x53EF) // 可
|
||||||
|
CHINESE_GLYPH(0x5408) // 合
|
||||||
|
CHINESE_GLYPH(0x540C) // 同
|
||||||
|
CHINESE_GLYPH(0x540E) // 后
|
||||||
|
CHINESE_GLYPH(0x542B) // 含
|
||||||
|
CHINESE_GLYPH(0x548C) // 和
|
||||||
|
CHINESE_GLYPH(0x56DB) // 四
|
||||||
|
CHINESE_GLYPH(0x56DE) // 回
|
||||||
|
CHINESE_GLYPH(0x5747) // 均
|
||||||
|
CHINESE_GLYPH(0x590D) // 复
|
||||||
|
CHINESE_GLYPH(0x5916) // 外
|
||||||
|
CHINESE_GLYPH(0x5947) // 奇
|
||||||
|
CHINESE_GLYPH(0x5B50) // 子
|
||||||
|
CHINESE_GLYPH(0x5BF9) // 对
|
||||||
|
CHINESE_GLYPH(0x5C06) // 将
|
||||||
|
CHINESE_GLYPH(0x5F03) // 弃
|
||||||
|
CHINESE_GLYPH(0x5F53) // 当
|
||||||
|
CHINESE_GLYPH(0x6210) // 成
|
||||||
|
CHINESE_GLYPH(0x6216) // 或
|
||||||
|
CHINESE_GLYPH(0x6240) // 所
|
||||||
|
CHINESE_GLYPH(0x624B) // 手
|
||||||
|
CHINESE_GLYPH(0x62E5) // 拥
|
||||||
|
CHINESE_GLYPH(0x62EC) // 括
|
||||||
|
CHINESE_GLYPH(0x63A5) // 接
|
||||||
|
CHINESE_GLYPH(0x6570) // 数
|
||||||
|
CHINESE_GLYPH(0x65B9) // 方
|
||||||
|
CHINESE_GLYPH(0x65E0) // 无
|
||||||
|
CHINESE_GLYPH(0x6700) // 最
|
||||||
|
CHINESE_GLYPH(0x6709) // 有
|
||||||
|
CHINESE_GLYPH(0x672C) // 本
|
||||||
|
CHINESE_GLYPH(0x673A) // 机
|
||||||
|
CHINESE_GLYPH(0x6761) // 条
|
||||||
|
CHINESE_GLYPH(0x6765) // 来
|
||||||
|
CHINESE_GLYPH(0x6843) // 桃
|
||||||
|
CHINESE_GLYPH(0x6885) // 梅
|
||||||
|
CHINESE_GLYPH(0x69FD) // 槽
|
||||||
|
CHINESE_GLYPH(0x6B21) // 次
|
||||||
|
CHINESE_GLYPH(0x6BCF) // 每
|
||||||
|
CHINESE_GLYPH(0x70B9) // 点
|
||||||
|
CHINESE_GLYPH(0x7247) // 片
|
||||||
|
CHINESE_GLYPH(0x7528) // 用
|
||||||
|
CHINESE_GLYPH(0x7531) // 由
|
||||||
|
CHINESE_GLYPH(0x7A7A) // 空
|
||||||
|
CHINESE_GLYPH(0x7B2C) // 第
|
||||||
|
CHINESE_GLYPH(0x7EA2) // 红
|
||||||
|
CHINESE_GLYPH(0x7EC4) // 组
|
||||||
|
CHINESE_GLYPH(0x80FD) // 能
|
||||||
|
CHINESE_GLYPH(0x8272) // 色
|
||||||
|
CHINESE_GLYPH(0x82B1) // 花
|
||||||
|
CHINESE_GLYPH(0x89C6) // 视
|
||||||
|
CHINESE_GLYPH(0x89E6) // 触
|
||||||
|
CHINESE_GLYPH(0x95F4) // 间
|
||||||
|
CHINESE_GLYPH(0x968F) // 随
|
||||||
|
CHINESE_GLYPH(0x9694) // 隔
|
||||||
|
CHINESE_GLYPH(0x987A) // 顺
|
||||||
|
CHINESE_GLYPH(0x989D) // 额
|
||||||
|
CHINESE_GLYPH(0x9ED1) // 黑
|
||||||
|
CHINESE_GLYPH(0x3001) // 、
|
||||||
@@ -2,6 +2,12 @@
|
|||||||
// (stateEnum, on_init, on_update, on_exit)
|
// (stateEnum, on_init, on_update, on_exit)
|
||||||
DEF_STATE_INFO(GAME_STATE_SPLASH_SCREEN, splash_screen_on_init, splash_screen_on_update, splash_screen_on_exit )
|
DEF_STATE_INFO(GAME_STATE_SPLASH_SCREEN, splash_screen_on_init, splash_screen_on_update, splash_screen_on_exit )
|
||||||
DEF_STATE_INFO(GAME_STATE_MAIN_MENU, game_main_menu_on_init, game_main_menu_on_update, game_main_menu_on_exit )
|
DEF_STATE_INFO(GAME_STATE_MAIN_MENU, game_main_menu_on_init, game_main_menu_on_update, game_main_menu_on_exit )
|
||||||
|
DEF_STATE_INFO(
|
||||||
|
GAME_STATE_JOKER_GALLERY,
|
||||||
|
game_joker_gallery_on_init,
|
||||||
|
game_joker_gallery_on_update,
|
||||||
|
game_joker_gallery_on_exit
|
||||||
|
)
|
||||||
DEF_STATE_INFO(GAME_STATE_OPTIONS_MENU, game_options_menu_on_init, game_options_menu_on_update, game_options_menu_on_exit)
|
DEF_STATE_INFO(GAME_STATE_OPTIONS_MENU, game_options_menu_on_init, game_options_menu_on_update, game_options_menu_on_exit)
|
||||||
DEF_STATE_INFO(GAME_STATE_RUN_SETUP , game_run_setup_on_init, game_run_setup_on_update, game_run_setup_on_exit )
|
DEF_STATE_INFO(GAME_STATE_RUN_SETUP , game_run_setup_on_init, game_run_setup_on_update, game_run_setup_on_exit )
|
||||||
DEF_STATE_INFO(GAME_STATE_GAME_START, noop, game_start, noop )
|
DEF_STATE_INFO(GAME_STATE_GAME_START, noop, game_start, noop )
|
||||||
|
|||||||
+3
-1
@@ -4,12 +4,13 @@
|
|||||||
#include "game/common_ui.h"
|
#include "game/common_ui.h"
|
||||||
#include "game_variables.h"
|
#include "game_variables.h"
|
||||||
#include "graphic_utils.h"
|
#include "graphic_utils.h"
|
||||||
|
#include "item.h"
|
||||||
|
|
||||||
#include <tonc.h>
|
#include <tonc.h>
|
||||||
|
|
||||||
#define MAX_DECK_SIZE 52
|
#define MAX_DECK_SIZE 52
|
||||||
#define MAX_JOKERS_HELD_SIZE 5 // This doesn't account for negatives right now.
|
#define MAX_JOKERS_HELD_SIZE 5 // This doesn't account for negatives right now.
|
||||||
#define MAX_SHOP_JOKERS 2 // TODO: Make this dynamic and allow for other items besides jokers
|
#define MAX_SHOP_ITEMS 2 // TODO: Make this dynamic
|
||||||
#define MAX_SELECTION_SIZE 5
|
#define MAX_SELECTION_SIZE 5
|
||||||
#define FRAMES(x) (((x) + (g_game_vars.game_speed) - 1) / (g_game_vars.game_speed))
|
#define FRAMES(x) (((x) + (g_game_vars.game_speed) - 1) / (g_game_vars.game_speed))
|
||||||
|
|
||||||
@@ -69,6 +70,7 @@ void game_change_state(enum GameState new_game_state);
|
|||||||
enum GameState game_get_state(void);
|
enum GameState game_get_state(void);
|
||||||
|
|
||||||
bool is_joker_owned(int joker_id);
|
bool is_joker_owned(int joker_id);
|
||||||
|
bool joker_object_can_acquire(Item* item);
|
||||||
bool card_is_face(Card* card);
|
bool card_is_face(Card* card);
|
||||||
void add_joker(JokerObject* joker_object);
|
void add_joker(JokerObject* joker_object);
|
||||||
void remove_owned_joker(int owned_joker_idx);
|
void remove_owned_joker(int owned_joker_idx);
|
||||||
|
|||||||
@@ -33,4 +33,9 @@ enum BackgroundId get_current_background(void);
|
|||||||
*/
|
*/
|
||||||
void change_background(enum BackgroundId id, bool force_redraw);
|
void change_background(enum BackgroundId id, bool force_redraw);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Restores the bottom row of the top-left panel from the background map.
|
||||||
|
*/
|
||||||
|
void reset_top_left_panel_bottom_row(void);
|
||||||
|
|
||||||
#endif // COMMON_UI_H
|
#endif // COMMON_UI_H
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @file joker_gallery.h
|
||||||
|
*
|
||||||
|
* @brief Main-menu Joker description gallery state functions.
|
||||||
|
*/
|
||||||
|
#ifndef GAME_JOKER_GALLERY_H
|
||||||
|
#define GAME_JOKER_GALLERY_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the Joker gallery and display the selected Joker.
|
||||||
|
*/
|
||||||
|
void game_joker_gallery_on_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process Joker gallery navigation input.
|
||||||
|
*/
|
||||||
|
void game_joker_gallery_on_update(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroy gallery resources before leaving the state.
|
||||||
|
*/
|
||||||
|
void game_joker_gallery_on_exit(void);
|
||||||
|
|
||||||
|
#endif // GAME_JOKER_GALLERY_H
|
||||||
@@ -23,14 +23,6 @@ void game_shop_reset(void);
|
|||||||
*/
|
*/
|
||||||
JokerObject* game_shop_get_description_card(void);
|
JokerObject* game_shop_get_description_card(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set whether a Joker can appear in the shop.
|
|
||||||
*
|
|
||||||
* @param avail - true to make it available to the shop to appear in
|
|
||||||
* false to make it unavailable.
|
|
||||||
*/
|
|
||||||
void game_shop_set_joker_avail(int joker_id, bool avail);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Change to the shop background
|
* @brief Change to the shop background
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#define GAME_VARIABLES_H
|
#define GAME_VARIABLES_H
|
||||||
|
|
||||||
#include "blind.h"
|
#include "blind.h"
|
||||||
|
#include "hand.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
|
|
||||||
#include <tonc.h>
|
#include <tonc.h>
|
||||||
@@ -49,6 +50,9 @@ typedef struct
|
|||||||
s32 hand_size;
|
s32 hand_size;
|
||||||
s32 deck;
|
s32 deck;
|
||||||
|
|
||||||
|
u32 best_hand_score;
|
||||||
|
u32 nb_played_hands[HAND_TYPE_MAX];
|
||||||
|
|
||||||
// Blind variables
|
// Blind variables
|
||||||
|
|
||||||
enum BlindType current_blind;
|
enum BlindType current_blind;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#ifndef GBALATRO_SYS8_FONT_H
|
#ifndef GBALATRO_SYS8_H
|
||||||
#define GBALATRO_SYS8_FONT_H
|
#define GBALATRO_SYS8_H
|
||||||
|
|
||||||
#include <tonc.h>
|
#include <tonc.h>
|
||||||
|
|
||||||
extern const TFont gbalatro_sys8Font;
|
extern const TFont gbalatro_sys8Font;
|
||||||
extern const unsigned int gbalatro_sys8Glyphs[192];
|
extern const unsigned int gbalatro_sys8Glyphs[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
#define MAIN_BG_CBB 1
|
#define MAIN_BG_CBB 1
|
||||||
#define TTE_SBB 30
|
#define TTE_SBB 30
|
||||||
#define TTE_CBB 0
|
#define TTE_CBB 0
|
||||||
#define AFFINE_BG_SBB 2
|
#define AFFINE_BG_SBB 4
|
||||||
#define AFFINE_BG_CBB 2
|
#define AFFINE_BG_CBB 2
|
||||||
#define PAL_ROW_LEN 16
|
#define PAL_ROW_LEN 16
|
||||||
#define NUM_PALETTES 16
|
#define NUM_PALETTES 16
|
||||||
@@ -510,11 +510,6 @@ void memcpy32_tile8_with_palette_offset(u32* dst, const u32* src, uint wcount, u
|
|||||||
*/
|
*/
|
||||||
void toggle_windows(bool win0, bool win1);
|
void toggle_windows(bool win0, bool win1);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Restores the bottom row of the top-left panel from the background map.
|
|
||||||
*/
|
|
||||||
void reset_top_left_panel_bottom_row(void);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Justify a text with custom formatting tags according to the given
|
* @brief Justify a text with custom formatting tags according to the given
|
||||||
* justification and bias direction tags
|
* justification and bias direction tags
|
||||||
|
|||||||
+44
-10
@@ -40,7 +40,8 @@ enum HandType
|
|||||||
ROYAL_FLUSH,
|
ROYAL_FLUSH,
|
||||||
FIVE_OF_A_KIND,
|
FIVE_OF_A_KIND,
|
||||||
FLUSH_HOUSE,
|
FLUSH_HOUSE,
|
||||||
FLUSH_FIVE
|
FLUSH_FIVE,
|
||||||
|
HAND_TYPE_MAX = FLUSH_FIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -71,6 +72,10 @@ typedef struct ContainedHandTypes
|
|||||||
} ContainedHandTypes;
|
} ContainedHandTypes;
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
// Misc Hand Functions
|
||||||
|
|
||||||
|
const char* get_hand_type_name(enum HandType hand_type);
|
||||||
|
|
||||||
// Hand Structure Manipulation
|
// Hand Structure Manipulation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,24 +216,53 @@ void sort_cards(void);
|
|||||||
// Hand Contents Analysis
|
// Hand Contents Analysis
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the largest flush (set of cards with the same suit) in the given array of played cards.
|
* @brief Finds the best flush (set of cards with the same suit) in the given array of played
|
||||||
* Marks the cards belonging to the best flush in the out_selection array.
|
* cards.
|
||||||
|
*
|
||||||
|
* Normally a Flush is made of 5 cards of the same suit, but this function takes into account
|
||||||
|
* the Four Fingers joker which allows for Flushes made of 4 cards only.
|
||||||
|
*
|
||||||
|
* The cards belonging to that flush will be marked as selected in the out_selection array.
|
||||||
*
|
*
|
||||||
* @param played Array of pointers to CardObject representing played cards.
|
* @param played Array of pointers to CardObject representing played cards.
|
||||||
* @param top Index of the top of the played stack.
|
* @param top Index of the top of the played stack.
|
||||||
* @param min_len Minimum number of cards required for a flush.
|
* @param min_len Minimum number of cards required for a flush.
|
||||||
* @param out_selection Output array of bools; set to true for cards in the best flush, false
|
* @param out_selection Output array of bools; set to true for cards in the best flush, false
|
||||||
* otherwise.
|
* otherwise.
|
||||||
|
*
|
||||||
* @return The number of cards in the best flush found, or 0 if no flush meets min_len.
|
* @return The number of cards in the best flush found, or 0 if no flush meets min_len.
|
||||||
*/
|
*/
|
||||||
int find_flush_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection);
|
int find_flush_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection);
|
||||||
int find_straight_in_played_cards(
|
|
||||||
CardObject** played,
|
/**
|
||||||
int top,
|
* @brief Finds the best straight in the given array of played cards.
|
||||||
bool shortcut_active,
|
*
|
||||||
int min_len,
|
* Normally, a Straight is a set of 5 cards with sequential ranks, but this function takes into
|
||||||
bool* out_selection
|
* account the Four Fingers and Shortcut jokers, which respectively allow for Straights made of
|
||||||
);
|
* 4 cards and with gaps of 1 rank between two cards.
|
||||||
|
*
|
||||||
|
* The cards belonging to that straight will be marked as selected in the out_selection array.
|
||||||
|
*
|
||||||
|
* @param played Array of pointers to CardObject representing played cards.
|
||||||
|
* @param top Index of the top of the played stack.
|
||||||
|
* @param min_len Minimum number of cards required for a straight.
|
||||||
|
* @param out_selection Output array of bools; set to true for cards in the best straight, false
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* @return The number of cards in the best straight found, or 0 if no straight meets
|
||||||
|
* min_len.
|
||||||
|
*/
|
||||||
|
int find_straight_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is used for the special case in "Four Fingers" where you can add a pair into a
|
||||||
|
* straight (e.g. AA234 should score all 5 cards)
|
||||||
|
*
|
||||||
|
* @param played Array of pointers to CardObject representing played cards.
|
||||||
|
* @param top Index of the top of the played stack.
|
||||||
|
* @param selection In/Out array of bools; cards from a straight are already set to true, so we
|
||||||
|
* add to those the leftover ones that form a pair with an already selected card.
|
||||||
|
*/
|
||||||
void select_paired_cards_in_hand(CardObject** played, int top, bool* selection);
|
void select_paired_cards_in_hand(CardObject** played, int top, bool* selection);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+173
@@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
* @file item.h
|
||||||
|
*
|
||||||
|
* @brief The core structure for items in the shop and inventory.
|
||||||
|
* Provides a common API for the shop and inventory to handle all types of items.
|
||||||
|
* Uses struct inheritance so all inherited items can implement an is-a relationship with Item.
|
||||||
|
* This means that pointers to structs that inherit Item using first member struct inheritance
|
||||||
|
* can and should be cast to Item* so code that expects an Item* can use them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ITEM_H
|
||||||
|
#define ITEM_H
|
||||||
|
|
||||||
|
#include "mgba_logger.h"
|
||||||
|
#include "sprite.h"
|
||||||
|
|
||||||
|
// TODO: Merge these with GBAL_RETURN_IF_NULL macros?
|
||||||
|
/**
|
||||||
|
* @brief Checks if @p item is of type @p expected_type - logs error message and returns otherwise
|
||||||
|
*
|
||||||
|
* This version is for a function that returns a value, while
|
||||||
|
* @ref ITEM_RETURN_IF_UNEXPECTED_TYPE_VOID is for a void function.
|
||||||
|
*/
|
||||||
|
#define ITEM_RETURN_IF_UNEXPECTED_TYPE_RET(item, expected_type, ret_val) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if ((item)->type != expected_type) \
|
||||||
|
{ \
|
||||||
|
MGBA_FUNC_ERROR("Unexpected %s->type != %s", #item, #expected_type); \
|
||||||
|
return (ret_val); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if @p item is of type @p expected_type - logs error message and returns otherwise
|
||||||
|
*
|
||||||
|
* This version is for a void function, while @ref ITEM_RETURN_IF_UNEXPECTED_TYPE_RET is for one
|
||||||
|
* with a return value.
|
||||||
|
*/
|
||||||
|
#define ITEM_RETURN_IF_UNEXPECTED_TYPE_VOID(item, expected_type) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if ((item)->type != expected_type) \
|
||||||
|
{ \
|
||||||
|
MGBA_FUNC_ERROR("Unexpected %s->type != %s", #item, #expected_type); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
enum ItemType
|
||||||
|
{
|
||||||
|
ITEM_TYPE_JOKER,
|
||||||
|
ITEM_TYPE_PLAYING_CARD,
|
||||||
|
|
||||||
|
// Future planned item types
|
||||||
|
// ITEM_TYPE_CONSUMABLE, // Expand to PLANET, TAROT, and SPECTRAL?
|
||||||
|
// ITEM_TYPE_VOUCHER,
|
||||||
|
// ITEM_TYPE_PACK
|
||||||
|
|
||||||
|
ITEM_NUM_TYPES
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A generic interface for all items that can appear in the shop or be in the inventory.
|
||||||
|
* This uses first member struct inheritance - other structs are meant to inherit it by
|
||||||
|
* making their first member field Item.
|
||||||
|
* Then casts from inheriting structs to Item* are allowed and intentional and this allows for
|
||||||
|
* generic code that uses polymorphism.
|
||||||
|
* The -fms-extensions compile flag allows for anonymous members making it behave fully
|
||||||
|
* as inheritance. It makes all member fields be fully inherited so any struct
|
||||||
|
* that inherits Item for example will have all its fields accessible directly,
|
||||||
|
* e.g. `JokerObject joker_object; joker_object.type = ITEM_TYPE_JOKER`
|
||||||
|
*/
|
||||||
|
typedef struct Item
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief First member struct inheritance
|
||||||
|
* all items that can appear in the shop are SpriteObjects.
|
||||||
|
* Note that this is an anonymous member.
|
||||||
|
*/
|
||||||
|
SpriteObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The item type - used to dispatch the function implementations for inheriting types.
|
||||||
|
*/
|
||||||
|
enum ItemType type;
|
||||||
|
} Item;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The set of functions that each item type implements.
|
||||||
|
*/
|
||||||
|
typedef struct ItemFuncs
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* All items must implement the following since they are called by the shop and all items
|
||||||
|
* must be capable of appearing in the shop.
|
||||||
|
*/
|
||||||
|
Item* (*roll_new)(void);
|
||||||
|
int (*get_buy_price)(Item* item);
|
||||||
|
bool (*can_acquire)(Item* item);
|
||||||
|
void (*acquire)(Item* item);
|
||||||
|
void (*dispose)(Item** item);
|
||||||
|
// TODO: void (*print_description)(Item* item); // or something of the form
|
||||||
|
|
||||||
|
// Optional implementation functions will be added here
|
||||||
|
} ItemFuncs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rolls a random item of type @p item_type and returns a newly created one.
|
||||||
|
* Manages rollable items set if necessary (i.e. not rolling items already in inventory)
|
||||||
|
* To be used when rolling new items for the shop or packs.
|
||||||
|
*
|
||||||
|
* Matches @ref ItemFuncs.roll_new()
|
||||||
|
*
|
||||||
|
* @param item_type The type of the item to roll
|
||||||
|
*
|
||||||
|
* @return The newly created randomly rolled item
|
||||||
|
*/
|
||||||
|
Item* item_roll_new(enum ItemType item_type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the buy price of the item.
|
||||||
|
*
|
||||||
|
* Matches @ref ItemFuncs.get_buy_price()
|
||||||
|
*
|
||||||
|
* @param item The item whose price to return.
|
||||||
|
*
|
||||||
|
* @return UNDEFINED in case of error, the item's buy price otherwise.
|
||||||
|
*/
|
||||||
|
int item_get_buy_price(Item* item);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Acquires the item, adding to inventory if applicable.
|
||||||
|
* Called when it is purchased from the shop, note that it does not
|
||||||
|
* perform the purchase operation of decrementing the player's money,
|
||||||
|
* that should be handled by the shop code.
|
||||||
|
* For packs this can be to just open the pack,
|
||||||
|
* for vouchers, this will apply their effect.
|
||||||
|
*
|
||||||
|
* Matches @ref ItemFuncs.acquire()
|
||||||
|
*
|
||||||
|
* @param item The item to acquire
|
||||||
|
*/
|
||||||
|
void item_acquire(Item* item);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the item can be acquired, i.e. added to inventory.
|
||||||
|
* Does not check if the player has enough money to buy the item, that is the shop's job,
|
||||||
|
* as this will be used both when purchasing and when selecting in a pack.
|
||||||
|
*
|
||||||
|
* Matches @ref ItemFuncs.can_acquire()
|
||||||
|
*
|
||||||
|
* @param item The item to check
|
||||||
|
*/
|
||||||
|
bool item_can_acquire(Item* item);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroys an item, freeing underlying resources, and manages rollable items sets if needed.
|
||||||
|
* To be used when destroying items from the inventory, shop, or packs.
|
||||||
|
*
|
||||||
|
* @param item A pointer to an item for destruction.
|
||||||
|
*/
|
||||||
|
void item_dispose(Item** item);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints the buy price under the item
|
||||||
|
* Relies on the fact item is a SpriteObject
|
||||||
|
*
|
||||||
|
* @param item The item to print under
|
||||||
|
*/
|
||||||
|
void item_print_buy_price_under(Item* item);
|
||||||
|
|
||||||
|
#endif // ITEM_H
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @file item_funcs.h
|
||||||
|
*
|
||||||
|
* @brief An API for the item functions defined in item.h
|
||||||
|
* Separated module from item.c/.h so item_funcs.c can include joker.h and card.h etc.
|
||||||
|
* so the item.h doesn't depend on implementations such as jokers and cards.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ITEM_FUNCS_H
|
||||||
|
#define ITEM_FUNCS_H
|
||||||
|
|
||||||
|
#include "item.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the function table for a given item type.
|
||||||
|
*
|
||||||
|
* Looks up the @ref ItemFuncs dispatch table corresponding to @p type.
|
||||||
|
*
|
||||||
|
* @param type The item type whose function table to retrieve.
|
||||||
|
*
|
||||||
|
* @return Pointer to the @ref ItemFuncs table for @p type,
|
||||||
|
* or NULL if @p type is invalid or has no implemented functions.
|
||||||
|
*/
|
||||||
|
ItemFuncs* get_item_type_funcs(enum ItemType type);
|
||||||
|
|
||||||
|
#endif // ITEM_FUNCS_H
|
||||||
+47
-2
@@ -9,7 +9,7 @@
|
|||||||
#include "card.h"
|
#include "card.h"
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "graphic_utils.h"
|
#include "graphic_utils.h"
|
||||||
#include "sprite.h"
|
#include "item.h"
|
||||||
|
|
||||||
#include <maxmod.h>
|
#include <maxmod.h>
|
||||||
|
|
||||||
@@ -112,8 +112,8 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct JokerObject
|
typedef struct JokerObject
|
||||||
{
|
{
|
||||||
|
Item; // First member struct inheritance
|
||||||
Joker* joker;
|
Joker* joker;
|
||||||
SpriteObject* sprite_object;
|
|
||||||
} JokerObject;
|
} JokerObject;
|
||||||
|
|
||||||
typedef struct // These jokers are triggered after the played hand has finished scoring.
|
typedef struct // These jokers are triggered after the played hand has finished scoring.
|
||||||
@@ -197,6 +197,51 @@ JokerObject* joker_object_new(Joker* joker);
|
|||||||
void joker_object_destroy(JokerObject** joker_object);
|
void joker_object_destroy(JokerObject** joker_object);
|
||||||
// This doesn't actually score anything, it just performs an animation and plays a sound effect
|
// This doesn't actually score anything, it just performs an animation and plays a sound effect
|
||||||
void joker_object_shake(JokerObject* joker_object, mm_word sound_id);
|
void joker_object_shake(JokerObject* joker_object, mm_word sound_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the buy price of the joker object.
|
||||||
|
*
|
||||||
|
* @param joker_object the joker object whose price to return.
|
||||||
|
*
|
||||||
|
* @return UNDEFINED in case of error, the buy price of the joker otherwise.
|
||||||
|
*/
|
||||||
|
int joker_object_get_buy_price(Item* joker_object);
|
||||||
|
|
||||||
|
// TODO: Move to an owned_jokers.c/.h file?
|
||||||
|
/**
|
||||||
|
* @brief Add a Joker to the list of owned Jokers and place it in the joker row.
|
||||||
|
*
|
||||||
|
* @param joker_object The JokerObject to add cast to Item*
|
||||||
|
*/
|
||||||
|
void joker_object_add_to_owned(Item* joker_object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroy a JokerObject item, free its resources, and make it available to be rolled.
|
||||||
|
*
|
||||||
|
* @param joker_object Pointer to the JokerObject Item* to destroy; set to NULL.
|
||||||
|
*/
|
||||||
|
void joker_object_dispose(Item** joker_object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set whether a Joker is available to be rolled for the shop, packs, etc.
|
||||||
|
*
|
||||||
|
* @param joker_id The ID of the joker whose availability to set.
|
||||||
|
* @param rollable true to make it rollable, false otherwise.
|
||||||
|
*/
|
||||||
|
void joker_set_rollable(int joker_id, bool rollable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset rollable jokers to include all jokers in the registry.
|
||||||
|
*/
|
||||||
|
void joker_reset_rollable_jokers(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Roll and create a new JokerObject item.
|
||||||
|
*
|
||||||
|
* @return Newly created `Item*` (JokerObject) or NULL if none available.
|
||||||
|
*/
|
||||||
|
Item* joker_object_roll_new(void);
|
||||||
|
|
||||||
// This scores the joker and returns true if it was scored successfully
|
// This scores the joker and returns true if it was scored successfully
|
||||||
// card_object = NULL means the joker_event does not concern a particular Card, i.e. Independend or
|
// card_object = NULL means the joker_event does not concern a particular Card, i.e. Independend or
|
||||||
// On_Blind_Selected as opposed to events that concern a particular card, i.e. On_Card_Scored or
|
// On_Blind_Selected as opposed to events that concern a particular card, i.e. On_Card_Scored or
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ static const Rect TOP_LEFT_PANEL_ANIM_RECT = {0, 0, 8,
|
|||||||
static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31};
|
static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31};
|
||||||
static const Rect DECK_ANIM_RECT = {25, 14, 28, 23}; // Can be used for the Shop and Blind Select screen
|
static const Rect DECK_ANIM_RECT = {25, 14, 28, 23}; // Can be used for the Shop and Blind Select screen
|
||||||
static const Rect TOP_LEFT_ITEM_SRC_RECT = {0, 20, 8, 25};
|
static const Rect TOP_LEFT_ITEM_SRC_RECT = {0, 20, 8, 25};
|
||||||
|
static const Rect TOP_LEFT_PANEL_BOTTOM_ROW_RESET_RECT = {0, 28, 8, 28};
|
||||||
static const Rect BLIND_REWARD_RECT = {40, 32, 64, 40};
|
static const Rect BLIND_REWARD_RECT = {40, 32, 64, 40};
|
||||||
static const Rect BLIND_REQ_TEXT_RECT = {32, 24, 64, 32};
|
static const Rect BLIND_REQ_TEXT_RECT = {32, 24, 64, 32};
|
||||||
static const Rect PLAYING_SCREEN_RECT = {72, 0, 240, 160};
|
static const Rect PLAYING_SCREEN_RECT = {72, 0, 240, 160};
|
||||||
|
|||||||
+32
-1
@@ -17,7 +17,7 @@
|
|||||||
* mgba -l 14 game.rom # INFO, WARN, and ERROR
|
* mgba -l 14 game.rom # INFO, WARN, and ERROR
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @note You have to fight with other logs in mgba and DEBUG can get messy.
|
* @note You have to fight with other logs in mgba and INFO can get messy.
|
||||||
*
|
*
|
||||||
* @note FATAL does kill the game. Use with care.
|
* @note FATAL does kill the game. Use with care.
|
||||||
*/
|
*/
|
||||||
@@ -54,19 +54,50 @@ bool mgba_logger_init(void);
|
|||||||
*/
|
*/
|
||||||
void mgba_printf(MgbaLogLevel level, const char* fmt, ...);
|
void mgba_printf(MgbaLogLevel level, const char* fmt, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print to mgba log with a format string and function name
|
||||||
|
*
|
||||||
|
* @param level
|
||||||
|
* @param func_name Function name - prepended to the log string "<func_name>(): <log_string>"
|
||||||
|
* @param fmt Format string
|
||||||
|
* @param ... variadic arguments
|
||||||
|
*
|
||||||
|
* @note for all logs, it's cutoff at the hard mgba limit of 0x100
|
||||||
|
*/
|
||||||
|
void mgba_func_printf(MgbaLogLevel level, const char* func_name, const char* fmt, ...);
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#ifdef MGBA_LOGGING
|
#ifdef MGBA_LOGGING
|
||||||
|
|
||||||
#define MGBA_FATAL(...) mgba_printf(MGBA_LOG_FATAL, __VA_ARGS__)
|
#define MGBA_FATAL(...) mgba_printf(MGBA_LOG_FATAL, __VA_ARGS__)
|
||||||
#define MGBA_ERROR(...) mgba_printf(MGBA_LOG_ERROR, __VA_ARGS__)
|
#define MGBA_ERROR(...) mgba_printf(MGBA_LOG_ERROR, __VA_ARGS__)
|
||||||
#define MGBA_WARN(...) mgba_printf(MGBA_LOG_WARN, __VA_ARGS__)
|
#define MGBA_WARN(...) mgba_printf(MGBA_LOG_WARN, __VA_ARGS__)
|
||||||
#define MGBA_INFO(...) mgba_printf(MGBA_LOG_INFO, __VA_ARGS__)
|
#define MGBA_INFO(...) mgba_printf(MGBA_LOG_INFO, __VA_ARGS__)
|
||||||
#define MGBA_DEBUG(...) mgba_printf(MGBA_LOG_DEBUG, __VA_ARGS__)
|
#define MGBA_DEBUG(...) mgba_printf(MGBA_LOG_DEBUG, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define MGBA_FUNC_LOG(level, ...) mgba_func_printf(level, __func__, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define MGBA_FUNC_FATAL(...) MGBA_FUNC_LOG(MGBA_LOG_FATAL, __VA_ARGS__)
|
||||||
|
#define MGBA_FUNC_ERROR(...) MGBA_FUNC_LOG(MGBA_LOG_ERROR, __VA_ARGS__)
|
||||||
|
#define MGBA_FUNC_WARN(...) MGBA_FUNC_LOG(MGBA_LOG_WARN, __VA_ARGS__)
|
||||||
|
#define MGBA_FUNC_INFO(...) MGBA_FUNC_LOG(MGBA_LOG_INFO, __VA_ARGS__)
|
||||||
|
#define MGBA_FUNC_DEBUG(...) MGBA_FUNC_LOG(MGBA_LOG_DEBUG, __VA_ARGS__)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define MGBA_FATAL(...) ((void)0)
|
#define MGBA_FATAL(...) ((void)0)
|
||||||
#define MGBA_ERROR(...) ((void)0)
|
#define MGBA_ERROR(...) ((void)0)
|
||||||
#define MGBA_WARN(...) ((void)0)
|
#define MGBA_WARN(...) ((void)0)
|
||||||
#define MGBA_INFO(...) ((void)0)
|
#define MGBA_INFO(...) ((void)0)
|
||||||
#define MGBA_DEBUG(...) ((void)0)
|
#define MGBA_DEBUG(...) ((void)0)
|
||||||
|
|
||||||
|
#define MGBA_FUNC_LOG(level, ...) ((void)0)
|
||||||
|
|
||||||
|
#define MGBA_FUNC_FATAL(...) ((void)0)
|
||||||
|
#define MGBA_FUNC_ERROR(...) ((void)0)
|
||||||
|
#define MGBA_FUNC_WARN(...) ((void)0)
|
||||||
|
#define MGBA_FUNC_INFO(...) ((void)0)
|
||||||
|
#define MGBA_FUNC_DEBUG(...) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ typedef struct
|
|||||||
void rng_init(void);
|
void rng_init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Update the CPU cycles counter and accumulate it into a bigger u32 `timer_acc` variable.
|
* @brief Accumulates CPU cycles for RNG seed generation, call exaclty once per frame.
|
||||||
*/
|
*/
|
||||||
void rng_update(void);
|
void rng_update(void);
|
||||||
|
|
||||||
|
|||||||
+9
-8
@@ -194,23 +194,24 @@ void sprite_init(void);
|
|||||||
void sprite_draw(void);
|
void sprite_draw(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Allocate and retrieve a pointer to a valid SpriteObject
|
* @brief Initialize a SpriteObject to a default state.
|
||||||
|
* Must be called only once per SpriteObject when it is created.
|
||||||
*
|
*
|
||||||
* @return A valid pointer to an newly allocated SpriteObject
|
* @param sprite_object - The SpriteObject to initialize
|
||||||
* if successful, othewise return **NULL**.
|
|
||||||
*/
|
*/
|
||||||
SpriteObject* sprite_object_new();
|
void sprite_object_init(SpriteObject* sprite_object);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destroy SpriteObject
|
* @brief Destroy SpriteObject
|
||||||
*
|
*
|
||||||
* Destroy a SpriteObject by freeing it back to the pool and releasing its
|
* Destroy a SpriteObject by releasing its associated resources (e.g. the sprite).
|
||||||
* associated resources
|
* This invalidates the SpriteObject and it should not be used after destroyed,
|
||||||
|
* a new one should be created instead.
|
||||||
*
|
*
|
||||||
* @param sprite_object pointer to a pointer of SpriteObject to destroy.
|
* @param sprite_object pointer to a SpriteObject to destroy.
|
||||||
* Cannot be **NULL**.
|
* Cannot be **NULL**.
|
||||||
*/
|
*/
|
||||||
void sprite_object_destroy(SpriteObject** sprite_object);
|
void sprite_object_destroy(SpriteObject* sprite_object);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register a Sprite to an associated SpriteObject
|
* @brief Register a Sprite to an associated SpriteObject
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
#define UTIL_H
|
#define UTIL_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#ifdef MGBA_LOGGING
|
||||||
|
#include "mgba_logger.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def GBAL_UNUSED
|
* @def GBAL_UNUSED
|
||||||
@@ -56,6 +59,49 @@
|
|||||||
// so it needs at least this number of chars to be able to display any suffixed number
|
// so it needs at least this number of chars to be able to display any suffixed number
|
||||||
#define SUFFIXED_NUM_MIN_REQ_CHARS 4
|
#define SUFFIXED_NUM_MIN_REQ_CHARS 4
|
||||||
|
|
||||||
|
#ifdef MGBA_LOGGING
|
||||||
|
#define LOG_ERROR(...) MGBA_FUNC_ERROR(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
// TODO: Add a define to conditionally compile print error to console and add it to the tests?
|
||||||
|
#define LOG_ERROR(...) ((void)(0))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if @p param is NULL and prints error message and returns in case it is.
|
||||||
|
* Useful for checking arguments to a function or errors during control flow.
|
||||||
|
*
|
||||||
|
* This version is for a void function, while @ref GBAL_RETURN_ON_ERROR_VAL_RET is for one with
|
||||||
|
* a return value.
|
||||||
|
*/
|
||||||
|
#define GBAL_RETURN_IF_NULL_VOID(param) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if ((param) == NULL) \
|
||||||
|
{ \
|
||||||
|
LOG_ERROR("Unexpected value: %s == NULL", #param); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if @p param is equal to NULL
|
||||||
|
* and prints error message and returns in case it is.
|
||||||
|
* Useful for checking arguments to a function or errors during control flow.
|
||||||
|
* @param ret_val The value to return in case @p param is equal to NULL.
|
||||||
|
*
|
||||||
|
* This version is for a function that returns a value while @ref GBAL_RETURN_ON_ERROR_VAL_VOID
|
||||||
|
* is for a void function.
|
||||||
|
*/
|
||||||
|
#define GBAL_RETURN_IF_NULL_RET(param, ret_val) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if ((param) == NULL) \
|
||||||
|
{ \
|
||||||
|
LOG_ERROR("Unexpected value: %s == NULL", #param); \
|
||||||
|
return (ret_val); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Avoid overflow when adding two u32 integers
|
* @brief Avoid overflow when adding two u32 integers
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""Append the Chinese Joker-description glyph subset to the project's 8x8 ASCII font."""
|
||||||
|
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
ASCII_FONT_HEIGHT = 48
|
||||||
|
CELL_SIZE = 8
|
||||||
|
GLYPHS_PER_ROW = 16
|
||||||
|
|
||||||
|
|
||||||
|
def read_codepoints(table_path: Path) -> list[int]:
|
||||||
|
table = table_path.read_text(encoding="utf-8")
|
||||||
|
return [int(value, 16) for value in re.findall(r"CHINESE_GLYPH\(0x([0-9A-Fa-f]+)\)", table)]
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument("--font", required=True, help="Fusion Pixel Font 8px zh_hans TTF")
|
||||||
|
parser.add_argument("--table", default="include/def_chinese_glyph_table.h")
|
||||||
|
parser.add_argument("--image", default="font/gbalatro_sys8.png")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
image_path = Path(args.image)
|
||||||
|
base = Image.open(image_path).convert("RGB").crop((0, 0, 128, ASCII_FONT_HEIGHT))
|
||||||
|
codepoints = read_codepoints(Path(args.table))
|
||||||
|
extra_rows = (len(codepoints) + GLYPHS_PER_ROW - 1) // GLYPHS_PER_ROW
|
||||||
|
|
||||||
|
output = Image.new("RGB", (128, ASCII_FONT_HEIGHT + extra_rows * CELL_SIZE), "white")
|
||||||
|
output.paste(base, (0, 0))
|
||||||
|
|
||||||
|
draw = ImageDraw.Draw(output)
|
||||||
|
font = ImageFont.truetype(args.font, CELL_SIZE)
|
||||||
|
for index, codepoint in enumerate(codepoints):
|
||||||
|
x = (index % GLYPHS_PER_ROW) * CELL_SIZE
|
||||||
|
y = ASCII_FONT_HEIGHT + (index // GLYPHS_PER_ROW) * CELL_SIZE
|
||||||
|
draw.text((x, y - 1), chr(codepoint), font=font, fill="black")
|
||||||
|
|
||||||
|
output.save(image_path, optimize=True)
|
||||||
|
print(f"Appended {len(codepoints)} Chinese glyphs to {image_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -5,7 +5,6 @@ import argparse
|
|||||||
|
|
||||||
CHAR_WIDTH = 8
|
CHAR_WIDTH = 8
|
||||||
CHAR_HEIGHT = 8
|
CHAR_HEIGHT = 8
|
||||||
NUM_VERT_CHARS = 6
|
|
||||||
NUM_HORZ_CHARS = 16
|
NUM_HORZ_CHARS = 16
|
||||||
WORD_SIZE = 32
|
WORD_SIZE = 32
|
||||||
|
|
||||||
@@ -48,8 +47,14 @@ img = img.convert("P")
|
|||||||
img = img.convert('1')
|
img = img.convert('1')
|
||||||
img = ImageOps.invert(img)
|
img = ImageOps.invert(img)
|
||||||
|
|
||||||
|
if img.width != NUM_HORZ_CHARS * CHAR_WIDTH or img.height % CHAR_HEIGHT != 0:
|
||||||
|
raise ValueError("font image must be 128px wide and a multiple of 8px high")
|
||||||
|
|
||||||
|
num_vert_chars = img.height // CHAR_HEIGHT
|
||||||
|
num_chars = num_vert_chars * NUM_HORZ_CHARS
|
||||||
|
|
||||||
glyphs = []
|
glyphs = []
|
||||||
for char_y in range(NUM_VERT_CHARS):
|
for char_y in range(num_vert_chars):
|
||||||
for char_x in range(NUM_HORZ_CHARS):
|
for char_x in range(NUM_HORZ_CHARS):
|
||||||
# Get the 8x8 crop area at our current character in the image
|
# Get the 8x8 crop area at our current character in the image
|
||||||
pix_x = char_x * CHAR_WIDTH
|
pix_x = char_x * CHAR_WIDTH
|
||||||
@@ -86,7 +91,7 @@ with open(out_path, "w") as out:
|
|||||||
.global gbalatro_sys8Font
|
.global gbalatro_sys8Font
|
||||||
gbalatro_sys8Font:
|
gbalatro_sys8Font:
|
||||||
.word gbalatro_sys8Glyphs, 0, 0
|
.word gbalatro_sys8Glyphs, 0, 0
|
||||||
.hword 32, 96
|
.hword 32, %d
|
||||||
.byte 8, 8
|
.byte 8, 8
|
||||||
.byte 8, 8
|
.byte 8, 8
|
||||||
.hword 8
|
.hword 8
|
||||||
@@ -94,9 +99,9 @@ gbalatro_sys8Font:
|
|||||||
|
|
||||||
.section .rodata
|
.section .rodata
|
||||||
.align 2
|
.align 2
|
||||||
.global gbalatro_sys8Glyphs @ 768 bytes (192 unsigned ints)
|
.global gbalatro_sys8Glyphs
|
||||||
gbalatro_sys8Glyphs:
|
gbalatro_sys8Glyphs:
|
||||||
""")
|
""" % num_chars)
|
||||||
|
|
||||||
for i, glyph in enumerate(glyphs):
|
for i, glyph in enumerate(glyphs):
|
||||||
if first:
|
if first:
|
||||||
|
|||||||
+29
-29
@@ -11,10 +11,10 @@
|
|||||||
// done in VBLANK so the HBLANK code can just fetch the values quickly.
|
// done in VBLANK so the HBLANK code can just fetch the values quickly.
|
||||||
static IWRAM_CODE void s_affine_background_prep_bgaff_arr();
|
static IWRAM_CODE void s_affine_background_prep_bgaff_arr();
|
||||||
|
|
||||||
static BG_AFFINE _bgaff_arr[SCREEN_HEIGHT + 1];
|
static BG_AFFINE s_bgaff_arr[SCREEN_HEIGHT + 1];
|
||||||
static AFF_SRC_EX _asx = {0};
|
static AFF_SRC_EX s_asx = {0};
|
||||||
static enum AffineBackgroundID _background = AFFINE_BG_NONE;
|
static enum AffineBackgroundID s_background = AFFINE_BG_NONE;
|
||||||
static uint _timer = 0;
|
static uint s_timer = 0;
|
||||||
|
|
||||||
void affine_background_init()
|
void affine_background_init()
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,7 @@ IWRAM_CODE void affine_background_hblank()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See comment in affine_background_prep_bgaff_arr()
|
// See comment in affine_background_prep_bgaff_arr()
|
||||||
REG_BG_AFFINE[AFFINE_BG_IDX] = _bgaff_arr[vcount + 1];
|
REG_BG_AFFINE[AFFINE_BG_IDX] = s_bgaff_arr[vcount + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
void affine_background_update()
|
void affine_background_update()
|
||||||
@@ -44,21 +44,21 @@ void affine_background_update()
|
|||||||
}
|
}
|
||||||
else // Low quality mode without HBLANK interrupt
|
else // Low quality mode without HBLANK interrupt
|
||||||
{
|
{
|
||||||
_asx.scr_x = 0;
|
s_asx.scr_x = 0;
|
||||||
_asx.scr_y = 0;
|
s_asx.scr_y = 0;
|
||||||
_asx.tex_x += 5;
|
s_asx.tex_x += 5;
|
||||||
_asx.tex_y += 12;
|
s_asx.tex_y += 12;
|
||||||
// Scale the sine value to fit in a s16
|
// Scale the sine value to fit in a s16
|
||||||
_asx.sx = ((lu_sin(_timer * 100)) >> 8) + 256;
|
s_asx.sx = ((lu_sin(s_timer * 100)) >> 8) + 256;
|
||||||
// Scale the sine value to fit in a s16
|
// Scale the sine value to fit in a s16
|
||||||
_asx.sy = ((lu_sin(_timer * 100 + 0x4000)) >> 8) + 256;
|
s_asx.sy = ((lu_sin(s_timer * 100 + 0x4000)) >> 8) + 256;
|
||||||
_asx.alpha = 0;
|
s_asx.alpha = 0;
|
||||||
|
|
||||||
bg_rotscale_ex(&_bgaff_arr[0], &_asx);
|
bg_rotscale_ex(&s_bgaff_arr[0], &s_asx);
|
||||||
REG_BG_AFFINE[AFFINE_BG_IDX] = _bgaff_arr[0];
|
REG_BG_AFFINE[AFFINE_BG_IDX] = s_bgaff_arr[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
_timer++;
|
s_timer++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void affine_background_set_color(COLOR color)
|
void affine_background_set_color(COLOR color)
|
||||||
@@ -66,7 +66,7 @@ void affine_background_set_color(COLOR color)
|
|||||||
// Reload the palette to reset any previous color scaling.
|
// Reload the palette to reset any previous color scaling.
|
||||||
// Take source color directly from `affine_background_gfxPal` to avoid excessive
|
// Take source color directly from `affine_background_gfxPal` to avoid excessive
|
||||||
// darkening in case this function is called twice by mistake.
|
// darkening in case this function is called twice by mistake.
|
||||||
affine_background_change_background(_background);
|
affine_background_change_background(s_background);
|
||||||
for (int i = 0; i < AFFINE_BG_PAL_LEN; i++)
|
for (int i = 0; i < AFFINE_BG_PAL_LEN; i++)
|
||||||
{
|
{
|
||||||
clr_rgbscale(&pal_bg_mem[AFFINE_BG_PB] + i, affine_background_gfxPal + i, 1, color);
|
clr_rgbscale(&pal_bg_mem[AFFINE_BG_PB] + i, affine_background_gfxPal + i, 1, color);
|
||||||
@@ -80,14 +80,14 @@ void affine_background_load_palette(const u16* src)
|
|||||||
|
|
||||||
void affine_background_change_background(enum AffineBackgroundID new_bg)
|
void affine_background_change_background(enum AffineBackgroundID new_bg)
|
||||||
{
|
{
|
||||||
if (_background == new_bg)
|
if (s_background == new_bg)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_background = new_bg;
|
s_background = new_bg;
|
||||||
|
|
||||||
switch (_background)
|
switch (s_background)
|
||||||
{
|
{
|
||||||
case AFFINE_BG_MAIN_MENU:
|
case AFFINE_BG_MAIN_MENU:
|
||||||
REG_BG2CNT &= ~BG_AFF_32x32;
|
REG_BG2CNT &= ~BG_AFF_32x32;
|
||||||
@@ -126,26 +126,26 @@ static IWRAM_CODE void s_affine_background_prep_bgaff_arr()
|
|||||||
{
|
{
|
||||||
for (u16 vcount = 0; vcount < SCREEN_HEIGHT; vcount++)
|
for (u16 vcount = 0; vcount < SCREEN_HEIGHT; vcount++)
|
||||||
{
|
{
|
||||||
const s32 timer_s32 = _timer << 8;
|
const s32 timer_s32 = s_timer << 8;
|
||||||
const s32 vcount_s32 = vcount << 8;
|
const s32 vcount_s32 = vcount << 8;
|
||||||
const s16 vcount_s16 = vcount;
|
const s16 vcount_s16 = vcount;
|
||||||
const s32 vcount_sine = lu_sin(vcount_s32 + timer_s32 / ANIMATION_SPEED_DIVISOR);
|
const s32 vcount_sine = lu_sin(vcount_s32 + timer_s32 / ANIMATION_SPEED_DIVISOR);
|
||||||
|
|
||||||
_asx.scr_x = (SCREEN_WIDTH / 2);
|
s_asx.scr_x = (SCREEN_WIDTH / 2);
|
||||||
// scr_y must equal vcount otherwise the background will have no vertical difference
|
// scr_y must equal vcount otherwise the background will have no vertical difference
|
||||||
_asx.scr_y = vcount_s16 - (SCREEN_HEIGHT / 2);
|
s_asx.scr_y = vcount_s16 - (SCREEN_HEIGHT / 2);
|
||||||
_asx.tex_x = (1000 * 1000) + (vcount_sine);
|
s_asx.tex_x = (1000 * 1000) + (vcount_sine);
|
||||||
_asx.tex_y = (1000 * 1000);
|
s_asx.tex_y = (1000 * 1000);
|
||||||
_asx.sx = 128;
|
s_asx.sx = 128;
|
||||||
_asx.sy = 128;
|
s_asx.sy = 128;
|
||||||
_asx.alpha = vcount_sine + (timer_s32 / ANIMATION_SPEED_DIVISOR);
|
s_asx.alpha = vcount_sine + (timer_s32 / ANIMATION_SPEED_DIVISOR);
|
||||||
|
|
||||||
bg_rotscale_ex(&_bgaff_arr[vcount], &_asx);
|
bg_rotscale_ex(&s_bgaff_arr[vcount], &s_asx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HBLANK occurs after the scanline so REG_VCOUNT represents the
|
/* HBLANK occurs after the scanline so REG_VCOUNT represents the
|
||||||
* the scanline that just passed, so when it's SCREEN_HEIGHT we will
|
* the scanline that just passed, so when it's SCREEN_HEIGHT we will
|
||||||
* actually be updating the first line
|
* actually be updating the first line
|
||||||
*/
|
*/
|
||||||
_bgaff_arr[SCREEN_HEIGHT] = _bgaff_arr[0];
|
s_bgaff_arr[SCREEN_HEIGHT] = s_bgaff_arr[0];
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-12
@@ -36,16 +36,16 @@ static const unsigned short* blind_gfxPal[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Bitfields storing blinds we have yet to beat during the current run
|
// Bitfields storing blinds we have yet to beat during the current run
|
||||||
static List unbeaten_boss_blinds;
|
static List s_unbeaten_boss_blinds;
|
||||||
static List unbeaten_showdown_blinds;
|
static List s_unbeaten_showdown_blinds;
|
||||||
|
|
||||||
// Maps the ante number to the base blind requirement for that ante.
|
// Maps the ante number to the base blind requirement for that ante.
|
||||||
// The game starts at ante 1 which is at index 1 for base requirement 300.
|
// The game starts at ante 1 which is at index 1 for base requirement 300.
|
||||||
// Ante 0 is also there in case it is ever reached.
|
// Ante 0 is also there in case it is ever reached.
|
||||||
static const u32 ante_lut[] = {100, 300, 800, 2000, 5000, 11000, 20000, 35000, 50000};
|
static const u32 ANTE_LUT[] = {100, 300, 800, 2000, 5000, 11000, 20000, 35000, 50000};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static Blind _blind_type_map[BLIND_TYPE_MAX] = {
|
static Blind s_blind_type_map[BLIND_TYPE_MAX] = {
|
||||||
{BLIND_TYPE_SMALL, FIX_ONE, 3},
|
{BLIND_TYPE_SMALL, FIX_ONE, 3},
|
||||||
{BLIND_TYPE_BIG, (FIX_ONE * 3) / 2, 4},
|
{BLIND_TYPE_BIG, (FIX_ONE * 3) / 2, 4},
|
||||||
{BLIND_TYPE_HOOK, FIX_ONE * 2, 5},
|
{BLIND_TYPE_HOOK, FIX_ONE * 2, 5},
|
||||||
@@ -94,12 +94,12 @@ u32 blind_get_requirement(enum BlindType type, int ante)
|
|||||||
if (ante < 0 || ante > MAX_ANTE)
|
if (ante < 0 || ante > MAX_ANTE)
|
||||||
ante = 0;
|
ante = 0;
|
||||||
|
|
||||||
return fx2int(_blind_type_map[type].score_req_multipler * ante_lut[ante]);
|
return fx2int(s_blind_type_map[type].score_req_multipler * ANTE_LUT[ante]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int blind_get_reward(enum BlindType type)
|
int blind_get_reward(enum BlindType type)
|
||||||
{
|
{
|
||||||
return _blind_type_map[type].reward;
|
return s_blind_type_map[type].reward;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fills the unbeaten boss blinds lists
|
// Fills the unbeaten boss blinds lists
|
||||||
@@ -111,11 +111,11 @@ void init_unbeaten_blinds_list(bool showdown)
|
|||||||
if (!init)
|
if (!init)
|
||||||
{
|
{
|
||||||
init = true;
|
init = true;
|
||||||
unbeaten_showdown_blinds = list_init();
|
s_unbeaten_showdown_blinds = list_init();
|
||||||
unbeaten_boss_blinds = list_init();
|
s_unbeaten_boss_blinds = list_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
List* p_unbeaten_blinds = showdown ? &unbeaten_showdown_blinds : &unbeaten_boss_blinds;
|
List* p_unbeaten_blinds = showdown ? &s_unbeaten_showdown_blinds : &s_unbeaten_boss_blinds;
|
||||||
|
|
||||||
// empty the list just to be sure
|
// empty the list just to be sure
|
||||||
list_clear(p_unbeaten_blinds);
|
list_clear(p_unbeaten_blinds);
|
||||||
@@ -125,13 +125,13 @@ void init_unbeaten_blinds_list(bool showdown)
|
|||||||
|
|
||||||
for (int i = lower_blind; i <= upper_blind; i++)
|
for (int i = lower_blind; i <= upper_blind; i++)
|
||||||
{
|
{
|
||||||
list_push_back(p_unbeaten_blinds, &_blind_type_map[i]);
|
list_push_back(p_unbeaten_blinds, &s_blind_type_map[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum BlindType roll_blind_type(bool showdown)
|
enum BlindType roll_blind_type(bool showdown)
|
||||||
{
|
{
|
||||||
List* p_unbeaten_blinds = showdown ? &unbeaten_showdown_blinds : &unbeaten_boss_blinds;
|
List* p_unbeaten_blinds = showdown ? &s_unbeaten_showdown_blinds : &s_unbeaten_boss_blinds;
|
||||||
|
|
||||||
// Fill the list with all blinds if it is empty
|
// Fill the list with all blinds if it is empty
|
||||||
// (happens on startup or if we have beaten all blinds)
|
// (happens on startup or if we have beaten all blinds)
|
||||||
@@ -150,7 +150,7 @@ enum BlindType roll_blind_type(bool showdown)
|
|||||||
void set_blind_beaten(enum BlindType type)
|
void set_blind_beaten(enum BlindType type)
|
||||||
{
|
{
|
||||||
bool showdown = (type >= BLIND_TYPE_SHOWDOWN);
|
bool showdown = (type >= BLIND_TYPE_SHOWDOWN);
|
||||||
List* p_unbeaten_blinds = showdown ? &unbeaten_showdown_blinds : &unbeaten_boss_blinds;
|
List* p_unbeaten_blinds = showdown ? &s_unbeaten_showdown_blinds : &s_unbeaten_boss_blinds;
|
||||||
|
|
||||||
// find the beaten blind idx in the list
|
// find the beaten blind idx in the list
|
||||||
int beaten_idx = 0;
|
int beaten_idx = 0;
|
||||||
|
|||||||
+41
-19
@@ -1,6 +1,8 @@
|
|||||||
#include "card.h"
|
#include "card.h"
|
||||||
|
|
||||||
#include "graphic_utils.h"
|
#include "graphic_utils.h"
|
||||||
|
#include "item.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#include <maxmod.h>
|
#include <maxmod.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -17,17 +19,17 @@
|
|||||||
|
|
||||||
// Card sprites lookup table. First index is the suit, second index is the rank. The value is the
|
// Card sprites lookup table. First index is the suit, second index is the rank. The value is the
|
||||||
// tile index.
|
// tile index.
|
||||||
const static u16 _card_sprite_lut[NUM_SUITS][NUM_RANKS] = {
|
const static u16 CARD_SPRITE_LUT[NUM_SUITS][NUM_RANKS] = {
|
||||||
{0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192},
|
{0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192},
|
||||||
{208, 224, 240, 256, 272, 288, 304, 320, 336, 352, 368, 384, 400},
|
{208, 224, 240, 256, 272, 288, 304, 320, 336, 352, 368, 384, 400},
|
||||||
{416, 432, 448, 464, 480, 496, 512, 528, 544, 560, 576, 592, 608},
|
{416, 432, 448, 464, 480, 496, 512, 528, 544, 560, 576, 592, 608},
|
||||||
{624, 640, 656, 672, 688, 704, 720, 736, 752, 768, 784, 800, 816}
|
{624, 640, 656, 672, 688, 704, 720, 736, 752, 768, 784, 800, 816}
|
||||||
};
|
};
|
||||||
// Deck sprites lookup table. Index is the deck Id. The value is the tile index.
|
// Deck sprites lookup table. Index is the deck Id. The value is the tile index.
|
||||||
const static u16 _deck_sprite_lut[DECK_TYPE_MAX] = {0, 16, 32, 48, 64, 80};
|
const static u16 DECK_SPRITE_LUT[DECK_TYPE_MAX] = {0, 16, 32, 48, 64, 80};
|
||||||
|
|
||||||
bool high_contrast = DEFAULT_HIGH_CONTRAST;
|
static bool s_high_contrast = DEFAULT_HIGH_CONTRAST;
|
||||||
bool more_readable = DEFAULT_MORE_READABLE;
|
static bool s_more_readable = DEFAULT_MORE_READABLE;
|
||||||
|
|
||||||
void card_init()
|
void card_init()
|
||||||
{
|
{
|
||||||
@@ -36,8 +38,8 @@ void card_init()
|
|||||||
|
|
||||||
void set_cards_high_contrast(bool enable)
|
void set_cards_high_contrast(bool enable)
|
||||||
{
|
{
|
||||||
high_contrast = enable;
|
s_high_contrast = enable;
|
||||||
if (high_contrast)
|
if (s_high_contrast)
|
||||||
{
|
{
|
||||||
GRIT_CPY(&pal_obj_mem[CARD_PB * PAL_ROW_LEN], high_contrast_deck_pal_gfxPal);
|
GRIT_CPY(&pal_obj_mem[CARD_PB * PAL_ROW_LEN], high_contrast_deck_pal_gfxPal);
|
||||||
}
|
}
|
||||||
@@ -49,17 +51,17 @@ void set_cards_high_contrast(bool enable)
|
|||||||
|
|
||||||
void set_cards_more_readable(bool enable)
|
void set_cards_more_readable(bool enable)
|
||||||
{
|
{
|
||||||
more_readable = enable;
|
s_more_readable = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_cards_high_contrast(void)
|
bool get_cards_high_contrast(void)
|
||||||
{
|
{
|
||||||
return high_contrast;
|
return s_high_contrast;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_cards_more_readable(void)
|
bool get_cards_more_readable(void)
|
||||||
{
|
{
|
||||||
return more_readable;
|
return s_more_readable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Card methods
|
// Card methods
|
||||||
@@ -101,9 +103,10 @@ u8 card_get_value(Card* card)
|
|||||||
CardObject* card_object_new(Card* card)
|
CardObject* card_object_new(Card* card)
|
||||||
{
|
{
|
||||||
CardObject* card_object = POOL_GET(CardObject);
|
CardObject* card_object = POOL_GET(CardObject);
|
||||||
|
sprite_object_init((SpriteObject*)card_object);
|
||||||
card_object->card = card;
|
card_object->card = card;
|
||||||
card_object->sprite_object = sprite_object_new();
|
|
||||||
|
card_object->type = ITEM_TYPE_PLAYING_CARD;
|
||||||
card_object->selected = false;
|
card_object->selected = false;
|
||||||
|
|
||||||
return card_object;
|
return card_object;
|
||||||
@@ -113,7 +116,7 @@ void card_object_destroy(CardObject** card_object)
|
|||||||
{
|
{
|
||||||
if (*card_object == NULL)
|
if (*card_object == NULL)
|
||||||
return;
|
return;
|
||||||
sprite_object_destroy(&((*card_object)->sprite_object));
|
sprite_object_destroy((SpriteObject*)(*card_object));
|
||||||
POOL_FREE(CardObject, *card_object);
|
POOL_FREE(CardObject, *card_object);
|
||||||
*card_object = NULL;
|
*card_object = NULL;
|
||||||
}
|
}
|
||||||
@@ -121,10 +124,10 @@ void card_object_destroy(CardObject** card_object)
|
|||||||
void card_object_set_sprite(CardObject* card_object, int layer)
|
void card_object_set_sprite(CardObject* card_object, int layer)
|
||||||
{
|
{
|
||||||
int tile_index = CARD_TID + (layer * CARD_SPRITE_OFFSET);
|
int tile_index = CARD_TID + (layer * CARD_SPRITE_OFFSET);
|
||||||
const unsigned int* card_tiles = more_readable ? deck_big_gfxTiles : deck_gfxTiles;
|
const unsigned int* card_tiles = s_more_readable ? deck_big_gfxTiles : deck_gfxTiles;
|
||||||
memcpy32(
|
memcpy32(
|
||||||
&tile_mem[TILE_MEM_OBJ_CHARBLOCK0_IDX][tile_index],
|
&tile_mem[TILE_MEM_OBJ_CHARBLOCK0_IDX][tile_index],
|
||||||
&card_tiles[_card_sprite_lut[card_object->card->suit][card_object->card->rank] * TILE_SIZE],
|
&card_tiles[CARD_SPRITE_LUT[card_object->card->suit][card_object->card->rank] * TILE_SIZE],
|
||||||
TILE_SIZE * CARD_SPRITE_OFFSET
|
TILE_SIZE * CARD_SPRITE_OFFSET
|
||||||
);
|
);
|
||||||
Sprite* sprite = sprite_new(
|
Sprite* sprite = sprite_new(
|
||||||
@@ -134,7 +137,7 @@ void card_object_set_sprite(CardObject* card_object, int layer)
|
|||||||
CARD_PB,
|
CARD_PB,
|
||||||
layer + CARD_STARTING_LAYER
|
layer + CARD_STARTING_LAYER
|
||||||
);
|
);
|
||||||
sprite_object_set_sprite(card_object->sprite_object, sprite);
|
sprite_object_set_sprite((SpriteObject*)card_object, sprite);
|
||||||
}
|
}
|
||||||
|
|
||||||
void card_object_set_sprite_face_down(CardObject* card_object, enum DeckType deck, int layer)
|
void card_object_set_sprite_face_down(CardObject* card_object, enum DeckType deck, int layer)
|
||||||
@@ -142,7 +145,7 @@ void card_object_set_sprite_face_down(CardObject* card_object, enum DeckType dec
|
|||||||
int tile_index = CARD_TID + (layer * CARD_SPRITE_OFFSET);
|
int tile_index = CARD_TID + (layer * CARD_SPRITE_OFFSET);
|
||||||
memcpy32(
|
memcpy32(
|
||||||
&tile_mem[TILE_MEM_OBJ_CHARBLOCK0_IDX][tile_index],
|
&tile_mem[TILE_MEM_OBJ_CHARBLOCK0_IDX][tile_index],
|
||||||
&decks_face_down_gfxTiles[_deck_sprite_lut[deck] * TILE_SIZE],
|
&decks_face_down_gfxTiles[DECK_SPRITE_LUT[deck] * TILE_SIZE],
|
||||||
TILE_SIZE * CARD_SPRITE_OFFSET
|
TILE_SIZE * CARD_SPRITE_OFFSET
|
||||||
);
|
);
|
||||||
Sprite* sprite = sprite_new(
|
Sprite* sprite = sprite_new(
|
||||||
@@ -152,12 +155,12 @@ void card_object_set_sprite_face_down(CardObject* card_object, enum DeckType dec
|
|||||||
DECK_SPRITES_PB,
|
DECK_SPRITES_PB,
|
||||||
layer + CARD_STARTING_LAYER
|
layer + CARD_STARTING_LAYER
|
||||||
);
|
);
|
||||||
sprite_object_set_sprite(card_object->sprite_object, sprite);
|
sprite_object_set_sprite((SpriteObject*)card_object, sprite);
|
||||||
}
|
}
|
||||||
|
|
||||||
void card_object_shake(CardObject* card_object, mm_word sound_id)
|
void card_object_shake(CardObject* card_object, mm_word sound_id)
|
||||||
{
|
{
|
||||||
sprite_object_shake(card_object->sprite_object, sound_id);
|
sprite_object_shake((SpriteObject*)card_object, sound_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void card_object_set_selected(CardObject* card_object, bool selected)
|
void card_object_set_selected(CardObject* card_object, bool selected)
|
||||||
@@ -178,5 +181,24 @@ Sprite* card_object_get_sprite(CardObject* card_object)
|
|||||||
{
|
{
|
||||||
if (card_object == NULL)
|
if (card_object == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
return sprite_object_get_sprite(card_object->sprite_object);
|
return sprite_object_get_sprite((SpriteObject*)card_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
int card_object_get_buy_price(Item* card_object)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_RET(card_object, UNDEFINED);
|
||||||
|
ITEM_RETURN_IF_UNEXPECTED_TYPE_RET(card_object, ITEM_TYPE_PLAYING_CARD, UNDEFINED);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void card_object_dispose(Item** card_object_item)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(card_object_item);
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(*card_object_item);
|
||||||
|
ITEM_RETURN_IF_UNEXPECTED_TYPE_VOID(*card_object_item, ITEM_TYPE_PLAYING_CARD);
|
||||||
|
|
||||||
|
CardObject* card_object = (CardObject*)(*card_object_item);
|
||||||
|
card_object_destroy(&card_object);
|
||||||
|
*card_object_item = NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#include "chinese_text.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define CHINESE_GLYPH_FIRST 128
|
||||||
|
|
||||||
|
static const uint32_t s_chinese_codepoints[] = {
|
||||||
|
#define CHINESE_GLYPH(codepoint) codepoint,
|
||||||
|
#include "def_chinese_glyph_table.h"
|
||||||
|
#undef CHINESE_GLYPH
|
||||||
|
};
|
||||||
|
|
||||||
|
_Static_assert(
|
||||||
|
sizeof(s_chinese_codepoints) / sizeof(s_chinese_codepoints[0]) <= 256 - CHINESE_GLYPH_FIRST,
|
||||||
|
"Chinese glyph table exceeds the single-byte encoding range"
|
||||||
|
);
|
||||||
|
|
||||||
|
static uint32_t decode_utf8_codepoint(const unsigned char** cursor)
|
||||||
|
{
|
||||||
|
const unsigned char* text = *cursor;
|
||||||
|
|
||||||
|
if (text[0] < 0x80)
|
||||||
|
{
|
||||||
|
*cursor += 1;
|
||||||
|
return text[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((text[0] & 0xE0) == 0xC0 && (text[1] & 0xC0) == 0x80)
|
||||||
|
{
|
||||||
|
*cursor += 2;
|
||||||
|
return ((uint32_t)(text[0] & 0x1F) << 6) | (text[1] & 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((text[0] & 0xF0) == 0xE0 && (text[1] & 0xC0) == 0x80 && (text[2] & 0xC0) == 0x80)
|
||||||
|
{
|
||||||
|
*cursor += 3;
|
||||||
|
return ((uint32_t)(text[0] & 0x0F) << 12) | ((uint32_t)(text[1] & 0x3F) << 6) |
|
||||||
|
(text[2] & 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
*cursor += 1;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t codepoint_to_glyph(uint32_t codepoint)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof(s_chinese_codepoints) / sizeof(s_chinese_codepoints[0]); i++)
|
||||||
|
{
|
||||||
|
if (s_chinese_codepoints[i] == codepoint)
|
||||||
|
{
|
||||||
|
return CHINESE_GLYPH_FIRST + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t encode_tte_codepoint(uint32_t codepoint, char* output, size_t output_size)
|
||||||
|
{
|
||||||
|
if (codepoint < 0x80)
|
||||||
|
{
|
||||||
|
if (output_size < 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
output[0] = (char)codepoint;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codepoint <= 0x7FF)
|
||||||
|
{
|
||||||
|
if (output_size < 2)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
output[0] = (char)(0xC0 | (codepoint >> 6));
|
||||||
|
output[1] = (char)(0x80 | (codepoint & 0x3F));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t chinese_text_encode(const char* utf8, char* output, size_t output_size)
|
||||||
|
{
|
||||||
|
if (utf8 == NULL || output == NULL || output_size == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned char* cursor = (const unsigned char*)utf8;
|
||||||
|
size_t output_len = 0;
|
||||||
|
|
||||||
|
while (*cursor != '\0')
|
||||||
|
{
|
||||||
|
uint32_t codepoint = decode_utf8_codepoint(&cursor);
|
||||||
|
uint32_t tte_codepoint = codepoint < 0x80 ? codepoint : codepoint_to_glyph(codepoint);
|
||||||
|
size_t encoded_size =
|
||||||
|
encode_tte_codepoint(tte_codepoint, output + output_len, output_size - output_len - 1);
|
||||||
|
|
||||||
|
if (encoded_size == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_len += encoded_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
output[output_len] = '\0';
|
||||||
|
return output_len;
|
||||||
|
}
|
||||||
+2
-2
@@ -14,7 +14,7 @@
|
|||||||
/**
|
/**
|
||||||
* @brief List of DeckType names, all formatted to be centered in a string of 13 characters.
|
* @brief List of DeckType names, all formatted to be centered in a string of 13 characters.
|
||||||
*/
|
*/
|
||||||
static const char deck_names[DECK_TYPE_MAX][DECK_NAME_LENGTH] = {
|
static const char DECK_NAMES[DECK_TYPE_MAX][DECK_NAME_LENGTH] = {
|
||||||
" Red Deck ",
|
" Red Deck ",
|
||||||
" Blue Deck ",
|
" Blue Deck ",
|
||||||
" Yellow Deck ",
|
" Yellow Deck ",
|
||||||
@@ -42,7 +42,7 @@ static const PrintDescCallback deck_description_functions[DECK_TYPE_MAX] = {
|
|||||||
|
|
||||||
void print_deck_name(enum DeckType deck, BG_POINT pos)
|
void print_deck_name(enum DeckType deck, BG_POINT pos)
|
||||||
{
|
{
|
||||||
tte_printf("#{P:%d,%d; cx:0x%X000}%s", pos.x, pos.y, TTE_WHITE_PB, deck_names[deck]);
|
tte_printf("#{P:%d,%d; cx:0x%X000}%s", pos.x, pos.y, TTE_WHITE_PB, DECK_NAMES[deck]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_deck_description(enum DeckType deck, BG_POINT pos)
|
void print_deck_description(enum DeckType deck, BG_POINT pos)
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
static const char* s_font_point_lookup[] = {
|
static const char* FONT_POINT_LOOKUP[] = {
|
||||||
FP0_STR,
|
FP0_STR,
|
||||||
FP1_STR,
|
FP1_STR,
|
||||||
FP2_STR,
|
FP2_STR,
|
||||||
@@ -20,7 +20,7 @@ static const char* s_font_point_lookup[] = {
|
|||||||
const char* get_font_point_str(int val)
|
const char* get_font_point_str(int val)
|
||||||
{
|
{
|
||||||
val = abs(val) % 10;
|
val = abs(val) % 10;
|
||||||
return s_font_point_lookup[val];
|
return FONT_POINT_LOOKUP[val];
|
||||||
}
|
}
|
||||||
|
|
||||||
char digit_char_to_font_point(char digit_char)
|
char digit_char_to_font_point(char digit_char)
|
||||||
|
|||||||
+70
-52
@@ -9,6 +9,7 @@
|
|||||||
#include "game/blind_select.h"
|
#include "game/blind_select.h"
|
||||||
#include "game/common_ui.h"
|
#include "game/common_ui.h"
|
||||||
#include "game/game_over.h"
|
#include "game/game_over.h"
|
||||||
|
#include "game/joker_gallery.h"
|
||||||
#include "game/joker_row.h"
|
#include "game/joker_row.h"
|
||||||
#include "game/main_menu.h"
|
#include "game/main_menu.h"
|
||||||
#include "game/options_menu.h"
|
#include "game/options_menu.h"
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
#include <maxmod.h>
|
#include <maxmod.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define STRAIGHT_AND_FLUSH_SIZE_FOUR_FINGERS 4
|
#define STRAIGHT_AND_FLUSH_SIZE_FOUR_FINGERS 4
|
||||||
#define STRAIGHT_AND_FLUSH_SIZE_DEFAULT 5
|
#define STRAIGHT_AND_FLUSH_SIZE_DEFAULT 5
|
||||||
@@ -95,11 +97,15 @@ static StateMachine game_sm = STATE_MACHINE_DEFINE(state_info, GAME_STATE_MAX);
|
|||||||
// Initialization of the global vars
|
// Initialization of the global vars
|
||||||
// clang-format off
|
// clang-format off
|
||||||
GameVariables g_game_vars = {
|
GameVariables g_game_vars = {
|
||||||
.timer = 0, .rng_info = {0, 0},
|
.timer = 0,
|
||||||
|
// Setting the seed to an invalid value so that the Run Setup screen knows we're not reusing a previous Run's seed
|
||||||
|
.rng_info = {UNDEFINED, 0},
|
||||||
|
|
||||||
.round = 0, .ante = 0, .money = 0, .hand_size = DEFAULT_HAND_SIZE,
|
.round = 0, .ante = 0, .money = 0, .hand_size = DEFAULT_HAND_SIZE,
|
||||||
.deck = DECK_TYPE_RED,
|
.deck = DECK_TYPE_RED,
|
||||||
|
|
||||||
|
.best_hand_score = 0, .nb_played_hands = {0},
|
||||||
|
|
||||||
.current_blind = BLIND_TYPE_SMALL,
|
.current_blind = BLIND_TYPE_SMALL,
|
||||||
.next_boss_blind = BLIND_TYPE_BIG,
|
.next_boss_blind = BLIND_TYPE_BIG,
|
||||||
.blinds_states =
|
.blinds_states =
|
||||||
@@ -124,31 +130,31 @@ GameVariables g_game_vars = {
|
|||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
static List _owned_jokers_list;
|
static List s_owned_jokers_list;
|
||||||
static List _discarded_jokers_list;
|
static List s_discarded_jokers_list;
|
||||||
static List _expired_jokers_list;
|
static List s_expired_jokers_list;
|
||||||
|
|
||||||
// Stacks
|
// Stacks
|
||||||
static Card* deck[MAX_DECK_SIZE] = {NULL};
|
static Card* s_deck[MAX_DECK_SIZE] = {NULL};
|
||||||
static int deck_top = -1;
|
static int s_deck_top = -1;
|
||||||
|
|
||||||
// Joker Special Variables
|
// Joker Special Variables
|
||||||
static int shortcut_joker_count = 0;
|
static int s_shortcut_joker_count = 0;
|
||||||
|
|
||||||
static int four_fingers_joker_count = 0;
|
static int s_four_fingers_joker_count = 0;
|
||||||
|
|
||||||
void deck_push(Card* card)
|
void deck_push(Card* card)
|
||||||
{
|
{
|
||||||
if (deck_top >= MAX_DECK_SIZE - 1)
|
if (s_deck_top >= MAX_DECK_SIZE - 1)
|
||||||
return;
|
return;
|
||||||
deck[++deck_top] = card;
|
s_deck[++s_deck_top] = card;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card* deck_pop(void)
|
Card* deck_pop(void)
|
||||||
{
|
{
|
||||||
if (deck_top < 0)
|
if (s_deck_top < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
return deck[deck_top--];
|
return s_deck[s_deck_top--];
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_ante(void)
|
void display_ante(void)
|
||||||
@@ -169,9 +175,9 @@ void game_init()
|
|||||||
state_machine_remove(&game_sm);
|
state_machine_remove(&game_sm);
|
||||||
state_machine_register(&game_sm);
|
state_machine_register(&game_sm);
|
||||||
// Initialize all jokers list once
|
// Initialize all jokers list once
|
||||||
_owned_jokers_list = list_init();
|
s_owned_jokers_list = list_init();
|
||||||
_discarded_jokers_list = list_init();
|
s_discarded_jokers_list = list_init();
|
||||||
_expired_jokers_list = list_init();
|
s_expired_jokers_list = list_init();
|
||||||
// TODO: Move this to an initialization of the play scoring states
|
// TODO: Move this to an initialization of the play scoring states
|
||||||
|
|
||||||
game_shop_reset();
|
game_shop_reset();
|
||||||
@@ -186,10 +192,15 @@ void game_init()
|
|||||||
g_game_vars.ante = STARTING_ANTE;
|
g_game_vars.ante = STARTING_ANTE;
|
||||||
g_game_vars.money = STARTING_MONEY;
|
g_game_vars.money = STARTING_MONEY;
|
||||||
g_game_vars.score = STARTING_SCORE;
|
g_game_vars.score = STARTING_SCORE;
|
||||||
|
g_game_vars.round = 0;
|
||||||
g_game_vars.chips = 0;
|
g_game_vars.chips = 0;
|
||||||
g_game_vars.mult = 0;
|
g_game_vars.mult = 0;
|
||||||
g_game_vars.round = STARTING_ROUND;
|
g_game_vars.round = STARTING_ROUND;
|
||||||
|
|
||||||
|
g_game_vars.best_hand_score = 0;
|
||||||
|
for (int i = 0; i < HAND_TYPE_MAX; i++)
|
||||||
|
g_game_vars.nb_played_hands[i] = 0;
|
||||||
|
|
||||||
// Initialize/reset unbeaten Boss/Showdown Blinds so they are all available
|
// Initialize/reset unbeaten Boss/Showdown Blinds so they are all available
|
||||||
init_unbeaten_blinds_list(false);
|
init_unbeaten_blinds_list(false);
|
||||||
init_unbeaten_blinds_list(true);
|
init_unbeaten_blinds_list(true);
|
||||||
@@ -197,9 +208,9 @@ void game_init()
|
|||||||
|
|
||||||
void game_reset()
|
void game_reset()
|
||||||
{
|
{
|
||||||
while (list_get_len(&_owned_jokers_list) > 0)
|
while (list_get_len(&s_owned_jokers_list) > 0)
|
||||||
{
|
{
|
||||||
JokerObject* joker_object = list_get_at_idx(&_owned_jokers_list, 0);
|
JokerObject* joker_object = list_get_at_idx(&s_owned_jokers_list, 0);
|
||||||
remove_owned_joker(0);
|
remove_owned_joker(0);
|
||||||
joker_object_destroy(&joker_object);
|
joker_object_destroy(&joker_object);
|
||||||
}
|
}
|
||||||
@@ -212,9 +223,9 @@ void game_reset()
|
|||||||
sprite_destroy(&g_game_vars.playing_blind_token);
|
sprite_destroy(&g_game_vars.playing_blind_token);
|
||||||
sprite_destroy(&g_game_vars.round_end_blind_token);
|
sprite_destroy(&g_game_vars.round_end_blind_token);
|
||||||
|
|
||||||
list_clear(&_owned_jokers_list);
|
list_clear(&s_owned_jokers_list);
|
||||||
list_clear(&_discarded_jokers_list);
|
list_clear(&s_discarded_jokers_list);
|
||||||
list_clear(&_expired_jokers_list);
|
list_clear(&s_expired_jokers_list);
|
||||||
|
|
||||||
game_init();
|
game_init();
|
||||||
|
|
||||||
@@ -233,20 +244,20 @@ void game_reset()
|
|||||||
|
|
||||||
static inline void discarded_jokers_update_loop(void)
|
static inline void discarded_jokers_update_loop(void)
|
||||||
{
|
{
|
||||||
if (list_is_empty(&_discarded_jokers_list))
|
if (list_is_empty(&s_discarded_jokers_list))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListItr itr = list_itr_create(&_discarded_jokers_list);
|
ListItr itr = list_itr_create(&s_discarded_jokers_list);
|
||||||
JokerObject* joker_object;
|
JokerObject* joker_object;
|
||||||
|
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
if (joker_object->sprite_object->x == joker_object->sprite_object->tx &&
|
if (joker_object->x == joker_object->tx && joker_object->y == joker_object->ty)
|
||||||
joker_object->sprite_object->y == joker_object->sprite_object->ty)
|
|
||||||
{
|
{
|
||||||
list_itr_remove_current_node(&itr);
|
list_itr_remove_current_node(&itr);
|
||||||
|
// TODO: joker_object_dispose() instead?
|
||||||
joker_object_destroy(&joker_object);
|
joker_object_destroy(&joker_object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,7 +265,7 @@ static inline void discarded_jokers_update_loop(void)
|
|||||||
|
|
||||||
static inline void held_jokers_update_loop(void)
|
static inline void held_jokers_update_loop(void)
|
||||||
{
|
{
|
||||||
static const int spacing_lut[MAX_JOKERS_HELD_SIZE][MAX_JOKERS_HELD_SIZE] = {
|
static const int SPACING_LUT[MAX_JOKERS_HELD_SIZE][MAX_JOKERS_HELD_SIZE] = {
|
||||||
{0, 0, 0, 0, 0 },
|
{0, 0, 0, 0, 0 },
|
||||||
{13, -13, 0, 0, 0 },
|
{13, -13, 0, 0, 0 },
|
||||||
{26, 0, -26, 0, 0 },
|
{26, 0, -26, 0, 0 },
|
||||||
@@ -264,27 +275,33 @@ static inline void held_jokers_update_loop(void)
|
|||||||
|
|
||||||
FIXED hand_x = int2fx(HELD_JOKERS_POS.x);
|
FIXED hand_x = int2fx(HELD_JOKERS_POS.x);
|
||||||
|
|
||||||
ListItr itr = list_itr_create(&_owned_jokers_list);
|
ListItr itr = list_itr_create(&s_owned_jokers_list);
|
||||||
JokerObject* joker;
|
JokerObject* joker;
|
||||||
int jokers_top = list_get_len(&_owned_jokers_list) - 1;
|
int jokers_top = list_get_len(&s_owned_jokers_list) - 1;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while ((joker = list_itr_next(&itr)))
|
while ((joker = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
// Let the Shop handle the position of this Joker
|
// Let the Shop handle the position of this Joker
|
||||||
if (joker != game_shop_get_description_card())
|
if (joker != game_shop_get_description_card())
|
||||||
joker->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]);
|
joker->tx = hand_x - int2fx(SPACING_LUT[jokers_top][i]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool joker_object_can_acquire(Item* joker_object)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_RET(joker_object, false);
|
||||||
|
return (list_get_len(get_jokers_list()) < MAX_JOKERS_HELD_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void expired_jokers_update_loop(void)
|
static inline void expired_jokers_update_loop(void)
|
||||||
{
|
{
|
||||||
if (list_is_empty(&_expired_jokers_list))
|
if (list_is_empty(&s_expired_jokers_list))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListItr itr = list_itr_create(&_expired_jokers_list);
|
ListItr itr = list_itr_create(&s_expired_jokers_list);
|
||||||
JokerObject* joker_object;
|
JokerObject* joker_object;
|
||||||
|
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((joker_object = list_itr_next(&itr)))
|
||||||
@@ -294,7 +311,7 @@ static inline void expired_jokers_update_loop(void)
|
|||||||
{
|
{
|
||||||
// get joker idx
|
// get joker idx
|
||||||
int expired_joker_idx = 0;
|
int expired_joker_idx = 0;
|
||||||
ListItr joker_itr = list_itr_create(&_owned_jokers_list);
|
ListItr joker_itr = list_itr_create(&s_owned_jokers_list);
|
||||||
JokerObject* expired_joker;
|
JokerObject* expired_joker;
|
||||||
while ((expired_joker = list_itr_next(&joker_itr)) && expired_joker != joker_object)
|
while ((expired_joker = list_itr_next(&joker_itr)) && expired_joker != joker_object)
|
||||||
{
|
{
|
||||||
@@ -346,7 +363,7 @@ enum GameState game_get_state(void)
|
|||||||
|
|
||||||
bool is_joker_owned(int joker_id)
|
bool is_joker_owned(int joker_id)
|
||||||
{
|
{
|
||||||
ListItr itr = list_itr_create(&_owned_jokers_list);
|
ListItr itr = list_itr_create(&s_owned_jokers_list);
|
||||||
JokerObject* joker;
|
JokerObject* joker;
|
||||||
|
|
||||||
while ((joker = list_itr_next(&itr)))
|
while ((joker = list_itr_next(&itr)))
|
||||||
@@ -361,71 +378,72 @@ bool is_joker_owned(int joker_id)
|
|||||||
|
|
||||||
List* get_jokers_list(void)
|
List* get_jokers_list(void)
|
||||||
{
|
{
|
||||||
return &_owned_jokers_list;
|
return &s_owned_jokers_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
List* get_expired_jokers_list(void)
|
List* get_expired_jokers_list(void)
|
||||||
{
|
{
|
||||||
return &_expired_jokers_list;
|
return &s_expired_jokers_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
List* get_discarded_jokers_list(void)
|
List* get_discarded_jokers_list(void)
|
||||||
{
|
{
|
||||||
return &_discarded_jokers_list;
|
return &s_discarded_jokers_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_shortcut_joker_active(void)
|
bool is_shortcut_joker_active(void)
|
||||||
{
|
{
|
||||||
return shortcut_joker_count > 0;
|
return s_shortcut_joker_count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_straight_and_flush_size(void)
|
int get_straight_and_flush_size(void)
|
||||||
{
|
{
|
||||||
return four_fingers_joker_count > 0 ? STRAIGHT_AND_FLUSH_SIZE_FOUR_FINGERS
|
return s_four_fingers_joker_count > 0 ? STRAIGHT_AND_FLUSH_SIZE_FOUR_FINGERS
|
||||||
: STRAIGHT_AND_FLUSH_SIZE_DEFAULT;
|
: STRAIGHT_AND_FLUSH_SIZE_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_joker(JokerObject* joker_object)
|
void add_joker(JokerObject* joker_object)
|
||||||
{
|
{
|
||||||
list_push_back(&_owned_jokers_list, joker_object);
|
list_push_back(&s_owned_jokers_list, joker_object);
|
||||||
|
|
||||||
// TODO: Extract to on_joker_added() callback
|
// TODO: Extract to on_joker_added() callback
|
||||||
// In case the player gets multiple Four Fingers Jokers,
|
// In case the player gets multiple Four Fingers Jokers,
|
||||||
// only change size when the first one is added
|
// only change size when the first one is added
|
||||||
if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
|
if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
|
||||||
{
|
{
|
||||||
four_fingers_joker_count++;
|
s_four_fingers_joker_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (joker_object->joker->id == SHORTCUT_JOKER_ID)
|
if (joker_object->joker->id == SHORTCUT_JOKER_ID)
|
||||||
{
|
{
|
||||||
shortcut_joker_count++;
|
s_shortcut_joker_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_owned_joker(int owned_joker_idx)
|
void remove_owned_joker(int owned_joker_idx)
|
||||||
{
|
{
|
||||||
// TODO: Extract to on_joker_removed() callback
|
// TODO: Extract to on_joker_removed() callback
|
||||||
JokerObject* joker_object = list_get_at_idx(&_owned_jokers_list, owned_joker_idx);
|
JokerObject* joker_object = list_get_at_idx(&s_owned_jokers_list, owned_joker_idx);
|
||||||
// In case the player gets multiple Four Fingers Jokers,
|
// In case the player gets multiple Four Fingers Jokers,
|
||||||
// and only reset the size when all of them have been removed
|
// and only reset the size when all of them have been removed
|
||||||
if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
|
if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
|
||||||
{
|
{
|
||||||
four_fingers_joker_count--;
|
s_four_fingers_joker_count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (joker_object->joker->id == SHORTCUT_JOKER_ID)
|
if (joker_object->joker->id == SHORTCUT_JOKER_ID)
|
||||||
{
|
{
|
||||||
shortcut_joker_count--;
|
s_shortcut_joker_count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
game_shop_set_joker_avail(joker_object->joker->id, true);
|
// TODO: Move to site of joker_destroy()?
|
||||||
list_remove_at_idx(&_owned_jokers_list, owned_joker_idx);
|
joker_set_rollable(joker_object->joker->id, true);
|
||||||
|
list_remove_at_idx(&s_owned_jokers_list, owned_joker_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_deck_top(void)
|
int get_deck_top(void)
|
||||||
{
|
{
|
||||||
return deck_top;
|
return s_deck_top;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_num_discards_remaining(void)
|
int get_num_discards_remaining(void)
|
||||||
@@ -618,23 +636,23 @@ void display_discards(void)
|
|||||||
|
|
||||||
int deck_get_size(void)
|
int deck_get_size(void)
|
||||||
{
|
{
|
||||||
return deck_top + 1;
|
return s_deck_top + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int deck_get_max_size(void)
|
static int deck_get_max_size(void)
|
||||||
{
|
{
|
||||||
// This is the max amount of cards that the player currently has in their possession
|
// This is the max amount of cards that the player currently has in their possession
|
||||||
return get_hand_top() + get_played_top() + deck_top + get_discard_top() + 4;
|
return get_hand_top() + get_played_top() + s_deck_top + get_discard_top() + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
void deck_shuffle(void)
|
void deck_shuffle(void)
|
||||||
{
|
{
|
||||||
for (int i = deck_top; i > 0; i--)
|
for (int i = s_deck_top; i > 0; i--)
|
||||||
{
|
{
|
||||||
int j = rng_get_u32() % (i + 1);
|
int j = rng_get_u32() % (i + 1);
|
||||||
Card* temp = deck[i];
|
Card* temp = s_deck[i];
|
||||||
deck[i] = deck[j];
|
s_deck[i] = s_deck[j];
|
||||||
deck[j] = temp;
|
s_deck[j] = temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-15
@@ -25,7 +25,7 @@ static const u32 BLIND_SELECT_BTN_SELECTED_BORDER_PID = 18;
|
|||||||
static const u32 TM_DISP_BLIND_PANEL_FINISH = 7;
|
static const u32 TM_DISP_BLIND_PANEL_FINISH = 7;
|
||||||
static const u32 TM_DISP_BLIND_PANEL_START = 1;
|
static const u32 TM_DISP_BLIND_PANEL_START = 1;
|
||||||
|
|
||||||
static int timer;
|
static int s_timer;
|
||||||
|
|
||||||
static void game_blind_select_start_anim_seq(void);
|
static void game_blind_select_start_anim_seq(void);
|
||||||
static void game_blind_select_handle_input(void);
|
static void game_blind_select_handle_input(void);
|
||||||
@@ -97,11 +97,11 @@ static void game_blind_select_start_anim_seq()
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer == TM_END_ANIM_SEQ)
|
if (s_timer == TM_END_ANIM_SEQ)
|
||||||
{
|
{
|
||||||
game_blind_select_print_blinds_reqs_and_rewards();
|
game_blind_select_print_blinds_reqs_and_rewards();
|
||||||
state_machine_change_state(&blind_select_sm, BLIND_SELECT);
|
state_machine_change_state(&blind_select_sm, BLIND_SELECT);
|
||||||
timer = TM_ZERO; // Reset the timer
|
s_timer = TM_ZERO; // Reset the timer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ static inline void highlight_skip_button(void)
|
|||||||
|
|
||||||
static void game_blind_select_handle_input()
|
static void game_blind_select_handle_input()
|
||||||
{
|
{
|
||||||
if (timer == TM_BLIND_SELECT_START && g_game_vars.current_blind == BLIND_TYPE_BOSS)
|
if (s_timer == TM_BLIND_SELECT_START && g_game_vars.current_blind == BLIND_TYPE_BOSS)
|
||||||
{
|
{
|
||||||
selection_y = BLIND_ROW;
|
selection_y = BLIND_ROW;
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ static void game_blind_select_handle_input()
|
|||||||
case BLIND_ROW:
|
case BLIND_ROW:
|
||||||
play_sfx(SFX_BUTTON, MM_BASE_PITCH_RATE, BUTTON_SFX_VOLUME);
|
play_sfx(SFX_BUTTON, MM_BASE_PITCH_RATE, BUTTON_SFX_VOLUME);
|
||||||
state_machine_change_state(&blind_select_sm, BLIND_SELECTED_ANIM_SEQ);
|
state_machine_change_state(&blind_select_sm, BLIND_SELECTED_ANIM_SEQ);
|
||||||
timer = TM_ZERO;
|
s_timer = TM_ZERO;
|
||||||
++g_game_vars.round;
|
++g_game_vars.round;
|
||||||
display_round();
|
display_round();
|
||||||
break;
|
break;
|
||||||
@@ -230,7 +230,7 @@ static void game_blind_select_handle_input()
|
|||||||
game_blind_select_print_blinds_reqs_and_rewards();
|
game_blind_select_print_blinds_reqs_and_rewards();
|
||||||
highlight_select_button();
|
highlight_select_button();
|
||||||
|
|
||||||
timer = TM_ZERO;
|
s_timer = TM_ZERO;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -241,7 +241,7 @@ static void game_blind_select_handle_input()
|
|||||||
|
|
||||||
static void game_blind_select_selected_anim_seq()
|
static void game_blind_select_selected_anim_seq()
|
||||||
{
|
{
|
||||||
if (timer < 15)
|
if (s_timer < 15)
|
||||||
{
|
{
|
||||||
Rect blinds_rect = POP_MENU_ANIM_RECT;
|
Rect blinds_rect = POP_MENU_ANIM_RECT;
|
||||||
blinds_rect.top -= 1; // Because of the raised blind
|
blinds_rect.top -= 1; // Because of the raised blind
|
||||||
@@ -256,28 +256,28 @@ static void game_blind_select_selected_anim_seq()
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (timer >= MENU_POP_OUT_ANIM_FRAMES)
|
else if (s_timer >= MENU_POP_OUT_ANIM_FRAMES)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < NUM_BLINDS_PER_ANTE; i++)
|
for (int i = 0; i < NUM_BLINDS_PER_ANTE; i++)
|
||||||
{
|
{
|
||||||
obj_hide(blind_select_tokens[i]->obj);
|
obj_hide(blind_select_tokens[i]->obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
timer = TM_ZERO;
|
s_timer = TM_ZERO;
|
||||||
state_machine_change_state(&blind_select_sm, DISPLAY_BLIND_PANEL);
|
state_machine_change_state(&blind_select_sm, DISPLAY_BLIND_PANEL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void game_blind_select_display_blind_panel()
|
static void game_blind_select_display_blind_panel()
|
||||||
{
|
{
|
||||||
if (timer >= TM_DISP_BLIND_PANEL_FINISH)
|
if (s_timer >= TM_DISP_BLIND_PANEL_FINISH)
|
||||||
{
|
{
|
||||||
state_machine_change_state(&blind_select_sm, BLIND_SELECT_EXIT);
|
state_machine_change_state(&blind_select_sm, BLIND_SELECT_EXIT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switches to the selecting background and clears the blind panel area
|
// Switches to the selecting background and clears the blind panel area
|
||||||
if (timer == TM_DISP_BLIND_PANEL_START)
|
if (s_timer == TM_DISP_BLIND_PANEL_START)
|
||||||
{
|
{
|
||||||
change_background(BG_CARD_SELECTING, false);
|
change_background(BG_CARD_SELECTING, false);
|
||||||
|
|
||||||
@@ -295,9 +295,9 @@ static void game_blind_select_display_blind_panel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shift the blind panel down onto screen
|
// Shift the blind panel down onto screen
|
||||||
for (int y = 0; y < timer; y++)
|
for (int y = 0; y < s_timer; y++)
|
||||||
{
|
{
|
||||||
int y_from = 26 + y - timer;
|
int y_from = 26 + y - s_timer;
|
||||||
int y_to = 0 + y;
|
int y_to = 0 + y;
|
||||||
|
|
||||||
Rect from = {0, y_from, 8, y_from};
|
Rect from = {0, y_from, 8, y_from};
|
||||||
@@ -451,7 +451,7 @@ static void blind_tokens_init()
|
|||||||
|
|
||||||
void game_blind_select_on_init(void)
|
void game_blind_select_on_init(void)
|
||||||
{
|
{
|
||||||
timer = TM_ZERO;
|
s_timer = TM_ZERO;
|
||||||
state_machine_register(&blind_select_sm);
|
state_machine_register(&blind_select_sm);
|
||||||
state_machine_change_state(&blind_select_sm, START_ANIM_SEQ);
|
state_machine_change_state(&blind_select_sm, START_ANIM_SEQ);
|
||||||
|
|
||||||
@@ -472,7 +472,7 @@ void game_blind_select_on_init(void)
|
|||||||
|
|
||||||
void game_blind_select_on_update(void)
|
void game_blind_select_on_update(void)
|
||||||
{
|
{
|
||||||
timer++;
|
s_timer++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_blind_select_on_exit(void)
|
void game_blind_select_on_exit(void)
|
||||||
|
|||||||
+14
-5
@@ -8,10 +8,11 @@
|
|||||||
#include "game/round_end.h"
|
#include "game/round_end.h"
|
||||||
#include "game/run_setup.h"
|
#include "game/run_setup.h"
|
||||||
#include "game/shop.h"
|
#include "game/shop.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
typedef void (*BackgroundRenderCallback)(void);
|
typedef void (*BackgroundRenderCallback)(void);
|
||||||
|
|
||||||
static enum BackgroundId background = BG_NONE;
|
static enum BackgroundId s_background = BG_NONE;
|
||||||
|
|
||||||
// Map to fill in for refactor
|
// Map to fill in for refactor
|
||||||
static const BackgroundRenderCallback bgCallbacks[] = {
|
static const BackgroundRenderCallback bgCallbacks[] = {
|
||||||
@@ -28,18 +29,26 @@ static const BackgroundRenderCallback bgCallbacks[] = {
|
|||||||
|
|
||||||
enum BackgroundId get_current_background(void)
|
enum BackgroundId get_current_background(void)
|
||||||
{
|
{
|
||||||
return background;
|
return s_background;
|
||||||
}
|
}
|
||||||
|
|
||||||
void change_background(enum BackgroundId id, bool force_redraw)
|
void change_background(enum BackgroundId id, bool force_redraw)
|
||||||
{
|
{
|
||||||
if (force_redraw)
|
if (force_redraw)
|
||||||
{
|
{
|
||||||
background = BG_NONE;
|
s_background = BG_NONE;
|
||||||
}
|
}
|
||||||
if (id != background && bgCallbacks[id] != NULL)
|
if (id != s_background && bgCallbacks[id] != NULL)
|
||||||
{
|
{
|
||||||
bgCallbacks[id]();
|
bgCallbacks[id]();
|
||||||
}
|
}
|
||||||
background = id;
|
s_background = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_top_left_panel_bottom_row(void)
|
||||||
|
{
|
||||||
|
BG_POINT top_left_panel_bottom_row_pos = TOP_LEFT_PANEL_POINT;
|
||||||
|
// Use the source rect height to offset to the bottom row point
|
||||||
|
top_left_panel_bottom_row_pos.y += rect_height(&TOP_LEFT_ITEM_SRC_RECT) - 1;
|
||||||
|
main_bg_se_copy_rect(TOP_LEFT_PANEL_BOTTOM_ROW_RESET_RECT, top_left_panel_bottom_row_pos);
|
||||||
}
|
}
|
||||||
|
|||||||
+297
-75
@@ -2,17 +2,30 @@
|
|||||||
|
|
||||||
#include "affine_background.h"
|
#include "affine_background.h"
|
||||||
#include "audio_utils.h"
|
#include "audio_utils.h"
|
||||||
|
#include "background_game_over_gfx.h"
|
||||||
#include "button.h"
|
#include "button.h"
|
||||||
|
#include "card.h"
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "graphic_utils.h"
|
#include "graphic_utils.h"
|
||||||
|
#include "hand.h"
|
||||||
|
#include "joker.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
|
#include "list.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
|
#include "selection_grid.h"
|
||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <tonc.h>
|
#include <tonc.h>
|
||||||
|
|
||||||
|
#define RED_BTN_MAIN_COLOR_PAL_IDX 6
|
||||||
|
#define BLUE_BTN_MAIN_COLOR_PAL_IDX 7
|
||||||
|
#define REUSE_SEED_BTN_OUTLINE_PAL_IDX 8
|
||||||
|
#define NEW_RUN_BTN_OUTLINE_PAL_IDX 9
|
||||||
|
#define MAIN_MENU_BTN_OUTLINE_PAL_IDX 10
|
||||||
|
#define ENDLESS_BTN_OUTLINE_PAL_IDX 11
|
||||||
|
|
||||||
enum EndCondition
|
enum EndCondition
|
||||||
{
|
{
|
||||||
END_CONDITION_NONE,
|
END_CONDITION_NONE,
|
||||||
@@ -21,52 +34,158 @@ enum EndCondition
|
|||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const BG_POINT GAME_OVER_SRC_RECT_3X3_POS = { 25, 29};
|
static const Rect GAME_OVER_ANIM_RECT = { 3, 5, 26, 31};
|
||||||
static const Rect GAME_OVER_DIALOG_DEST_RECT = { 11, 21, 23, 28};
|
|
||||||
static const Rect GAME_OVER_ANIM_RECT = { 11, 8, 23, 28};
|
static const Rect GAME_OVER_TEXT_TILES_SRC_RECT_1 = { 27, 24, 31, 25};
|
||||||
static const Rect GAME_LOSE_MSG_TEXT_RECT = {104, 72, UNDEFINED, UNDEFINED};
|
static const Rect GAME_OVER_TEXT_TILES_SRC_RECT_2 = { 27, 26, 31, 27};
|
||||||
// 1 character to the right of GAME_LOSE
|
static const BG_POINT GAME_OVER_TEXT_TILES_DEST_POS_1 = { 10, 21};
|
||||||
static const Rect GAME_WIN_MSG_TEXT_RECT = {112, 72, UNDEFINED, UNDEFINED};
|
static const BG_POINT GAME_OVER_TEXT_TILES_DEST_POS_2 = { 15, 21};
|
||||||
static const BG_POINT NEW_RUN_BTN_DEST_POS = { 15, 26};
|
|
||||||
static const Rect NEW_RUN_BTN_SRC_RECT = { 0, 30, 4, 31};
|
static const Rect DEFEATED_BY_TEXT_SRC_RECT = { 28, 30, 31, 31};
|
||||||
|
static const BG_POINT DEFEATED_BY_TEXT_DEST_POS = { 22, 24};
|
||||||
|
static const BG_POINT DEFEATED_BY_TOKEN_INIT_POS = {180, 207};
|
||||||
|
|
||||||
|
static const Rect REUSE_SEED_BTN_OFF_SRC_RECT = { 28, 28, 29, 29};
|
||||||
|
static const Rect REUSE_SEED_BTN_ON_SRC_RECT = { 30, 28, 31, 29};
|
||||||
|
static const BG_POINT REUSE_SEED_BTN_DEST_POS = { 4, 11};
|
||||||
|
|
||||||
|
static const Rect BEST_HAND_SCORE_RECT = {126, 64, 166, 72};
|
||||||
|
static const Rect MOST_PLAYED_HAND_RECT = {112, 80, 168, 88};
|
||||||
|
static const BG_POINT SEED_VALUE_POS = {112, 96};
|
||||||
|
|
||||||
|
static const BG_POINT NEW_RUN_BTN_TEXT_POS = { 48, 112};
|
||||||
|
static const BG_POINT MAIN_MENU_BTN_TEXT_POS = {120, 112};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
static const u32 GAME_OVER_ANIM_FRAMES = 15;
|
static const u32 GAME_OVER_ANIM_FRAMES = 17;
|
||||||
static const mm_byte GAME_OVER_SFX_VOL = 178; // 70% of MM_SFX_FULL_VOLUME
|
static const mm_byte GAME_OVER_SFX_VOL = 178; // 70% of MM_SFX_FULL_VOLUME
|
||||||
static const mm_word GAME_OVER_WHOOSH_RATE = 922; // 90% of MM_BASE_PITCH_RATE
|
static const mm_word GAME_OVER_WHOOSH_RATE = 922; // 90% of MM_BASE_PITCH_RATE
|
||||||
|
|
||||||
static enum EndCondition condition = END_CONDITION_NONE;
|
// Selection Grid
|
||||||
static u32 timer = TM_ZERO;
|
|
||||||
|
|
||||||
static void game_over_init(enum EndCondition init_condition)
|
enum GameOverRows
|
||||||
{
|
{
|
||||||
condition = init_condition;
|
GAME_OVER_SEED_ROW,
|
||||||
// Clears the round end menu
|
GAME_OVER_RUN_MENU_ROW,
|
||||||
main_bg_se_clear_rect(POP_MENU_ANIM_RECT);
|
GAME_OVER_ROW_MAX
|
||||||
main_bg_se_copy_expand_3x3_rect(GAME_OVER_DIALOG_DEST_RECT, GAME_OVER_SRC_RECT_3X3_POS);
|
};
|
||||||
main_bg_se_copy_rect(NEW_RUN_BTN_SRC_RECT, NEW_RUN_BTN_DEST_POS);
|
|
||||||
|
|
||||||
// Using the text color to match the Win/Loss condition
|
#define GAME_OVER_SEED_BTN_COL 0
|
||||||
switch (condition)
|
#define GAME_OVER_NEW_RUN_BTN_COL 0
|
||||||
|
#define GAME_OVER_MAIN_MENU_BTN_COL 1
|
||||||
|
|
||||||
|
static int game_over_get_row_size1(void)
|
||||||
{
|
{
|
||||||
case END_CONDITION_WIN:
|
return 1;
|
||||||
affine_background_set_color(TEXT_CLR_BLUE);
|
}
|
||||||
break;
|
static int game_over_get_row_size2(void)
|
||||||
case END_CONDITION_LOSS:
|
{
|
||||||
affine_background_set_color(TEXT_CLR_RED);
|
return 2;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timer = TM_ZERO;
|
static void game_over_on_key_transit(SelectionGrid* selection_grid, Selection* selection);
|
||||||
|
static bool game_over_on_selection_changed(
|
||||||
|
SelectionGrid* selection_grid,
|
||||||
|
int row_idx,
|
||||||
|
const Selection* prev_selection,
|
||||||
|
const Selection* new_selection
|
||||||
|
);
|
||||||
|
|
||||||
|
static const SelectionGridRow game_over_selection_rows[] = {
|
||||||
|
{GAME_OVER_SEED_ROW, game_over_get_row_size1, game_over_on_selection_changed, game_over_on_key_transit, {.wrap = false}},
|
||||||
|
{GAME_OVER_RUN_MENU_ROW, game_over_get_row_size2, game_over_on_selection_changed, game_over_on_key_transit, {.wrap = false}}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Selection GAME_OVER_INIT_SEL = {0, GAME_OVER_RUN_MENU_ROW};
|
||||||
|
static SelectionGrid game_over_selection_grid = {
|
||||||
|
game_over_selection_rows,
|
||||||
|
GAME_OVER_ROW_MAX,
|
||||||
|
GAME_OVER_INIT_SEL
|
||||||
|
};
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
|
||||||
|
static void reuse_seed_on_pressed(void);
|
||||||
|
static void new_run_on_pressed(void);
|
||||||
|
static void main_menu_on_pressed(void);
|
||||||
|
static Button* game_over_get_button_from_sel(const Selection* selection);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
static Button game_over_buttons[GAME_OVER_ROW_MAX][2] = {
|
||||||
|
// GAME_OVER_SEED_ROW
|
||||||
|
{
|
||||||
|
{ REUSE_SEED_BTN_OUTLINE_PAL_IDX, RED_BTN_MAIN_COLOR_PAL_IDX, reuse_seed_on_pressed, NULL }
|
||||||
|
},
|
||||||
|
// GAME_OVER_RUN_MENU_ROW
|
||||||
|
{
|
||||||
|
{ NEW_RUN_BTN_OUTLINE_PAL_IDX, RED_BTN_MAIN_COLOR_PAL_IDX, new_run_on_pressed, NULL },
|
||||||
|
{ MAIN_MENU_BTN_OUTLINE_PAL_IDX, RED_BTN_MAIN_COLOR_PAL_IDX, main_menu_on_pressed, NULL }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
// Internal Variables
|
||||||
|
|
||||||
|
static enum EndCondition s_end_condition = END_CONDITION_NONE;
|
||||||
|
static u32 s_timer = TM_ZERO;
|
||||||
|
static bool s_reuse_seed = false;
|
||||||
|
static bool s_continue_run = false;
|
||||||
|
|
||||||
|
static void game_over_common_init(enum EndCondition init_condition)
|
||||||
|
{
|
||||||
|
s_end_condition = init_condition;
|
||||||
|
s_timer = TM_ZERO;
|
||||||
|
s_reuse_seed = false;
|
||||||
|
s_continue_run = false;
|
||||||
|
|
||||||
|
// Hide all Joker sprites
|
||||||
|
JokerObject* joker_object = NULL;
|
||||||
|
ListItr itr = list_itr_create(get_jokers_list());
|
||||||
|
while ((joker_object = list_itr_next(&itr)))
|
||||||
|
{
|
||||||
|
obj_hide(joker_object->sprite->obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy any preexisting cards in the deck, which will be present if we restart a run
|
||||||
|
Card* card = NULL;
|
||||||
|
while (get_deck_top() >= 0)
|
||||||
|
{
|
||||||
|
card = deck_pop();
|
||||||
|
card_destroy(&card);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clears the round end menu
|
||||||
|
toggle_windows(false, false);
|
||||||
|
GRIT_CPY(pal_bg_mem, background_game_over_gfxPal);
|
||||||
|
GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_game_over_gfxTiles);
|
||||||
|
GRIT_CPY(&se_mem[MAIN_BG_SBB], background_game_over_gfxMap);
|
||||||
|
|
||||||
|
// Move blind token offscreen at initial position before moving it up
|
||||||
|
sprite_position(
|
||||||
|
g_game_vars.playing_blind_token,
|
||||||
|
DEFEATED_BY_TOKEN_INIT_POS.x,
|
||||||
|
DEFEATED_BY_TOKEN_INIT_POS.y
|
||||||
|
);
|
||||||
|
|
||||||
|
// Highlight New Run button
|
||||||
|
game_over_selection_grid.selection = GAME_OVER_INIT_SEL;
|
||||||
|
button_set_highlight(&game_over_buttons[GAME_OVER_SEED_ROW][GAME_OVER_SEED_BTN_COL], false);
|
||||||
|
button_set_highlight(
|
||||||
|
&game_over_buttons[GAME_OVER_RUN_MENU_ROW][GAME_OVER_NEW_RUN_BTN_COL],
|
||||||
|
true
|
||||||
|
);
|
||||||
|
button_set_highlight(
|
||||||
|
&game_over_buttons[GAME_OVER_RUN_MENU_ROW][GAME_OVER_MAIN_MENU_BTN_COL],
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_win_on_init(void)
|
void game_win_on_init(void)
|
||||||
{
|
{
|
||||||
play_sfx(SFX_GAME_WIN, MM_BASE_PITCH_RATE, SFX_DEFAULT_VOLUME);
|
play_sfx(SFX_GAME_WIN, MM_BASE_PITCH_RATE, SFX_DEFAULT_VOLUME);
|
||||||
|
|
||||||
game_over_init(END_CONDITION_WIN);
|
game_over_common_init(END_CONDITION_WIN);
|
||||||
|
affine_background_set_color(TEXT_CLR_BLUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_lose_on_init(void)
|
void game_lose_on_init(void)
|
||||||
@@ -76,67 +195,170 @@ void game_lose_on_init(void)
|
|||||||
|
|
||||||
play_lose_music();
|
play_lose_music();
|
||||||
|
|
||||||
game_over_init(END_CONDITION_LOSS);
|
game_over_common_init(END_CONDITION_LOSS);
|
||||||
}
|
affine_background_set_color(TEXT_CLR_RED);
|
||||||
|
|
||||||
/**
|
// Change top text to "GAME OVER"
|
||||||
* @brief Makes the message background frame move 1 tile up, called until it is in its place.
|
main_bg_se_copy_rect(GAME_OVER_TEXT_TILES_SRC_RECT_1, GAME_OVER_TEXT_TILES_DEST_POS_1);
|
||||||
*/
|
main_bg_se_copy_rect(GAME_OVER_TEXT_TILES_SRC_RECT_2, GAME_OVER_TEXT_TILES_DEST_POS_2);
|
||||||
static inline void game_over_anim_frame(void)
|
|
||||||
{
|
|
||||||
main_bg_se_move_rect_1_tile_vert(GAME_OVER_ANIM_RECT, SCREEN_UP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Change blind frame text
|
||||||
* @brief Polls user input to detect "Retry" button press.
|
main_bg_se_copy_rect(DEFEATED_BY_TEXT_SRC_RECT, DEFEATED_BY_TEXT_DEST_POS);
|
||||||
*/
|
|
||||||
static inline void game_over_process_user_input()
|
|
||||||
{
|
|
||||||
if (key_hit(SELECT_CARD))
|
|
||||||
{
|
|
||||||
play_sfx(SFX_BUTTON, MM_BASE_PITCH_RATE, BUTTON_SFX_VOLUME);
|
|
||||||
game_change_state(GAME_STATE_BLIND_SELECT);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_over_on_update(void)
|
void game_over_on_update(void)
|
||||||
{
|
{
|
||||||
timer++;
|
s_timer++;
|
||||||
if (timer < GAME_OVER_ANIM_FRAMES)
|
|
||||||
|
// Need to clear text here or the number of cards remaining in deck stays for some reason
|
||||||
|
if (s_timer == 1)
|
||||||
|
tte_erase_screen();
|
||||||
|
|
||||||
|
// Move whole panel and Blind Token sprite up by a tile each frame
|
||||||
|
if (s_timer < GAME_OVER_ANIM_FRAMES)
|
||||||
{
|
{
|
||||||
game_over_anim_frame();
|
main_bg_se_move_rect_1_tile_vert(GAME_OVER_ANIM_RECT, SCREEN_UP);
|
||||||
}
|
|
||||||
else if (timer == GAME_OVER_ANIM_FRAMES)
|
sprite_position(
|
||||||
{
|
g_game_vars.playing_blind_token,
|
||||||
switch (condition)
|
g_game_vars.playing_blind_token->pos.x,
|
||||||
{
|
g_game_vars.playing_blind_token->pos.y - TILE_SIZE
|
||||||
case END_CONDITION_WIN:
|
|
||||||
tte_printf(
|
|
||||||
"#{P:%d,%d; cx:0x%X000}YOU WIN",
|
|
||||||
GAME_WIN_MSG_TEXT_RECT.left,
|
|
||||||
GAME_WIN_MSG_TEXT_RECT.top,
|
|
||||||
TTE_BLUE_PB
|
|
||||||
);
|
);
|
||||||
break;
|
}
|
||||||
case END_CONDITION_LOSS:
|
|
||||||
tte_printf(
|
// Print values and buttons text
|
||||||
"#{P:%d,%d; cx:0x%X000}GAME OVER",
|
else if (s_timer == GAME_OVER_ANIM_FRAMES)
|
||||||
GAME_LOSE_MSG_TEXT_RECT.left,
|
{
|
||||||
GAME_LOSE_MSG_TEXT_RECT.top,
|
char best_hand_str[UINT_MAX_DIGITS + 1];
|
||||||
TTE_RED_PB
|
truncate_uint_to_suffixed_str(
|
||||||
|
g_game_vars.best_hand_score,
|
||||||
|
rect_width(&BEST_HAND_SCORE_RECT) / TTE_CHAR_SIZE,
|
||||||
|
best_hand_str
|
||||||
);
|
);
|
||||||
break;
|
Rect best_hand_rect = BEST_HAND_SCORE_RECT;
|
||||||
default:
|
update_text_rect_to_center_str(&best_hand_rect, best_hand_str, SCREEN_LEFT);
|
||||||
break;
|
tte_printf(
|
||||||
|
"#{P:%d,%d; cx:0x%X000}%s",
|
||||||
|
best_hand_rect.left,
|
||||||
|
best_hand_rect.top,
|
||||||
|
TTE_RED_PB,
|
||||||
|
best_hand_str
|
||||||
|
);
|
||||||
|
|
||||||
|
// Most played hand
|
||||||
|
enum HandType most_played_hand = HIGH_CARD;
|
||||||
|
for (enum HandType hand_type = PAIR; hand_type <= HAND_TYPE_MAX; hand_type++)
|
||||||
|
{
|
||||||
|
if (g_game_vars.nb_played_hands[hand_type - 1] >
|
||||||
|
g_game_vars.nb_played_hands[most_played_hand - 1])
|
||||||
|
most_played_hand = hand_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* hand_name_str = get_hand_type_name(most_played_hand);
|
||||||
|
Rect hand_type_rect = MOST_PLAYED_HAND_RECT;
|
||||||
|
update_text_rect_to_center_str(&hand_type_rect, hand_name_str, SCREEN_LEFT);
|
||||||
|
tte_printf(
|
||||||
|
"#{P:%d,%d; cx:0x%X000}%s",
|
||||||
|
hand_type_rect.left,
|
||||||
|
hand_type_rect.top,
|
||||||
|
TTE_WHITE_PB,
|
||||||
|
hand_name_str
|
||||||
|
);
|
||||||
|
|
||||||
|
// Run's seed
|
||||||
|
char seed_str[BASE36_MAX_DIGITS + 1] = {'\0'};
|
||||||
|
u32_to_base36(g_game_vars.rng_info.seed, seed_str);
|
||||||
|
tte_printf(
|
||||||
|
"#{P:%d,%d; cx:0x%X000}%s",
|
||||||
|
SEED_VALUE_POS.x,
|
||||||
|
SEED_VALUE_POS.y,
|
||||||
|
TTE_WHITE_PB,
|
||||||
|
seed_str
|
||||||
|
);
|
||||||
|
|
||||||
|
// Buttons' text
|
||||||
|
tte_printf(
|
||||||
|
"#{P:%d,%d; cx:0x%X000}New Run",
|
||||||
|
NEW_RUN_BTN_TEXT_POS.x,
|
||||||
|
NEW_RUN_BTN_TEXT_POS.y,
|
||||||
|
TTE_WHITE_PB
|
||||||
|
);
|
||||||
|
tte_printf(
|
||||||
|
"#{P:%d,%d; cx:0x%X000}Main Menu",
|
||||||
|
MAIN_MENU_BTN_TEXT_POS.x,
|
||||||
|
MAIN_MENU_BTN_TEXT_POS.y,
|
||||||
|
TTE_WHITE_PB
|
||||||
|
);
|
||||||
}
|
}
|
||||||
game_over_process_user_input();
|
|
||||||
|
if (s_timer >= GAME_OVER_ANIM_FRAMES)
|
||||||
|
selection_grid_process_input(&game_over_selection_grid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_over_on_exit(void)
|
void game_over_on_exit(void)
|
||||||
{
|
{
|
||||||
|
toggle_windows(false, false);
|
||||||
|
|
||||||
play_regular_music();
|
play_regular_music();
|
||||||
condition = END_CONDITION_NONE;
|
s_end_condition = END_CONDITION_NONE;
|
||||||
|
|
||||||
|
u32 previous_seed = g_game_vars.rng_info.seed;
|
||||||
|
|
||||||
|
if (!s_continue_run)
|
||||||
game_reset();
|
game_reset();
|
||||||
rng_shuffle_seed();
|
|
||||||
|
if (s_reuse_seed)
|
||||||
|
rng_set_seed(previous_seed);
|
||||||
|
else
|
||||||
|
g_game_vars.rng_info.seed = UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectionGrid Implementation
|
||||||
|
|
||||||
|
static Button* game_over_get_button_from_sel(const Selection* selection)
|
||||||
|
{
|
||||||
|
return &game_over_buttons[selection->y][selection->x];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void game_over_on_key_transit(SelectionGrid* selection_grid, Selection* selection)
|
||||||
|
{
|
||||||
|
if (key_hit(SELECT_CARD))
|
||||||
|
button_press(&game_over_buttons[selection->y][selection->x]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool game_over_on_selection_changed(
|
||||||
|
SelectionGrid* selection_grid,
|
||||||
|
int row_idx,
|
||||||
|
const Selection* prev_selection,
|
||||||
|
const Selection* new_selection
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (row_idx == prev_selection->y)
|
||||||
|
button_set_highlight(game_over_get_button_from_sel(prev_selection), false);
|
||||||
|
|
||||||
|
if (row_idx == new_selection->y)
|
||||||
|
button_set_highlight(game_over_get_button_from_sel(new_selection), true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
|
||||||
|
static void reuse_seed_on_pressed(void)
|
||||||
|
{
|
||||||
|
s_reuse_seed = !s_reuse_seed;
|
||||||
|
main_bg_se_copy_rect(
|
||||||
|
s_reuse_seed ? REUSE_SEED_BTN_ON_SRC_RECT : REUSE_SEED_BTN_OFF_SRC_RECT,
|
||||||
|
REUSE_SEED_BTN_DEST_POS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void new_run_on_pressed(void)
|
||||||
|
{
|
||||||
|
game_change_state(GAME_STATE_RUN_SETUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void main_menu_on_pressed(void)
|
||||||
|
{
|
||||||
|
game_change_state(GAME_STATE_MAIN_MENU);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
#include "game/joker_gallery.h"
|
||||||
|
|
||||||
|
#include "affine_background.h"
|
||||||
|
#include "game.h"
|
||||||
|
#include "game/common_ui.h"
|
||||||
|
#include "graphic_utils.h"
|
||||||
|
#include "joker.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <tonc.h>
|
||||||
|
|
||||||
|
#define SHOP_DESC_RARITY_MAIN_COLOR_PAL_IDX 27
|
||||||
|
#define SHOP_DESC_RARITY_SHADOW_COLOR_PAL_IDX 28
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
static const Rect VISIBLE_SCREEN_TILE_RECT = { 0, 0, 29, 19};
|
||||||
|
static const BG_POINT CLEAR_TILE_SOURCE = {30, 1};
|
||||||
|
static const Rect CARD_DESC_PANEL_RECT = { 9, 6, 28, 18};
|
||||||
|
static const NinePatchRect CARD_DESC_PANEL_SOURCE = {
|
||||||
|
.patch_rect = {27, 25, 31, 31},
|
||||||
|
.margins = { 2, 3, 2, 3}
|
||||||
|
};
|
||||||
|
static const int CARD_DESC_MAX_TEXT_HEIGHT = CARD_DESC_PANEL_RECT.bottom -
|
||||||
|
CARD_DESC_PANEL_RECT.top + 1 -
|
||||||
|
CARD_DESC_PANEL_SOURCE.margins.top -
|
||||||
|
CARD_DESC_PANEL_SOURCE.margins.bottom;
|
||||||
|
static const Rect CARD_DESC_TEXT_RECT = {11, 9, 26, 18};
|
||||||
|
static const Rect CARD_NAME_TEXT_RECT = {10, 7, 27, 7};
|
||||||
|
static const BG_POINT JOKER_SPRITE_POS = {135, 9};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
static JokerObject* s_gallery_joker = NULL;
|
||||||
|
static size_t s_joker_index = 0;
|
||||||
|
|
||||||
|
static void game_joker_gallery_render(void)
|
||||||
|
{
|
||||||
|
size_t joker_count = get_joker_registry_size();
|
||||||
|
const JokerInfo* info = get_joker_registry_entry(s_joker_index);
|
||||||
|
|
||||||
|
tte_erase_screen();
|
||||||
|
main_bg_se_copy_expand_tile(VISIBLE_SCREEN_TILE_RECT, CLEAR_TILE_SOURCE);
|
||||||
|
|
||||||
|
if (joker_count == 0 || info == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_gallery_joker = joker_object_new(joker_new(s_joker_index));
|
||||||
|
if (s_gallery_joker == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprite_object_position((SpriteObject*)s_gallery_joker, JOKER_SPRITE_POS.x, JOKER_SPRITE_POS.y);
|
||||||
|
|
||||||
|
int desc_bottom_offset = CARD_DESC_MAX_TEXT_HEIGHT -
|
||||||
|
info->joker_print_desc(s_gallery_joker->joker, CARD_DESC_TEXT_RECT);
|
||||||
|
desc_bottom_offset = max(0, desc_bottom_offset);
|
||||||
|
|
||||||
|
pal_bg_mem[SHOP_DESC_RARITY_MAIN_COLOR_PAL_IDX] = joker_get_rarity_color(info->rarity, true);
|
||||||
|
pal_bg_mem[SHOP_DESC_RARITY_SHADOW_COLOR_PAL_IDX] = joker_get_rarity_color(info->rarity, false);
|
||||||
|
|
||||||
|
Rect panel_rect = CARD_DESC_PANEL_RECT;
|
||||||
|
panel_rect.bottom -= desc_bottom_offset;
|
||||||
|
main_bg_se_copy_expand_9_patch(panel_rect, &CARD_DESC_PANEL_SOURCE);
|
||||||
|
|
||||||
|
tte_printf(
|
||||||
|
TTE_WHITE_TAG "#{P:%d,%d}%*s%s",
|
||||||
|
CARD_NAME_TEXT_RECT.left * TILE_SIZE,
|
||||||
|
CARD_NAME_TEXT_RECT.top * TILE_SIZE,
|
||||||
|
(rect_width(&CARD_NAME_TEXT_RECT) - strlen(info->name)) / 2,
|
||||||
|
"",
|
||||||
|
info->name
|
||||||
|
);
|
||||||
|
|
||||||
|
const char* rarity = joker_get_rarity_string(info->rarity);
|
||||||
|
tte_printf(
|
||||||
|
TTE_WHITE_TAG "#{P:%d,%d}%*s%s",
|
||||||
|
CARD_DESC_TEXT_RECT.left * TILE_SIZE,
|
||||||
|
(CARD_DESC_TEXT_RECT.bottom - desc_bottom_offset - 1) * TILE_SIZE,
|
||||||
|
(rect_width(&CARD_DESC_TEXT_RECT) - strlen(rarity)) / 2,
|
||||||
|
"",
|
||||||
|
rarity
|
||||||
|
);
|
||||||
|
|
||||||
|
tte_printf(TTE_WHITE_TAG "#{P:8,16}JOKER\n#{P:8,24}GALLERY");
|
||||||
|
tte_printf(TTE_YELLOW_TAG "#{P:8,48}%02d/%02d", (int)s_joker_index + 1, (int)joker_count);
|
||||||
|
tte_printf(TTE_WHITE_TAG "#{P:8,80}L PREV\n#{P:8,96}R NEXT\n#{P:8,120}B BACK");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void game_joker_gallery_change_page(int direction)
|
||||||
|
{
|
||||||
|
size_t joker_count = get_joker_registry_size();
|
||||||
|
if (joker_count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
joker_object_destroy(&s_gallery_joker);
|
||||||
|
if (direction < 0)
|
||||||
|
{
|
||||||
|
s_joker_index = (s_joker_index + joker_count - 1) % joker_count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_joker_index = (s_joker_index + 1) % joker_count;
|
||||||
|
}
|
||||||
|
game_joker_gallery_render();
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_joker_gallery_on_init(void)
|
||||||
|
{
|
||||||
|
affine_background_change_background(AFFINE_BG_MAIN_MENU);
|
||||||
|
change_background(BG_SHOP, true);
|
||||||
|
toggle_windows(false, false);
|
||||||
|
tte_colors_setup();
|
||||||
|
|
||||||
|
if (s_joker_index >= get_joker_registry_size())
|
||||||
|
{
|
||||||
|
s_joker_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_joker_gallery_render();
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_joker_gallery_on_update(void)
|
||||||
|
{
|
||||||
|
if (key_hit(KEY_LEFT | KEY_L))
|
||||||
|
{
|
||||||
|
game_joker_gallery_change_page(-1);
|
||||||
|
}
|
||||||
|
else if (key_hit(KEY_RIGHT | KEY_R | KEY_A))
|
||||||
|
{
|
||||||
|
game_joker_gallery_change_page(1);
|
||||||
|
}
|
||||||
|
else if (key_hit(KEY_B))
|
||||||
|
{
|
||||||
|
game_change_state(GAME_STATE_MAIN_MENU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_joker_gallery_on_exit(void)
|
||||||
|
{
|
||||||
|
joker_object_destroy(&s_gallery_joker);
|
||||||
|
tte_erase_screen();
|
||||||
|
}
|
||||||
@@ -33,8 +33,8 @@ bool jokers_sel_row_on_selection_changed(
|
|||||||
// Don't change focus from current Joker if swapping
|
// Don't change focus from current Joker if swapping
|
||||||
if (joker_object != NULL && !swapping)
|
if (joker_object != NULL && !swapping)
|
||||||
{
|
{
|
||||||
sprite_object_erase_text_under(joker_object->sprite_object);
|
sprite_object_erase_text_under((SpriteObject*)joker_object);
|
||||||
sprite_object_set_focus(joker_object->sprite_object, false);
|
sprite_object_set_focus((SpriteObject*)joker_object, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ bool jokers_sel_row_on_selection_changed(
|
|||||||
{
|
{
|
||||||
if (!swapping)
|
if (!swapping)
|
||||||
{
|
{
|
||||||
sprite_object_set_focus(joker_object->sprite_object, true);
|
sprite_object_set_focus((SpriteObject*)joker_object, true);
|
||||||
}
|
}
|
||||||
// If we land on this row while the A button is being held, we are in swapping mode
|
// If we land on this row while the A button is being held, we are in swapping mode
|
||||||
// This means that we need to hide the price, whether we were already
|
// This means that we need to hide the price, whether we were already
|
||||||
@@ -54,7 +54,7 @@ bool jokers_sel_row_on_selection_changed(
|
|||||||
if (!key_is_down(SELECT_CARD))
|
if (!key_is_down(SELECT_CARD))
|
||||||
{
|
{
|
||||||
sprite_object_print_price_under(
|
sprite_object_print_price_under(
|
||||||
joker_object->sprite_object,
|
(SpriteObject*)joker_object,
|
||||||
joker_get_sell_value(joker_object->joker)
|
joker_get_sell_value(joker_object->joker)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -75,8 +75,8 @@ bool jokers_sel_row_on_selection_changed(
|
|||||||
|
|
||||||
static inline void joker_start_discard_animation(JokerObject* joker_object)
|
static inline void joker_start_discard_animation(JokerObject* joker_object)
|
||||||
{
|
{
|
||||||
joker_object->sprite_object->tx = int2fx(JOKER_DISCARD_TARGET.x);
|
joker_object->tx = int2fx(JOKER_DISCARD_TARGET.x);
|
||||||
joker_object->sprite_object->ty = int2fx(JOKER_DISCARD_TARGET.y);
|
joker_object->ty = int2fx(JOKER_DISCARD_TARGET.y);
|
||||||
list_push_back(get_discarded_jokers_list(), joker_object);
|
list_push_back(get_discarded_jokers_list(), joker_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ static inline void game_sell_joker(int joker_idx)
|
|||||||
JokerObject* joker_object = (JokerObject*)list_get_at_idx(owned_jokers_list, joker_idx);
|
JokerObject* joker_object = (JokerObject*)list_get_at_idx(owned_jokers_list, joker_idx);
|
||||||
g_game_vars.money += joker_get_sell_value(joker_object->joker);
|
g_game_vars.money += joker_get_sell_value(joker_object->joker);
|
||||||
display_money();
|
display_money();
|
||||||
sprite_object_erase_text_under(joker_object->sprite_object);
|
sprite_object_erase_text_under((SpriteObject*)joker_object);
|
||||||
|
|
||||||
remove_owned_joker(joker_idx);
|
remove_owned_joker(joker_idx);
|
||||||
|
|
||||||
@@ -104,12 +104,12 @@ void jokers_sel_row_on_key_transit(SelectionGrid* selection_grid, Selection* sel
|
|||||||
{
|
{
|
||||||
if (key_hit(SELECT_CARD))
|
if (key_hit(SELECT_CARD))
|
||||||
{
|
{
|
||||||
sprite_object_erase_text_under(joker_object->sprite_object);
|
sprite_object_erase_text_under((SpriteObject*)joker_object);
|
||||||
}
|
}
|
||||||
else if (key_released(SELECT_CARD))
|
else if (key_released(SELECT_CARD))
|
||||||
{
|
{
|
||||||
sprite_object_print_price_under(
|
sprite_object_print_price_under(
|
||||||
joker_object->sprite_object,
|
(SpriteObject*)joker_object,
|
||||||
joker_get_sell_value(joker_object->joker)
|
joker_get_sell_value(joker_object->joker)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-12
@@ -78,10 +78,11 @@ static SelectionGrid main_menu_selection_grid = {
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// Main menu sprite - the ace of spades
|
// Main menu sprite - the ace of spades
|
||||||
static CardObject* main_menu_ace = NULL;
|
static CardObject* s_main_menu_ace = NULL;
|
||||||
|
|
||||||
// Keep track of last highlighted button
|
// Keep track of last highlighted button
|
||||||
static enum MainButtons last_highlighted_button = PLAY_BTN_IDX;
|
static enum MainButtons s_last_highlighted_button = PLAY_BTN_IDX;
|
||||||
|
static bool s_gallery_prompt_drawn = false;
|
||||||
|
|
||||||
void game_main_menu_change_background(void)
|
void game_main_menu_change_background(void)
|
||||||
{
|
{
|
||||||
@@ -103,15 +104,17 @@ void game_main_menu_on_init(void)
|
|||||||
{
|
{
|
||||||
affine_background_change_background(AFFINE_BG_MAIN_MENU);
|
affine_background_change_background(AFFINE_BG_MAIN_MENU);
|
||||||
change_background(BG_MAIN_MENU, true);
|
change_background(BG_MAIN_MENU, true);
|
||||||
main_menu_ace = card_object_new(card_new(SPADES, ACE));
|
s_gallery_prompt_drawn = false;
|
||||||
card_object_set_sprite(main_menu_ace, 0);
|
s_main_menu_ace = card_object_new(card_new(SPADES, ACE));
|
||||||
main_menu_ace->sprite_object->sprite->obj->attr0 |= ATTR0_AFF_DBL;
|
card_object_set_sprite(s_main_menu_ace, 0);
|
||||||
main_menu_ace->sprite_object->tscale = float2fx(0.8f);
|
// TODO: NULL-check sprite
|
||||||
sprite_object_position(main_menu_ace->sprite_object, MAIN_MENU_ACE_T_X, MAIN_MENU_ACE_T_Y);
|
s_main_menu_ace->sprite->obj->attr0 |= ATTR0_AFF_DBL;
|
||||||
|
s_main_menu_ace->tscale = float2fx(0.8f);
|
||||||
|
sprite_object_position((SpriteObject*)s_main_menu_ace, MAIN_MENU_ACE_T_X, MAIN_MENU_ACE_T_Y);
|
||||||
|
|
||||||
// Select last highlighted button, Play button by default.
|
// Select last highlighted button, Play button by default.
|
||||||
// e.g. if we return from the options menu, we want the Options button to be highlighted.
|
// e.g. if we return from the options menu, we want the Options button to be highlighted.
|
||||||
Selection sel_init = {last_highlighted_button, 0};
|
Selection sel_init = {s_last_highlighted_button, 0};
|
||||||
main_menu_selection_grid.selection = sel_init;
|
main_menu_selection_grid.selection = sel_init;
|
||||||
|
|
||||||
// Highlight current button
|
// Highlight current button
|
||||||
@@ -120,7 +123,14 @@ void game_main_menu_on_init(void)
|
|||||||
|
|
||||||
void game_main_menu_on_update(void)
|
void game_main_menu_on_update(void)
|
||||||
{
|
{
|
||||||
main_menu_ace->sprite_object->trotation = lu_sin((g_game_vars.timer << 8) / 2) / 3;
|
s_main_menu_ace->trotation = lu_sin((g_game_vars.timer << 8) / 2) / 3;
|
||||||
|
|
||||||
|
// The splash state clears TTE after transitioning, so draw this on the first menu update.
|
||||||
|
if (!s_gallery_prompt_drawn)
|
||||||
|
{
|
||||||
|
tte_printf(TTE_WHITE_TAG "#{P:128,152}START: JOKERS");
|
||||||
|
s_gallery_prompt_drawn = true;
|
||||||
|
}
|
||||||
|
|
||||||
selection_grid_process_input(&main_menu_selection_grid);
|
selection_grid_process_input(&main_menu_selection_grid);
|
||||||
}
|
}
|
||||||
@@ -128,12 +138,12 @@ void game_main_menu_on_update(void)
|
|||||||
void game_main_menu_on_exit(void)
|
void game_main_menu_on_exit(void)
|
||||||
{
|
{
|
||||||
// Save selected button
|
// Save selected button
|
||||||
last_highlighted_button = main_menu_selection_grid.selection.x;
|
s_last_highlighted_button = main_menu_selection_grid.selection.x;
|
||||||
|
|
||||||
// Normally I would just cache these and hide/unhide but I didn't feel like dealing with
|
// Normally I would just cache these and hide/unhide but I didn't feel like dealing with
|
||||||
// defining a layer for it
|
// defining a layer for it
|
||||||
card_destroy(&main_menu_ace->card);
|
card_destroy(&s_main_menu_ace->card);
|
||||||
card_object_destroy(&main_menu_ace);
|
card_object_destroy(&s_main_menu_ace);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement SelectionGrid handler functions
|
// Implement SelectionGrid handler functions
|
||||||
@@ -185,6 +195,7 @@ static void main_menu_on_key_transit(SelectionGrid* selection_grid, Selection* s
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
tte_erase_rect_wrapper(GBALATRO_VERSION_TEXT_RECT);
|
tte_erase_rect_wrapper(GBALATRO_VERSION_TEXT_RECT);
|
||||||
|
s_gallery_prompt_drawn = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,6 +206,12 @@ static void main_menu_on_key_transit(SelectionGrid* selection_grid, Selection* s
|
|||||||
combo_pressed = false;
|
combo_pressed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((key_hit(KEY_START) || key_hit(KEY_SELECT)) && !key_is_down(KEY_L) && !key_is_down(KEY_R))
|
||||||
|
{
|
||||||
|
game_change_state(GAME_STATE_JOKER_GALLERY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (key_hit(SELECT_CARD))
|
if (key_hit(SELECT_CARD))
|
||||||
{
|
{
|
||||||
button_press(&main_menu_buttons[selection->x]);
|
button_press(&main_menu_buttons[selection->x]);
|
||||||
|
|||||||
+242
-240
File diff suppressed because it is too large
Load Diff
+58
-20
@@ -18,6 +18,7 @@
|
|||||||
#include "state_machine.h"
|
#include "state_machine.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include <tonc.h>
|
#include <tonc.h>
|
||||||
|
|
||||||
// Palette Indices
|
// Palette Indices
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
|
|
||||||
#define NEW_RUN_BTN_OUTLINE_COLOR_PAL_IDX 30
|
#define NEW_RUN_BTN_OUTLINE_COLOR_PAL_IDX 30
|
||||||
#define RESUME_BTN_OUTLINE_COLOR_PAL_IDX 31
|
#define RESUME_BTN_OUTLINE_COLOR_PAL_IDX 31
|
||||||
#define CHANGE_DECK_BTN_OUTINE_COLOR_PAL_IDX 32
|
#define CHANGE_DECK_BTN_OUTLINE_COLOR_PAL_IDX 32
|
||||||
#define SEED_CHECK_BTN_OUTLINE_COLOR_PAL_IDX 33
|
#define SEED_CHECK_BTN_OUTLINE_COLOR_PAL_IDX 33
|
||||||
#define SEED_DECK_BTN_OUTLINE_COLOR_PAL_IDX 34
|
#define SEED_DECK_BTN_OUTLINE_COLOR_PAL_IDX 34
|
||||||
#define PLAY_BTN_OUTLINE_COLOR_PAL_IDX 35
|
#define PLAY_BTN_OUTLINE_COLOR_PAL_IDX 35
|
||||||
@@ -292,7 +293,7 @@ static SelectionGrid choose_deck_selection_grid = {
|
|||||||
static void change_deck_on_pressed(void);
|
static void change_deck_on_pressed(void);
|
||||||
|
|
||||||
static Button change_deck_button = {
|
static Button change_deck_button = {
|
||||||
CHANGE_DECK_BTN_OUTINE_COLOR_PAL_IDX,
|
CHANGE_DECK_BTN_OUTLINE_COLOR_PAL_IDX,
|
||||||
CHANGE_DECK_BTN_MAIN_COLOR_PAL_IDX,
|
CHANGE_DECK_BTN_MAIN_COLOR_PAL_IDX,
|
||||||
change_deck_on_pressed,
|
change_deck_on_pressed,
|
||||||
NULL
|
NULL
|
||||||
@@ -527,7 +528,7 @@ static Button choose_seed_bottom_buttons[2] = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char keyboard_buttons_to_char[KEYBOARD_HEIGHT * KEYBOARD_WIDTH] = {
|
static const char KEYBOARD_BUTTONS_TO_CHAR[KEYBOARD_HEIGHT * KEYBOARD_WIDTH] = {
|
||||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
||||||
@@ -536,8 +537,8 @@ static const char keyboard_buttons_to_char[KEYBOARD_HEIGHT * KEYBOARD_WIDTH] = {
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// Size BASE36_MAX_DIGITS + 1 to always have '\0' at the end
|
// Size BASE36_MAX_DIGITS + 1 to always have '\0' at the end
|
||||||
static char seed_str[BASE36_MAX_DIGITS + 1] = {'\0'};
|
static char s_seed_str[BASE36_MAX_DIGITS + 1] = {'\0'};
|
||||||
static u8 seed_cursor_pos = 0;
|
static u8 s_seed_cursor_pos = 0;
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
@@ -566,6 +567,7 @@ enum RunSetupResumeRows
|
|||||||
|
|
||||||
void game_run_setup_change_background(void)
|
void game_run_setup_change_background(void)
|
||||||
{
|
{
|
||||||
|
tte_erase_screen();
|
||||||
GRIT_CPY(pal_bg_mem, background_run_setup_gfxPal);
|
GRIT_CPY(pal_bg_mem, background_run_setup_gfxPal);
|
||||||
GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_run_setup_gfxTiles);
|
GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_run_setup_gfxTiles);
|
||||||
GRIT_CPY(&se_mem[MAIN_BG_SBB], background_run_setup_gfxMap);
|
GRIT_CPY(&se_mem[MAIN_BG_SBB], background_run_setup_gfxMap);
|
||||||
@@ -576,6 +578,22 @@ void game_run_setup_on_init(void)
|
|||||||
state_machine_register(&run_setup_sm);
|
state_machine_register(&run_setup_sm);
|
||||||
game_run_setup_change_background();
|
game_run_setup_change_background();
|
||||||
|
|
||||||
|
// Apply the current use_seed value if seed is UNDEFINED
|
||||||
|
if (g_game_vars.rng_info.seed == UNDEFINED)
|
||||||
|
{
|
||||||
|
// Make the string empty instead of showing all zeroes
|
||||||
|
memset(s_seed_str, '\0', BASE36_MAX_DIGITS + 1);
|
||||||
|
s_seed_cursor_pos = 0;
|
||||||
|
}
|
||||||
|
// Or use previous Run's seed if it hasn't been reset
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32_to_base36(g_game_vars.rng_info.seed, s_seed_str);
|
||||||
|
s_seed_cursor_pos = BASE36_MAX_DIGITS;
|
||||||
|
use_seed = true;
|
||||||
|
}
|
||||||
|
toggle_seed_enabled(use_seed);
|
||||||
|
|
||||||
// Rank doesn't matter, won't see it
|
// Rank doesn't matter, won't see it
|
||||||
run_setup_deck = card_object_new(card_new(SPADES, ACE));
|
run_setup_deck = card_object_new(card_new(SPADES, ACE));
|
||||||
|
|
||||||
@@ -584,7 +602,7 @@ void game_run_setup_on_init(void)
|
|||||||
card_object_set_sprite_face_down(run_setup_deck, g_game_vars.deck, 0);
|
card_object_set_sprite_face_down(run_setup_deck, g_game_vars.deck, 0);
|
||||||
|
|
||||||
sprite_object_position(
|
sprite_object_position(
|
||||||
run_setup_deck->sprite_object,
|
(SpriteObject*)run_setup_deck,
|
||||||
RUN_SETUP_DECK_SPRITE_T_X,
|
RUN_SETUP_DECK_SPRITE_T_X,
|
||||||
RUN_SETUP_DECK_SPRITE_T_Y
|
RUN_SETUP_DECK_SPRITE_T_Y
|
||||||
);
|
);
|
||||||
@@ -632,7 +650,13 @@ void game_run_setup_on_exit(void)
|
|||||||
static void choose_deck_substate_init(void)
|
static void choose_deck_substate_init(void)
|
||||||
{
|
{
|
||||||
// Show Deck sprite, name and TODO: description
|
// Show Deck sprite, name and TODO: description
|
||||||
obj_unhide(run_setup_deck->sprite_object->sprite->obj, ATTR0_AFF);
|
{
|
||||||
|
Sprite* deck_sprite = sprite_object_get_sprite((SpriteObject*)run_setup_deck);
|
||||||
|
if (deck_sprite != NULL)
|
||||||
|
{
|
||||||
|
obj_unhide(deck_sprite->obj, ATTR0_AFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
print_deck_name(g_game_vars.deck, RUN_SETUP_DECK_NAME_TEXT_POS);
|
print_deck_name(g_game_vars.deck, RUN_SETUP_DECK_NAME_TEXT_POS);
|
||||||
print_deck_description(g_game_vars.deck, RUN_SETUP_DECK_DESC_TEXT_POS);
|
print_deck_description(g_game_vars.deck, RUN_SETUP_DECK_DESC_TEXT_POS);
|
||||||
|
|
||||||
@@ -660,9 +684,9 @@ static void choose_deck_substate_init(void)
|
|||||||
// Set button highlights
|
// Set button highlights
|
||||||
button_set_highlight(&change_deck_button, true);
|
button_set_highlight(&change_deck_button, true);
|
||||||
button_set_highlight(&choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_USE_SEED], false);
|
button_set_highlight(&choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_USE_SEED], false);
|
||||||
toggle_seed_enabled(use_seed); // This just re-applies the current value
|
|
||||||
button_set_highlight(&choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_PLAY], false);
|
button_set_highlight(&choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_PLAY], false);
|
||||||
button_set_highlight(&back_button, false);
|
button_set_highlight(&back_button, false);
|
||||||
|
toggle_seed_enabled(use_seed);
|
||||||
|
|
||||||
// Print button text
|
// Print button text
|
||||||
tte_printf(
|
tte_printf(
|
||||||
@@ -784,7 +808,7 @@ static inline void update_seed_text(void)
|
|||||||
RUN_SETUP_SEED_FIELD_TEXT_POS.y,
|
RUN_SETUP_SEED_FIELD_TEXT_POS.y,
|
||||||
TTE_BLACK_PB,
|
TTE_BLACK_PB,
|
||||||
BASE36_MAX_DIGITS + 1,
|
BASE36_MAX_DIGITS + 1,
|
||||||
seed_str
|
s_seed_str
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,7 +821,13 @@ static void seed_keyboard_substate_init(void)
|
|||||||
tte_erase_rect_wrapper(RUN_SETUP_DECK_NAME_DESC_RECT);
|
tte_erase_rect_wrapper(RUN_SETUP_DECK_NAME_DESC_RECT);
|
||||||
|
|
||||||
// Hide Deck card sprite
|
// Hide Deck card sprite
|
||||||
obj_hide(run_setup_deck->sprite_object->sprite->obj);
|
{
|
||||||
|
Sprite* deck_sprite = sprite_object_get_sprite((SpriteObject*)run_setup_deck);
|
||||||
|
if (deck_sprite != NULL)
|
||||||
|
{
|
||||||
|
obj_hide(deck_sprite->obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clean deck swap screen with frame BG color
|
// Clean deck swap screen with frame BG color
|
||||||
main_bg_se_copy_expand_tile(
|
main_bg_se_copy_expand_tile(
|
||||||
@@ -890,9 +920,9 @@ static inline void reroll_seed_str(void)
|
|||||||
rng_shuffle_seed();
|
rng_shuffle_seed();
|
||||||
u32 new_seed = rng_get_u32();
|
u32 new_seed = rng_get_u32();
|
||||||
rng_set_seed(new_seed);
|
rng_set_seed(new_seed);
|
||||||
u32_to_base36(new_seed, seed_str);
|
u32_to_base36(new_seed, s_seed_str);
|
||||||
update_seed_text();
|
update_seed_text();
|
||||||
seed_cursor_pos = BASE36_MAX_DIGITS;
|
s_seed_cursor_pos = BASE36_MAX_DIGITS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -900,10 +930,10 @@ static inline void reroll_seed_str(void)
|
|||||||
*/
|
*/
|
||||||
static inline void delete_seed_char(void)
|
static inline void delete_seed_char(void)
|
||||||
{
|
{
|
||||||
if (seed_cursor_pos == 0)
|
if (s_seed_cursor_pos == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
seed_str[--seed_cursor_pos] = '\0';
|
s_seed_str[--s_seed_cursor_pos] = '\0';
|
||||||
update_seed_text();
|
update_seed_text();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -914,10 +944,10 @@ static inline void delete_seed_char(void)
|
|||||||
*/
|
*/
|
||||||
static inline void type_seed_char(enum RunSetupKeyboardButtons key)
|
static inline void type_seed_char(enum RunSetupKeyboardButtons key)
|
||||||
{
|
{
|
||||||
if (seed_cursor_pos >= BASE36_MAX_DIGITS)
|
if (s_seed_cursor_pos >= BASE36_MAX_DIGITS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
seed_str[seed_cursor_pos++] = keyboard_buttons_to_char[key];
|
s_seed_str[s_seed_cursor_pos++] = KEYBOARD_BUTTONS_TO_CHAR[key];
|
||||||
update_seed_text();
|
update_seed_text();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,8 +976,8 @@ static void keyboard_button_on_pressed(void)
|
|||||||
// The cursor position is unsigned so always positive, but we still need to
|
// The cursor position is unsigned so always positive, but we still need to
|
||||||
// ensure it doersn't go out of bounds by more than 1 so that we can always
|
// ensure it doersn't go out of bounds by more than 1 so that we can always
|
||||||
// substract 1 from it when erasing a character from the seed string.
|
// substract 1 from it when erasing a character from the seed string.
|
||||||
if (seed_cursor_pos > BASE36_MAX_DIGITS)
|
if (s_seed_cursor_pos > BASE36_MAX_DIGITS)
|
||||||
seed_cursor_pos = BASE36_MAX_DIGITS;
|
s_seed_cursor_pos = BASE36_MAX_DIGITS;
|
||||||
|
|
||||||
if (key_hit(DESELECT_CARDS))
|
if (key_hit(DESELECT_CARDS))
|
||||||
delete_seed_char();
|
delete_seed_char();
|
||||||
@@ -1092,7 +1122,13 @@ static void resume_substate_init(void)
|
|||||||
tab_set_highlight(RUN_SETUP_TAB_RESUME);
|
tab_set_highlight(RUN_SETUP_TAB_RESUME);
|
||||||
|
|
||||||
// Show Deck card sprite
|
// Show Deck card sprite
|
||||||
obj_unhide(run_setup_deck->sprite_object->sprite->obj, ATTR0_AFF);
|
{
|
||||||
|
Sprite* deck_sprite = sprite_object_get_sprite((SpriteObject*)run_setup_deck);
|
||||||
|
if (deck_sprite != NULL)
|
||||||
|
{
|
||||||
|
obj_unhide(deck_sprite->obj, ATTR0_AFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// COMMON BUTTONS
|
// COMMON BUTTONS
|
||||||
@@ -1157,10 +1193,12 @@ static void play_on_pressed(void)
|
|||||||
{
|
{
|
||||||
// Apply provided Seed if enabled
|
// Apply provided Seed if enabled
|
||||||
if (use_seed)
|
if (use_seed)
|
||||||
rng_set_seed(base36_to_u32(seed_str));
|
rng_set_seed(base36_to_u32(s_seed_str));
|
||||||
else
|
else
|
||||||
rng_shuffle_seed();
|
rng_shuffle_seed();
|
||||||
|
|
||||||
|
use_seed = false;
|
||||||
|
|
||||||
game_change_state(GAME_STATE_GAME_START);
|
game_change_state(GAME_STATE_GAME_START);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+162
-273
@@ -17,6 +17,7 @@
|
|||||||
#include "joker.h"
|
#include "joker.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
|
#include "mgba_logger.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "save.h"
|
#include "save.h"
|
||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
@@ -87,8 +88,7 @@ static const Rect SHOP_PRICES_TEXT_RECT = { 72, 56, 192, 160 };
|
|||||||
static const Rect SHOP_REROLL_RECT = { 88, 96, UNDEFINED, UNDEFINED };
|
static const Rect SHOP_REROLL_RECT = { 88, 96, UNDEFINED, UNDEFINED };
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
static List s_shop_jokers_list = LIST_DEFAULT;
|
static List s_shop_items_list = LIST_DEFAULT;
|
||||||
BITSET_DEFINE(s_avail_jokers_bitset, MAX_DEFINABLE_JOKERS)
|
|
||||||
|
|
||||||
enum GameShopStates
|
enum GameShopStates
|
||||||
{
|
{
|
||||||
@@ -165,45 +165,28 @@ static Button reroll_button = {
|
|||||||
|
|
||||||
// Shop internal variables
|
// Shop internal variables
|
||||||
|
|
||||||
static int timer;
|
static int s_timer;
|
||||||
|
|
||||||
static int reroll_cost = REROLL_BASE_COST;
|
static int s_reroll_cost = REROLL_BASE_COST;
|
||||||
|
|
||||||
// Variables relative to the Card we are showing the description of
|
// Variables relative to the Card we are showing the description of
|
||||||
static JokerObject* description_card = NULL;
|
|
||||||
static FIXED description_card_original_x_pos = UNDEFINED;
|
// TODO: Change this to item once it has description printing API.
|
||||||
static FIXED description_card_original_y_pos = UNDEFINED;
|
static JokerObject* s_description_card = NULL;
|
||||||
static List* description_card_original_list = NULL;
|
static FIXED s_description_card_original_x_pos = UNDEFINED;
|
||||||
|
static FIXED s_description_card_original_y_pos = UNDEFINED;
|
||||||
|
static List* s_description_card_original_list = NULL;
|
||||||
|
|
||||||
JokerObject* game_shop_get_description_card(void)
|
JokerObject* game_shop_get_description_card(void)
|
||||||
{
|
{
|
||||||
return description_card;
|
return s_description_card;
|
||||||
}
|
|
||||||
|
|
||||||
static inline void reset_shop_jokers(void)
|
|
||||||
{
|
|
||||||
int num_jokers = get_joker_registry_size();
|
|
||||||
|
|
||||||
bitset_clear(&s_avail_jokers_bitset);
|
|
||||||
for (int i = 0; i < num_jokers; i++)
|
|
||||||
{
|
|
||||||
bitset_set_idx(&s_avail_jokers_bitset, i, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_shop_reset(void)
|
void game_shop_reset(void)
|
||||||
{
|
{
|
||||||
list_clear(&s_shop_jokers_list);
|
list_clear(&s_shop_items_list);
|
||||||
s_shop_jokers_list = list_init();
|
s_shop_items_list = list_init();
|
||||||
reset_shop_jokers();
|
joker_reset_rollable_jokers();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set whether a Joker can appear in the shop.
|
|
||||||
*/
|
|
||||||
void game_shop_set_joker_avail(int joker_id, bool avail)
|
|
||||||
{
|
|
||||||
bitset_set_idx(&s_avail_jokers_bitset, joker_id, avail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_shop_change_background(void)
|
void game_shop_change_background(void)
|
||||||
@@ -234,7 +217,7 @@ void game_shop_on_init(void)
|
|||||||
{
|
{
|
||||||
game_shop_change_background();
|
game_shop_change_background();
|
||||||
|
|
||||||
timer = TM_ZERO;
|
s_timer = TM_ZERO;
|
||||||
|
|
||||||
state_machine_register(&shop_sm);
|
state_machine_register(&shop_sm);
|
||||||
state_machine_change_state(&shop_sm, GAME_SHOP_INTRO);
|
state_machine_change_state(&shop_sm, GAME_SHOP_INTRO);
|
||||||
@@ -246,123 +229,51 @@ void game_shop_on_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Computes the number of Jokers we can currently roll in the Shop.
|
* @brief Create a shop top row item - jokers, consumables, and possibly playing cards
|
||||||
* The Jokers we own is taken into account and can't be rolled again.
|
* Currently only jokers are implemented.
|
||||||
*/
|
*/
|
||||||
static inline int get_num_shop_jokers_avail(void)
|
static Item* game_shop_create_top_row_item(void)
|
||||||
{
|
{
|
||||||
return bitset_num_set_bits(&s_avail_jokers_bitset);
|
// TODO: Randomize item type when consumables are implemented
|
||||||
|
return item_roll_new(ITEM_TYPE_JOKER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Rolls a random Joker among the available ones
|
* @brief Setup for the lists of items we can purchase in the top row of the Shop.
|
||||||
|
* i.e. Jokers and consumables and possibly playing cards.
|
||||||
*/
|
*/
|
||||||
static inline int game_shop_get_rand_available_joker_id(void)
|
static void game_shop_create_top_row_items(void)
|
||||||
{
|
|
||||||
// Roll for what rarity the joker will be
|
|
||||||
int joker_rarity = joker_get_random_rarity();
|
|
||||||
|
|
||||||
// Now determine how many jokers are available based on the rarity
|
|
||||||
int jokers_avail_size = get_num_shop_jokers_avail();
|
|
||||||
|
|
||||||
if (jokers_avail_size == 0)
|
|
||||||
return UNDEFINED;
|
|
||||||
|
|
||||||
int matching_joker_ids[jokers_avail_size];
|
|
||||||
int fallback_random_idx = rng_get_u32() % jokers_avail_size;
|
|
||||||
int fallback_random_joker_id = UNDEFINED;
|
|
||||||
int match_count = 0;
|
|
||||||
|
|
||||||
BitsetItr itr = bitset_itr_create(&s_avail_jokers_bitset);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
int joker_id = UNDEFINED;
|
|
||||||
while ((joker_id = bitset_itr_next(&itr)) != UNDEFINED)
|
|
||||||
{
|
|
||||||
if (i++ == fallback_random_idx)
|
|
||||||
fallback_random_joker_id = joker_id;
|
|
||||||
const JokerInfo* info = get_joker_registry_entry(joker_id);
|
|
||||||
if (info->rarity == joker_rarity)
|
|
||||||
{
|
|
||||||
matching_joker_ids[match_count++] = joker_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int selected_joker_id = (match_count > 0) ? matching_joker_ids[rng_get_u32() % match_count]
|
|
||||||
: fallback_random_joker_id;
|
|
||||||
|
|
||||||
return selected_joker_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns true if we can't roll any Joker
|
|
||||||
*/
|
|
||||||
static inline bool no_avail_jokers(void)
|
|
||||||
{
|
|
||||||
return bitset_is_empty(&s_avail_jokers_bitset);
|
|
||||||
}
|
|
||||||
|
|
||||||
GBAL_UNUSED
|
|
||||||
static inline bool is_shop_joker_avail(int joker_id)
|
|
||||||
{
|
|
||||||
return bitset_get_idx(&s_avail_jokers_bitset, joker_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Setup for the lists of items we can purchase in the Shop.
|
|
||||||
* Only Jokers are available for now, but this is where consumables and
|
|
||||||
* booster packs will be rolled when they are implemented.
|
|
||||||
*/
|
|
||||||
static void game_shop_create_items(void)
|
|
||||||
{
|
{
|
||||||
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT);
|
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT);
|
||||||
|
|
||||||
if (no_avail_jokers())
|
List* shop_items_list = &s_shop_items_list;
|
||||||
|
|
||||||
|
list_clear(shop_items_list);
|
||||||
|
*shop_items_list = list_init();
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_SHOP_ITEMS; i++)
|
||||||
|
{
|
||||||
|
Item* item = game_shop_create_top_row_item();
|
||||||
|
|
||||||
|
if (item == NULL)
|
||||||
|
{
|
||||||
|
MGBA_FUNC_WARN("Could not create shop item");
|
||||||
|
|
||||||
|
// TODO: Decide how to handle this case gracefully
|
||||||
|
// If the issue for example is that we can't generate any more jokers,
|
||||||
|
// because for example the user owns all of them,
|
||||||
|
// maybe we need to generate consumables instead.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
List* shop_jokers_list = &s_shop_jokers_list;
|
|
||||||
|
|
||||||
list_clear(shop_jokers_list);
|
|
||||||
*shop_jokers_list = list_init();
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_SHOP_JOKERS; i++)
|
|
||||||
{
|
|
||||||
int joker_id = 0;
|
|
||||||
#ifdef TEST_JOKER_ID0 // Allow defining an ID for a joker to always appear in shop and be tested
|
|
||||||
if (is_shop_joker_avail(TEST_JOKER_ID0))
|
|
||||||
{
|
|
||||||
joker_id = TEST_JOKER_ID0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
#ifdef TEST_JOKER_ID1
|
|
||||||
if (is_shop_joker_avail(TEST_JOKER_ID1))
|
|
||||||
{
|
|
||||||
joker_id = TEST_JOKER_ID1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
joker_id = game_shop_get_rand_available_joker_id();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If for some reason only no joker is left, don't make another
|
item->x = int2fx(SHOP_JOKER_SPRITES_INIT_POS.x + i * CARD_SPRITE_SIZE);
|
||||||
if (joker_id == UNDEFINED)
|
item->y = int2fx(SHOP_JOKER_SPRITES_INIT_POS.y);
|
||||||
break;
|
item->tx = item->x;
|
||||||
|
item->ty = int2fx(ITEM_SHOP_Y);
|
||||||
|
|
||||||
game_shop_set_joker_avail(joker_id, false);
|
item_print_buy_price_under(item);
|
||||||
|
|
||||||
JokerObject* joker_object = joker_object_new(joker_new(joker_id));
|
list_push_back(shop_items_list, item);
|
||||||
|
|
||||||
joker_object->sprite_object->x =
|
|
||||||
int2fx(SHOP_JOKER_SPRITES_INIT_POS.x + i * CARD_SPRITE_SIZE);
|
|
||||||
joker_object->sprite_object->y = int2fx(SHOP_JOKER_SPRITES_INIT_POS.y);
|
|
||||||
joker_object->sprite_object->tx = joker_object->sprite_object->x;
|
|
||||||
joker_object->sprite_object->ty = int2fx(ITEM_SHOP_Y);
|
|
||||||
|
|
||||||
sprite_object_print_price_under(joker_object->sprite_object, joker_object->joker->value);
|
|
||||||
|
|
||||||
list_push_back(shop_jokers_list, joker_object);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,14 +284,14 @@ static void game_shop_intro()
|
|||||||
{
|
{
|
||||||
main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_UP);
|
main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_UP);
|
||||||
|
|
||||||
if (timer == TM_CREATE_SHOP_ITEMS_WAIT)
|
if (s_timer == TM_CREATE_SHOP_ITEMS_WAIT)
|
||||||
{
|
{
|
||||||
game_shop_create_items();
|
game_shop_create_top_row_items();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer >= TM_SHIFT_SHOP_ICON_WAIT) // Shift the shop icon
|
if (s_timer >= TM_SHIFT_SHOP_ICON_WAIT) // Shift the shop icon
|
||||||
{
|
{
|
||||||
int timer_offset = timer - 6;
|
int timer_offset = s_timer - 6;
|
||||||
|
|
||||||
// TODO: Extract to generic function?
|
// TODO: Extract to generic function?
|
||||||
for (int y = 0; y < timer_offset; y++)
|
for (int y = 0; y < timer_offset; y++)
|
||||||
@@ -396,10 +307,10 @@ static void game_shop_intro()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer == TM_END_GAME_SHOP_INTRO)
|
if (s_timer == TM_END_GAME_SHOP_INTRO)
|
||||||
{
|
{
|
||||||
state_machine_change_state(&shop_sm, GAME_SHOP_ACTIVE);
|
state_machine_change_state(&shop_sm, GAME_SHOP_ACTIVE);
|
||||||
timer = TM_ZERO; // Reset the timer
|
s_timer = TM_ZERO; // Reset the timer
|
||||||
|
|
||||||
// print initial reroll cost only when the panel is in place
|
// print initial reroll cost only when the panel is in place
|
||||||
tte_printf(
|
tte_printf(
|
||||||
@@ -407,7 +318,7 @@ static void game_shop_intro()
|
|||||||
SHOP_REROLL_RECT.left,
|
SHOP_REROLL_RECT.left,
|
||||||
SHOP_REROLL_RECT.top,
|
SHOP_REROLL_RECT.top,
|
||||||
TTE_WHITE_PB,
|
TTE_WHITE_PB,
|
||||||
reroll_cost
|
s_reroll_cost
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,32 +330,23 @@ static void game_shop_intro()
|
|||||||
static int shop_top_row_get_size(void)
|
static int shop_top_row_get_size(void)
|
||||||
{
|
{
|
||||||
// + 1 to account for next round button
|
// + 1 to account for next round button
|
||||||
return list_get_len(&s_shop_jokers_list) + 1;
|
return list_get_len(&s_shop_items_list) + 1;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Add a newly purchased Joker to the list of owned Jokers.
|
|
||||||
*/
|
|
||||||
static inline void add_to_held_jokers(JokerObject* joker_object)
|
|
||||||
{
|
|
||||||
joker_object->sprite_object->ty = int2fx(HELD_JOKERS_POS.y);
|
|
||||||
add_joker(joker_object);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Called when pressing A on a Shop Joker to buy it.
|
* @brief Called when pressing A on a Shop Joker to buy it.
|
||||||
*/
|
*/
|
||||||
static inline void game_shop_buy_joker(int shop_joker_idx)
|
static inline void game_shop_buy_item(int shop_item_idx)
|
||||||
{
|
{
|
||||||
List* shop_jokers_list = &s_shop_jokers_list;
|
List* shop_items_list = &s_shop_items_list;
|
||||||
JokerObject* joker_object = (JokerObject*)list_get_at_idx(shop_jokers_list, shop_joker_idx);
|
Item* item = (Item*)list_get_at_idx(shop_items_list, shop_item_idx);
|
||||||
|
|
||||||
g_game_vars.money -= joker_object->joker->value;
|
g_game_vars.money -= item_get_buy_price(item);
|
||||||
display_money();
|
display_money();
|
||||||
sprite_object_erase_text_under(joker_object->sprite_object);
|
sprite_object_erase_text_under((SpriteObject*)item);
|
||||||
sprite_object_set_focus(joker_object->sprite_object, false);
|
sprite_object_set_focus((SpriteObject*)item, false);
|
||||||
add_to_held_jokers(joker_object);
|
item_acquire(item);
|
||||||
list_remove_at_idx(shop_jokers_list, shop_joker_idx); // Remove the joker from the shop
|
list_remove_at_idx(shop_items_list, shop_item_idx); // Remove the joker from the shop
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -461,16 +363,14 @@ static void shop_top_row_on_key_transit(SelectionGrid* selection_grid, Selection
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int shop_joker_idx = selection->x - 1; // - 1 to account for next round button
|
int shop_item_idx = selection->x - 1; // - 1 to account for next round button
|
||||||
JokerObject* joker_object =
|
Item* item = (Item*)list_get_at_idx(&s_shop_items_list, shop_item_idx);
|
||||||
(JokerObject*)list_get_at_idx(&s_shop_jokers_list, shop_joker_idx);
|
if (!item_can_acquire(item) || g_game_vars.money < item_get_buy_price(item))
|
||||||
if (joker_object == NULL || list_get_len(get_jokers_list()) >= MAX_JOKERS_HELD_SIZE ||
|
|
||||||
g_game_vars.money < joker_object->joker->value)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
game_shop_buy_joker(shop_joker_idx);
|
game_shop_buy_item(shop_item_idx);
|
||||||
selection_grid_move_selection_horz(selection_grid, -1);
|
selection_grid_move_selection_horz(selection_grid, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,7 +385,7 @@ static bool shop_top_row_on_selection_changed(
|
|||||||
const Selection* new_selection
|
const Selection* new_selection
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
List* shop_jokers_list = &s_shop_jokers_list;
|
List* shop_items_list = &s_shop_items_list;
|
||||||
// Guard if we move down while on jokers
|
// Guard if we move down while on jokers
|
||||||
if (new_selection->y > row_idx && prev_selection->x > 0)
|
if (new_selection->y > row_idx && prev_selection->x > 0)
|
||||||
return false;
|
return false;
|
||||||
@@ -503,8 +403,8 @@ static bool shop_top_row_on_selection_changed(
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
int idx = prev_selection->x - 1; // -1 to account for next round button
|
int idx = prev_selection->x - 1; // -1 to account for next round button
|
||||||
JokerObject* joker_object = (JokerObject*)list_get_at_idx(shop_jokers_list, idx);
|
SpriteObject* sprite_object = (SpriteObject*)list_get_at_idx(shop_items_list, idx);
|
||||||
sprite_object_set_focus(joker_object->sprite_object, false);
|
sprite_object_set_focus(sprite_object, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,8 +417,8 @@ static bool shop_top_row_on_selection_changed(
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
int idx = new_selection->x - 1; // -1 to account for next round button
|
int idx = new_selection->x - 1; // -1 to account for next round button
|
||||||
JokerObject* joker_object = (JokerObject*)list_get_at_idx(shop_jokers_list, idx);
|
SpriteObject* sprite_object = (SpriteObject*)list_get_at_idx(shop_items_list, idx);
|
||||||
sprite_object_set_focus(joker_object->sprite_object, true);
|
sprite_object_set_focus(sprite_object, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,8 +452,8 @@ static bool shop_reroll_row_on_selection_changed(
|
|||||||
if (new_selection->x != NEXT_ROUND_BTN_SEL_X)
|
if (new_selection->x != NEXT_ROUND_BTN_SEL_X)
|
||||||
{
|
{
|
||||||
int idx = new_selection->x - 1;
|
int idx = new_selection->x - 1;
|
||||||
JokerObject* joker_object = (JokerObject*)list_get_at_idx(&s_shop_jokers_list, idx);
|
SpriteObject* sprite_object = (SpriteObject*)list_get_at_idx(&s_shop_items_list, idx);
|
||||||
sprite_object_set_focus(joker_object->sprite_object, true);
|
sprite_object_set_focus(sprite_object, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (row_idx == new_selection->y)
|
else if (row_idx == new_selection->y)
|
||||||
@@ -570,48 +470,46 @@ static bool shop_reroll_row_on_selection_changed(
|
|||||||
*/
|
*/
|
||||||
static inline void game_shop_reroll(void)
|
static inline void game_shop_reroll(void)
|
||||||
{
|
{
|
||||||
g_game_vars.money -= reroll_cost;
|
g_game_vars.money -= s_reroll_cost;
|
||||||
display_money(); // Update the money display
|
display_money(); // Update the money display
|
||||||
|
|
||||||
List* shop_jokers_list = &s_shop_jokers_list;
|
List* shop_items_list = &s_shop_items_list;
|
||||||
ListItr itr = list_itr_create(shop_jokers_list);
|
ListItr itr = list_itr_create(shop_items_list);
|
||||||
JokerObject* joker_object;
|
Item* item;
|
||||||
|
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((item = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
if (joker_object != NULL)
|
if (item != NULL && item->type == ITEM_TYPE_JOKER)
|
||||||
{
|
{
|
||||||
game_shop_set_joker_avail(joker_object->joker->id, true);
|
item_dispose(&item);
|
||||||
joker_object_destroy(&joker_object); // Destroy the joker object if it exists
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list_clear(shop_jokers_list);
|
list_clear(shop_items_list);
|
||||||
*shop_jokers_list = list_init();
|
*shop_items_list = list_init();
|
||||||
|
|
||||||
game_shop_create_items();
|
game_shop_create_top_row_items();
|
||||||
|
|
||||||
itr = list_itr_create(shop_jokers_list);
|
itr = list_itr_create(shop_items_list);
|
||||||
|
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
SpriteObject* item_sprite_object;
|
||||||
|
while ((item_sprite_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
if (joker_object != NULL)
|
if (item_sprite_object != NULL)
|
||||||
{
|
{
|
||||||
// Set the y position to the target position
|
item_sprite_object->y = item_sprite_object->ty;
|
||||||
joker_object->sprite_object->y = joker_object->sprite_object->ty;
|
|
||||||
|
|
||||||
// Give the joker a little wiggle animation
|
sprite_object_shake(item_sprite_object, UNDEFINED);
|
||||||
joker_object_shake(joker_object, UNDEFINED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reroll_cost++;
|
s_reroll_cost++;
|
||||||
tte_printf(
|
tte_printf(
|
||||||
"#{P:%d,%d; cx:0x%X000}$%d",
|
"#{P:%d,%d; cx:0x%X000}$%d",
|
||||||
SHOP_REROLL_RECT.left,
|
SHOP_REROLL_RECT.left,
|
||||||
SHOP_REROLL_RECT.top,
|
SHOP_REROLL_RECT.top,
|
||||||
TTE_WHITE_PB,
|
TTE_WHITE_PB,
|
||||||
reroll_cost
|
s_reroll_cost
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,8 +530,8 @@ static void next_round_on_pressed(void)
|
|||||||
{
|
{
|
||||||
// Go to next blind selection game state
|
// Go to next blind selection game state
|
||||||
state_machine_change_state(&shop_sm, GAME_SHOP_EXIT);
|
state_machine_change_state(&shop_sm, GAME_SHOP_EXIT);
|
||||||
timer = TM_ZERO;
|
s_timer = TM_ZERO;
|
||||||
reroll_cost = REROLL_BASE_COST;
|
s_reroll_cost = REROLL_BASE_COST;
|
||||||
|
|
||||||
pal_bg_mem[NEXT_ROUND_BTN_SELECTED_BORDER_PAL_IDX] = pal_bg_mem[SHOP_PANEL_SHADOW_PAL_IDX];
|
pal_bg_mem[NEXT_ROUND_BTN_SELECTED_BORDER_PAL_IDX] = pal_bg_mem[SHOP_PANEL_SHADOW_PAL_IDX];
|
||||||
}
|
}
|
||||||
@@ -646,7 +544,7 @@ static void reroll_on_pressed(void)
|
|||||||
|
|
||||||
static bool reroll_can_be_pressed(void)
|
static bool reroll_can_be_pressed(void)
|
||||||
{
|
{
|
||||||
return g_game_vars.money >= reroll_cost;
|
return g_game_vars.money >= s_reroll_cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -664,7 +562,7 @@ static void game_shop_process_user_input(void)
|
|||||||
// Owned Joker
|
// Owned Joker
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
description_card_original_list = get_jokers_list();
|
s_description_card_original_list = get_jokers_list();
|
||||||
tmp_card = list_get_at_idx(get_jokers_list(), shop_selection_grid.selection.x);
|
tmp_card = list_get_at_idx(get_jokers_list(), shop_selection_grid.selection.x);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -672,9 +570,9 @@ static void game_shop_process_user_input(void)
|
|||||||
// Jokers for sale
|
// Jokers for sale
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
description_card_original_list = &s_shop_jokers_list;
|
s_description_card_original_list = &s_shop_items_list;
|
||||||
tmp_card = (shop_selection_grid.selection.x > 0)
|
tmp_card = (shop_selection_grid.selection.x > 0)
|
||||||
? list_get_at_idx(&s_shop_jokers_list, shop_selection_grid.selection.x - 1)
|
? list_get_at_idx(&s_shop_items_list, shop_selection_grid.selection.x - 1)
|
||||||
: NULL;
|
: NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -683,7 +581,7 @@ static void game_shop_process_user_input(void)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
description_card_original_list = NULL;
|
s_description_card_original_list = NULL;
|
||||||
tmp_card = NULL;
|
tmp_card = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -692,14 +590,13 @@ static void game_shop_process_user_input(void)
|
|||||||
// Show description of selected card when pressing B.
|
// Show description of selected card when pressing B.
|
||||||
// Always wait for the card in question to be immobile to avoid accumulating
|
// Always wait for the card in question to be immobile to avoid accumulating
|
||||||
// errors when pressing and releasing B in quick succession.
|
// errors when pressing and releasing B in quick succession.
|
||||||
if (tmp_card != NULL && tmp_card->sprite_object->vx == 0 && tmp_card->sprite_object->vy == 0 &&
|
if (tmp_card != NULL && tmp_card->vx == 0 && tmp_card->vy == 0 && key_held(DESELECT_CARDS))
|
||||||
key_held(DESELECT_CARDS))
|
|
||||||
{
|
{
|
||||||
description_card = tmp_card;
|
s_description_card = tmp_card;
|
||||||
description_card_original_x_pos = description_card->sprite_object->x;
|
s_description_card_original_x_pos = s_description_card->x;
|
||||||
description_card_original_y_pos = description_card->sprite_object->y;
|
s_description_card_original_y_pos = s_description_card->y;
|
||||||
|
|
||||||
timer = TM_ZERO;
|
s_timer = TM_ZERO;
|
||||||
state_machine_change_state(&shop_sm, GAME_SHOP_SHOW_CARD_DESC);
|
state_machine_change_state(&shop_sm, GAME_SHOP_SHOW_CARD_DESC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -707,7 +604,7 @@ static void game_shop_process_user_input(void)
|
|||||||
static void game_shop_show_card_desc(void)
|
static void game_shop_show_card_desc(void)
|
||||||
{
|
{
|
||||||
// Anim start
|
// Anim start
|
||||||
if (timer == 1)
|
if (s_timer == 1)
|
||||||
{
|
{
|
||||||
// Erase shop text and disable transparency window
|
// Erase shop text and disable transparency window
|
||||||
|
|
||||||
@@ -722,29 +619,29 @@ static void game_shop_show_card_desc(void)
|
|||||||
ListItr itr = list_itr_create(get_jokers_list());
|
ListItr itr = list_itr_create(get_jokers_list());
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
if (joker_object != description_card)
|
if (joker_object != s_description_card)
|
||||||
joker_object->sprite_object->ty -= int2fx(OWNED_CARDS_HIDE_Y_OFFSET);
|
joker_object->ty -= int2fx(OWNED_CARDS_HIDE_Y_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shop Jokers
|
// Shop Jokers
|
||||||
itr = list_itr_create(&s_shop_jokers_list);
|
itr = list_itr_create(&s_shop_items_list);
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
if (joker_object != description_card)
|
if (joker_object != s_description_card)
|
||||||
joker_object->sprite_object->ty = int2fx(SHOP_JOKER_SPRITES_INIT_POS.y + TILE_SIZE);
|
joker_object->ty = int2fx(SHOP_JOKER_SPRITES_INIT_POS.y + TILE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set description_card new target position
|
// Set description_card new target position
|
||||||
|
|
||||||
description_card->sprite_object->tx = int2fx(CARD_DESCRIPTION_SPRITE_POS.x);
|
s_description_card->tx = int2fx(CARD_DESCRIPTION_SPRITE_POS.x);
|
||||||
description_card->sprite_object->ty = int2fx(CARD_DESCRIPTION_SPRITE_POS.y);
|
s_description_card->ty = int2fx(CARD_DESCRIPTION_SPRITE_POS.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First 12 anim frames
|
// First 12 anim frames
|
||||||
if (timer <= TM_SHOW_CARD_DESC_WAIT)
|
if (s_timer <= TM_SHOW_CARD_DESC_WAIT)
|
||||||
{
|
{
|
||||||
// Hide Deck (last 5 frames only)
|
// Hide Deck (last 5 frames only)
|
||||||
if (TM_SHOW_CARD_DESC_WAIT - timer < 5)
|
if (TM_SHOW_CARD_DESC_WAIT - s_timer < 5)
|
||||||
main_bg_se_move_rect_1_tile_vert(DECK_ANIM_RECT, SCREEN_DOWN);
|
main_bg_se_move_rect_1_tile_vert(DECK_ANIM_RECT, SCREEN_DOWN);
|
||||||
// Hide shop panel
|
// Hide shop panel
|
||||||
main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_DOWN);
|
main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_DOWN);
|
||||||
@@ -753,13 +650,13 @@ static void game_shop_show_card_desc(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Anim end
|
// Anim end
|
||||||
else if (timer == TM_SHOW_CARD_DESC_WAIT + 1)
|
else if (s_timer == TM_SHOW_CARD_DESC_WAIT + 1)
|
||||||
{
|
{
|
||||||
// Compute needed space for the description
|
// Compute needed space for the description
|
||||||
const JokerInfo* info = get_joker_registry_entry(description_card->joker->id);
|
const JokerInfo* info = get_joker_registry_entry(s_description_card->joker->id);
|
||||||
int desc_bottom_offset =
|
int desc_bottom_offset =
|
||||||
CARD_DESC_MAX_TEXT_HEIGHT -
|
CARD_DESC_MAX_TEXT_HEIGHT -
|
||||||
info->joker_print_desc(description_card->joker, CARD_DESC_TEXT_RECT);
|
info->joker_print_desc(s_description_card->joker, CARD_DESC_TEXT_RECT);
|
||||||
|
|
||||||
// Print Rarity and change color or the panel
|
// Print Rarity and change color or the panel
|
||||||
// Do it before drawing the panel so the color is already set
|
// Do it before drawing the panel so the color is already set
|
||||||
@@ -795,10 +692,10 @@ static void game_shop_show_card_desc(void)
|
|||||||
|
|
||||||
// Actively wait for the B button to be released, but only if the described card has stopped
|
// Actively wait for the B button to be released, but only if the described card has stopped
|
||||||
// moving
|
// moving
|
||||||
else if (description_card->sprite_object->vx == 0 && description_card->sprite_object->vy == 0 &&
|
else if (s_description_card->vx == 0 && s_description_card->vy == 0 &&
|
||||||
!key_held(DESELECT_CARDS))
|
!key_held(DESELECT_CARDS))
|
||||||
{
|
{
|
||||||
timer = TM_ZERO;
|
s_timer = TM_ZERO;
|
||||||
state_machine_change_state(&shop_sm, GAME_SHOP_HIDE_CARD_DESC);
|
state_machine_change_state(&shop_sm, GAME_SHOP_HIDE_CARD_DESC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -809,7 +706,7 @@ static void game_shop_hide_card_desc(void)
|
|||||||
static bool owned_joker_price_printed = false;
|
static bool owned_joker_price_printed = false;
|
||||||
|
|
||||||
// Anim start
|
// Anim start
|
||||||
if (timer == 1)
|
if (s_timer == 1)
|
||||||
{
|
{
|
||||||
// Erase shop text and Joker Description frame
|
// Erase shop text and Joker Description frame
|
||||||
tte_erase_rect_wrapper(PLAYING_SCREEN_RECT);
|
tte_erase_rect_wrapper(PLAYING_SCREEN_RECT);
|
||||||
@@ -832,52 +729,49 @@ static void game_shop_hide_card_desc(void)
|
|||||||
ListItr itr = list_itr_create(get_jokers_list());
|
ListItr itr = list_itr_create(get_jokers_list());
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
if (joker_object != description_card)
|
if (joker_object != s_description_card)
|
||||||
joker_object->sprite_object->ty = int2fx(HELD_JOKERS_POS.y);
|
joker_object->ty = int2fx(HELD_JOKERS_POS.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shop Jokers
|
// Shop Jokers
|
||||||
itr = list_itr_create(&s_shop_jokers_list);
|
itr = list_itr_create(&s_shop_items_list);
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
if (joker_object != description_card)
|
if (joker_object != s_description_card)
|
||||||
joker_object->sprite_object->ty = int2fx(ITEM_SHOP_Y);
|
joker_object->ty = int2fx(ITEM_SHOP_Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
description_card->sprite_object->tx = description_card_original_x_pos;
|
s_description_card->tx = s_description_card_original_x_pos;
|
||||||
description_card->sprite_object->ty = description_card_original_y_pos;
|
s_description_card->ty = s_description_card_original_y_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First 12 anim frames
|
// First 12 anim frames
|
||||||
if (timer <= TM_SHOW_CARD_DESC_WAIT)
|
if (s_timer <= TM_SHOW_CARD_DESC_WAIT)
|
||||||
{
|
{
|
||||||
// Show Deck (last 5 frames only)
|
// Show Deck (last 5 frames only)
|
||||||
if (TM_SHOW_CARD_DESC_WAIT - timer < 5)
|
if (TM_SHOW_CARD_DESC_WAIT - s_timer < 5)
|
||||||
main_bg_se_move_rect_1_tile_vert(DECK_ANIM_RECT, SCREEN_UP);
|
main_bg_se_move_rect_1_tile_vert(DECK_ANIM_RECT, SCREEN_UP);
|
||||||
// Show shop panel
|
// Show shop panel
|
||||||
main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_UP);
|
main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last anim frame (no need to wait for the Joker to have stopped for this):
|
// Last anim frame (no need to wait for the Joker to have stopped for this):
|
||||||
else if (timer == TM_SHOW_CARD_DESC_WAIT + 1)
|
else if (s_timer == TM_SHOW_CARD_DESC_WAIT + 1)
|
||||||
{
|
{
|
||||||
// Need to account for the description_card being selected if it came from the shop.
|
// Need to account for the description_card being selected if it came from the shop.
|
||||||
if (description_card_original_list == &s_shop_jokers_list)
|
if (s_description_card_original_list == &s_shop_items_list)
|
||||||
description_card->sprite_object->ty += int2fx(TILE_SIZE);
|
s_description_card->ty += int2fx(TILE_SIZE);
|
||||||
|
|
||||||
// Print price under shop Jokers
|
// Print price under shop Jokers
|
||||||
JokerObject* joker_object = NULL;
|
Item* item = NULL;
|
||||||
ListItr itr = list_itr_create(&s_shop_jokers_list);
|
ListItr itr = list_itr_create(&s_shop_items_list);
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((item = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
sprite_object_print_price_under(
|
item_print_buy_price_under(item);
|
||||||
joker_object->sprite_object,
|
|
||||||
joker_object->joker->value
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (description_card_original_list == &s_shop_jokers_list)
|
if (s_description_card_original_list == &s_shop_items_list)
|
||||||
description_card->sprite_object->ty -= int2fx(TILE_SIZE);
|
s_description_card->ty -= int2fx(TILE_SIZE);
|
||||||
|
|
||||||
// Print Reroll prince
|
// Print Reroll prince
|
||||||
tte_printf(
|
tte_printf(
|
||||||
@@ -885,7 +779,7 @@ static void game_shop_hide_card_desc(void)
|
|||||||
SHOP_REROLL_RECT.left,
|
SHOP_REROLL_RECT.left,
|
||||||
SHOP_REROLL_RECT.top,
|
SHOP_REROLL_RECT.top,
|
||||||
TTE_WHITE_PB,
|
TTE_WHITE_PB,
|
||||||
reroll_cost
|
s_reroll_cost
|
||||||
);
|
);
|
||||||
|
|
||||||
// Print Deck size that was erased
|
// Print Deck size that was erased
|
||||||
@@ -893,23 +787,23 @@ static void game_shop_hide_card_desc(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup and change state
|
// Cleanup and change state
|
||||||
else if (description_card->sprite_object->vx == 0 && description_card->sprite_object->vy == 0)
|
else if (s_description_card->vx == 0 && s_description_card->vy == 0)
|
||||||
{
|
{
|
||||||
owned_joker_price_printed = false;
|
owned_joker_price_printed = false;
|
||||||
description_card = NULL;
|
s_description_card = NULL;
|
||||||
timer = TM_ZERO;
|
s_timer = TM_ZERO;
|
||||||
state_machine_change_state(&shop_sm, GAME_SHOP_ACTIVE);
|
state_machine_change_state(&shop_sm, GAME_SHOP_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// At any point after the other prices have been printed, and while the card is still moving,
|
// At any point after the other prices have been printed, and while the card is still moving,
|
||||||
// if we are NOT pressing A, print the price under it.
|
// if we are NOT pressing A, print the price under it.
|
||||||
else if (!owned_joker_price_printed && !key_held(SELECT_CARD) &&
|
else if (!owned_joker_price_printed && !key_held(SELECT_CARD) &&
|
||||||
description_card_original_list == get_jokers_list())
|
s_description_card_original_list == get_jokers_list())
|
||||||
{
|
{
|
||||||
owned_joker_price_printed = true;
|
owned_joker_price_printed = true;
|
||||||
sprite_object_print_price_under(
|
sprite_object_print_price_under(
|
||||||
description_card->sprite_object,
|
(SpriteObject*)s_description_card,
|
||||||
description_card->joker->value
|
joker_get_sell_value(s_description_card->joker)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -925,33 +819,33 @@ static void game_shop_outro(void)
|
|||||||
|
|
||||||
main_bg_se_copy_rect_1_tile_vert(TOP_LEFT_PANEL_ANIM_RECT, SCREEN_UP);
|
main_bg_se_copy_rect_1_tile_vert(TOP_LEFT_PANEL_ANIM_RECT, SCREEN_UP);
|
||||||
|
|
||||||
// TODO: make heads or tails of what's going on here and replace
|
if (s_timer == 1)
|
||||||
// magic numbers.
|
|
||||||
if (timer == 1)
|
|
||||||
{
|
{
|
||||||
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); // Erase the shop prices text
|
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); // Erase the shop prices text
|
||||||
|
|
||||||
ListItr itr = list_itr_create(&s_shop_jokers_list);
|
ListItr itr = list_itr_create(&s_shop_items_list);
|
||||||
JokerObject* joker_object;
|
SpriteObject* shop_item;
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((shop_item = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
if (joker_object != NULL)
|
if (shop_item != NULL)
|
||||||
{
|
{
|
||||||
joker_object->sprite_object->ty = int2fx(160);
|
shop_item->ty = int2fx(160);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset_top_left_panel_bottom_row();
|
reset_top_left_panel_bottom_row();
|
||||||
}
|
}
|
||||||
else if (timer == 2)
|
else if (s_timer == 2)
|
||||||
{
|
{
|
||||||
|
// TODO: make heads or tails of what's going on here and replace
|
||||||
|
// magic numbers.
|
||||||
int y = 5;
|
int y = 5;
|
||||||
memset16(&se_mat[MAIN_BG_SBB][y - 1][0], 0x0001, 1);
|
memset16(&se_mat[MAIN_BG_SBB][y - 1][0], 0x0001, 1);
|
||||||
memset16(&se_mat[MAIN_BG_SBB][y - 1][1], 0x0002, 7);
|
memset16(&se_mat[MAIN_BG_SBB][y - 1][1], 0x0002, 7);
|
||||||
memset16(&se_mat[MAIN_BG_SBB][y - 1][8], SE_HFLIP | 0x0001, 1);
|
memset16(&se_mat[MAIN_BG_SBB][y - 1][8], SE_HFLIP | 0x0001, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer >= MENU_POP_OUT_ANIM_FRAMES)
|
if (s_timer >= MENU_POP_OUT_ANIM_FRAMES)
|
||||||
{
|
{
|
||||||
game_change_state(GAME_STATE_BLIND_SELECT);
|
game_change_state(GAME_STATE_BLIND_SELECT);
|
||||||
}
|
}
|
||||||
@@ -988,9 +882,9 @@ static inline void game_shop_lights_anim_frame(void)
|
|||||||
|
|
||||||
void game_shop_on_update(void)
|
void game_shop_on_update(void)
|
||||||
{
|
{
|
||||||
timer++;
|
s_timer++;
|
||||||
|
|
||||||
if (timer % 20 == 0)
|
if (s_timer % 20 == 0)
|
||||||
{
|
{
|
||||||
game_shop_lights_anim_frame();
|
game_shop_lights_anim_frame();
|
||||||
}
|
}
|
||||||
@@ -998,21 +892,16 @@ void game_shop_on_update(void)
|
|||||||
|
|
||||||
void game_shop_on_exit(void)
|
void game_shop_on_exit(void)
|
||||||
{
|
{
|
||||||
List* shop_jokers_list = &s_shop_jokers_list;
|
List* shop_items_list = &s_shop_items_list;
|
||||||
ListItr itr = list_itr_create(shop_jokers_list);
|
ListItr itr = list_itr_create(shop_items_list);
|
||||||
JokerObject* joker_object;
|
Item* item;
|
||||||
|
|
||||||
while ((joker_object = list_itr_next(&itr)))
|
while ((item = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
if (joker_object != NULL)
|
item_dispose(&item);
|
||||||
{
|
|
||||||
// Make the joker available back to shop
|
|
||||||
game_shop_set_joker_avail(joker_object->joker->id, true);
|
|
||||||
}
|
|
||||||
joker_object_destroy(&joker_object); // Destroy the joker objects
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list_clear(shop_jokers_list);
|
list_clear(shop_items_list);
|
||||||
|
|
||||||
increment_blind(BLIND_STATE_DEFEATED); // TODO: Move to game_round_end()?
|
increment_blind(BLIND_STATE_DEFEATED); // TODO: Move to game_round_end()?
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include <tonc_tte.h>
|
#include <tonc_tte.h>
|
||||||
|
|
||||||
static const Rect FULL_SCREENBLOCK_RECT = {0, 0, SE_ROW_LEN - 1, SE_COL_LEN - 1};
|
static const Rect FULL_SCREENBLOCK_RECT = {0, 0, SE_ROW_LEN - 1, SE_COL_LEN - 1};
|
||||||
static const Rect TOP_LEFT_PANEL_BOTTOM_ROW_RESET_RECT = {0, 28, 8, 28};
|
|
||||||
|
|
||||||
static void clip_se_rect_to_screenblock(Rect* rect);
|
static void clip_se_rect_to_screenblock(Rect* rect);
|
||||||
static void bg_se_copy_or_move_rect_1_tile_vert(
|
static void bg_se_copy_or_move_rect_1_tile_vert(
|
||||||
@@ -501,14 +500,6 @@ void main_bg_se_clear_rect(Rect se_rect)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_top_left_panel_bottom_row(void)
|
|
||||||
{
|
|
||||||
BG_POINT top_left_panel_bottom_row_pos = TOP_LEFT_PANEL_POINT;
|
|
||||||
// Use the source rect height to offset to the bottom row point
|
|
||||||
top_left_panel_bottom_row_pos.y += rect_height(&TOP_LEFT_ITEM_SRC_RECT) - 1;
|
|
||||||
main_bg_se_copy_rect(TOP_LEFT_PANEL_BOTTOM_ROW_RESET_RECT, top_left_panel_bottom_row_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Width of the screen, in nb of tiles
|
// Width of the screen, in nb of tiles
|
||||||
#define MAX_LINE_TEXT_LENGTH 30
|
#define MAX_LINE_TEXT_LENGTH 30
|
||||||
|
|
||||||
|
|||||||
+78
-75
@@ -24,7 +24,7 @@ typedef struct
|
|||||||
char* display_name;
|
char* display_name;
|
||||||
} HandValues;
|
} HandValues;
|
||||||
|
|
||||||
static const HandValues hand_base_values[] = {
|
static const HandValues HAND_BASE_VALUES[] = {
|
||||||
{.chips = 0, .mult = 0, .display_name = NULL }, // NONE
|
{.chips = 0, .mult = 0, .display_name = NULL }, // NONE
|
||||||
{.chips = 5, .mult = 1, .display_name = "Hi-Card"}, // HIGH_CARD
|
{.chips = 5, .mult = 1, .display_name = "Hi-Card"}, // HIGH_CARD
|
||||||
{.chips = 10, .mult = 2, .display_name = "Pair" }, // PAIR
|
{.chips = 10, .mult = 2, .display_name = "Pair" }, // PAIR
|
||||||
@@ -61,7 +61,7 @@ typedef struct Hand
|
|||||||
bool sort_by_suit;
|
bool sort_by_suit;
|
||||||
} Hand;
|
} Hand;
|
||||||
|
|
||||||
static Hand hand = {
|
static Hand s_hand = {
|
||||||
.cards = {NULL},
|
.cards = {NULL},
|
||||||
.hand_top = -1,
|
.hand_top = -1,
|
||||||
.hand_selections = 0,
|
.hand_selections = 0,
|
||||||
@@ -75,56 +75,66 @@ static Hand hand = {
|
|||||||
static ContainedHandTypes compute_contained_hand_types(void);
|
static ContainedHandTypes compute_contained_hand_types(void);
|
||||||
static enum HandType compute_hand_type(struct ContainedHandTypes contained_types);
|
static enum HandType compute_hand_type(struct ContainedHandTypes contained_types);
|
||||||
|
|
||||||
|
// Misc Hand Functions
|
||||||
|
|
||||||
|
const char* get_hand_type_name(enum HandType hand_type)
|
||||||
|
{
|
||||||
|
if (hand_type <= NONE || hand_type > FLUSH_FIVE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return HAND_BASE_VALUES[hand_type].display_name;
|
||||||
|
}
|
||||||
|
|
||||||
// Hand Struct Manipulation
|
// Hand Struct Manipulation
|
||||||
|
|
||||||
enum HandState get_hand_state(void)
|
enum HandState get_hand_state(void)
|
||||||
{
|
{
|
||||||
return hand.state;
|
return s_hand.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_hand_state(enum HandState new_hand_state)
|
void set_hand_state(enum HandState new_hand_state)
|
||||||
{
|
{
|
||||||
hand.state = new_hand_state;
|
s_hand.state = new_hand_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardObject** get_hand_array(void)
|
CardObject** get_hand_array(void)
|
||||||
{
|
{
|
||||||
return hand.cards;
|
return s_hand.cards;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_hand_top(void)
|
int get_hand_top(void)
|
||||||
{
|
{
|
||||||
return hand.hand_top;
|
return s_hand.hand_top;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_hand_top(int new_hand_top)
|
void set_hand_top(int new_hand_top)
|
||||||
{
|
{
|
||||||
hand.hand_top = new_hand_top;
|
s_hand.hand_top = new_hand_top;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hand_nb_held_cards(void)
|
int hand_nb_held_cards(void)
|
||||||
{
|
{
|
||||||
return hand.hand_top + 1;
|
return s_hand.hand_top + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hand_get_nb_selected_cards(void)
|
int hand_get_nb_selected_cards(void)
|
||||||
{
|
{
|
||||||
return hand.hand_selections;
|
return s_hand.hand_selections;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hand_set_nb_selected_cards(int new_selections)
|
void hand_set_nb_selected_cards(int new_selections)
|
||||||
{
|
{
|
||||||
hand.hand_selections = new_selections;
|
s_hand.hand_selections = new_selections;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum HandType get_hand_type(void)
|
enum HandType get_hand_type(void)
|
||||||
{
|
{
|
||||||
return hand.hand_type;
|
return s_hand.hand_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainedHandTypes* get_contained_hands(void)
|
ContainedHandTypes* get_contained_hands(void)
|
||||||
{
|
{
|
||||||
return &hand.contained_hands;
|
return &s_hand.contained_hands;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_hand_type(const char* hand_type_str)
|
static void print_hand_type(const char* hand_type_str)
|
||||||
@@ -146,10 +156,10 @@ static void print_hand_type(const char* hand_type_str)
|
|||||||
void compute_hand_value_info(void)
|
void compute_hand_value_info(void)
|
||||||
{
|
{
|
||||||
tte_erase_rect_wrapper(HAND_TYPE_RECT);
|
tte_erase_rect_wrapper(HAND_TYPE_RECT);
|
||||||
hand.contained_hands = compute_contained_hand_types();
|
s_hand.contained_hands = compute_contained_hand_types();
|
||||||
hand.hand_type = compute_hand_type(hand.contained_hands);
|
s_hand.hand_type = compute_hand_type(s_hand.contained_hands);
|
||||||
|
|
||||||
HandValues hand_values = hand_base_values[hand.hand_type];
|
HandValues hand_values = HAND_BASE_VALUES[s_hand.hand_type];
|
||||||
|
|
||||||
g_game_vars.chips = hand_values.chips;
|
g_game_vars.chips = hand_values.chips;
|
||||||
g_game_vars.mult = hand_values.mult;
|
g_game_vars.mult = hand_values.mult;
|
||||||
@@ -163,22 +173,22 @@ void compute_hand_value_info(void)
|
|||||||
// no checks will be performed here for performance's sake
|
// no checks will be performed here for performance's sake
|
||||||
void swap_cards_in_hand(int idx_a, int idx_b)
|
void swap_cards_in_hand(int idx_a, int idx_b)
|
||||||
{
|
{
|
||||||
CardObject* temp = hand.cards[idx_a];
|
CardObject* temp = s_hand.cards[idx_a];
|
||||||
hand.cards[idx_a] = hand.cards[idx_b];
|
s_hand.cards[idx_a] = s_hand.cards[idx_b];
|
||||||
hand.cards[idx_b] = temp;
|
s_hand.cards[idx_b] = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sort_hand_by_suit(void)
|
static inline void sort_hand_by_suit(void)
|
||||||
{
|
{
|
||||||
for (int idx_a = 0; idx_a < hand.hand_top; idx_a++)
|
for (int idx_a = 0; idx_a < s_hand.hand_top; idx_a++)
|
||||||
{
|
{
|
||||||
for (int idx_b = idx_a + 1; idx_b <= hand.hand_top; idx_b++)
|
for (int idx_b = idx_a + 1; idx_b <= s_hand.hand_top; idx_b++)
|
||||||
{
|
{
|
||||||
if (hand.cards[idx_a] == NULL ||
|
if (s_hand.cards[idx_a] == NULL ||
|
||||||
(hand.cards[idx_b] != NULL &&
|
(s_hand.cards[idx_b] != NULL &&
|
||||||
(hand.cards[idx_a]->card->suit > hand.cards[idx_b]->card->suit ||
|
(s_hand.cards[idx_a]->card->suit > s_hand.cards[idx_b]->card->suit ||
|
||||||
(hand.cards[idx_a]->card->suit == hand.cards[idx_b]->card->suit &&
|
(s_hand.cards[idx_a]->card->suit == s_hand.cards[idx_b]->card->suit &&
|
||||||
hand.cards[idx_a]->card->rank > hand.cards[idx_b]->card->rank))))
|
s_hand.cards[idx_a]->card->rank > s_hand.cards[idx_b]->card->rank))))
|
||||||
{
|
{
|
||||||
swap_cards_in_hand(idx_a, idx_b);
|
swap_cards_in_hand(idx_a, idx_b);
|
||||||
}
|
}
|
||||||
@@ -188,13 +198,13 @@ static inline void sort_hand_by_suit(void)
|
|||||||
|
|
||||||
static inline void sort_hand_by_rank(void)
|
static inline void sort_hand_by_rank(void)
|
||||||
{
|
{
|
||||||
for (int idx_a = 0; idx_a < hand.hand_top; idx_a++)
|
for (int idx_a = 0; idx_a < s_hand.hand_top; idx_a++)
|
||||||
{
|
{
|
||||||
for (int idx_b = idx_a + 1; idx_b <= hand.hand_top; idx_b++)
|
for (int idx_b = idx_a + 1; idx_b <= s_hand.hand_top; idx_b++)
|
||||||
{
|
{
|
||||||
if (hand.cards[idx_a] == NULL ||
|
if (s_hand.cards[idx_a] == NULL ||
|
||||||
(hand.cards[idx_b] != NULL &&
|
(s_hand.cards[idx_b] != NULL &&
|
||||||
hand.cards[idx_a]->card->rank > hand.cards[idx_b]->card->rank))
|
s_hand.cards[idx_a]->card->rank > s_hand.cards[idx_b]->card->rank))
|
||||||
{
|
{
|
||||||
swap_cards_in_hand(idx_a, idx_b);
|
swap_cards_in_hand(idx_a, idx_b);
|
||||||
}
|
}
|
||||||
@@ -207,25 +217,25 @@ static inline bool shift_null_card_to_end(int null_card_idx)
|
|||||||
// Start by searching any non NULL cards after the NULL one
|
// Start by searching any non NULL cards after the NULL one
|
||||||
// don't start at null_card_idx+1 to avoid potential illegal array access
|
// don't start at null_card_idx+1 to avoid potential illegal array access
|
||||||
int non_null_card_idx = null_card_idx;
|
int non_null_card_idx = null_card_idx;
|
||||||
for (; non_null_card_idx <= hand.hand_top; non_null_card_idx++)
|
for (; non_null_card_idx <= s_hand.hand_top; non_null_card_idx++)
|
||||||
{
|
{
|
||||||
if (hand.cards[non_null_card_idx] != NULL)
|
if (s_hand.cards[non_null_card_idx] != NULL)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// return false if there are no non-NULL cards left/there are no more sprites to destroy
|
// return false if there are no non-NULL cards left/there are no more sprites to destroy
|
||||||
if (non_null_card_idx > hand.hand_top)
|
if (non_null_card_idx > s_hand.hand_top)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is one, shift it and all the cards that follow forward
|
// If there is one, shift it and all the cards that follow forward
|
||||||
// This way we close the gap and ensure the next card is not NULL
|
// This way we close the gap and ensure the next card is not NULL
|
||||||
for (int j = 0; j <= hand.hand_top - non_null_card_idx; j++)
|
for (int j = 0; j <= s_hand.hand_top - non_null_card_idx; j++)
|
||||||
{
|
{
|
||||||
hand.cards[null_card_idx + j] = hand.cards[non_null_card_idx + j];
|
s_hand.cards[null_card_idx + j] = s_hand.cards[non_null_card_idx + j];
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -235,11 +245,11 @@ void reorder_card_sprites_layers(void)
|
|||||||
{
|
{
|
||||||
// Update the sprites in the hand by destroying them and creating new ones in the correct order
|
// Update the sprites in the hand by destroying them and creating new ones in the correct order
|
||||||
// (This feels like a diabolical solution but like literally how else would you do this)
|
// (This feels like a diabolical solution but like literally how else would you do this)
|
||||||
for (int i = 0; i <= hand.hand_top; i++)
|
for (int i = 0; i <= s_hand.hand_top; i++)
|
||||||
{
|
{
|
||||||
// a NULL card will only happen if we rearrange the sprites without having sorted them
|
// a NULL card will only happen if we rearrange the sprites without having sorted them
|
||||||
// before. Any NULL CardObject will be sent to the end by shifting all elements forward
|
// before. Any NULL CardObject will be sent to the end by shifting all elements forward
|
||||||
if (hand.cards[i] == NULL)
|
if (s_hand.cards[i] == NULL)
|
||||||
{
|
{
|
||||||
if (!shift_null_card_to_end(i))
|
if (!shift_null_card_to_end(i))
|
||||||
{
|
{
|
||||||
@@ -248,20 +258,20 @@ void reorder_card_sprites_layers(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// card_object_get_sprite() will not work here since we need the address
|
// card_object_get_sprite() will not work here since we need the address
|
||||||
sprite_destroy(&(hand.cards[i]->sprite_object->sprite));
|
sprite_destroy(&(s_hand.cards[i]->sprite));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recreate the sprites for the remaining non NULL cards, in order
|
// Recreate the sprites for the remaining non NULL cards, in order
|
||||||
for (int i = 0; i <= hand.hand_top; i++)
|
for (int i = 0; i <= s_hand.hand_top; i++)
|
||||||
{
|
{
|
||||||
if (hand.cards[i] != NULL)
|
if (s_hand.cards[i] != NULL)
|
||||||
{
|
{
|
||||||
// Set the sprite for the card object
|
// Set the sprite for the card object
|
||||||
card_object_set_sprite(hand.cards[i], i);
|
card_object_set_sprite(s_hand.cards[i], i);
|
||||||
sprite_position(
|
sprite_position(
|
||||||
card_object_get_sprite(hand.cards[i]),
|
card_object_get_sprite(s_hand.cards[i]),
|
||||||
fx2int(hand.cards[i]->sprite_object->x),
|
fx2int(s_hand.cards[i]->x),
|
||||||
fx2int(hand.cards[i]->sprite_object->y)
|
fx2int(s_hand.cards[i]->y)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,7 +279,7 @@ void reorder_card_sprites_layers(void)
|
|||||||
|
|
||||||
void sort_cards(void)
|
void sort_cards(void)
|
||||||
{
|
{
|
||||||
if (hand.sort_by_suit)
|
if (s_hand.sort_by_suit)
|
||||||
{
|
{
|
||||||
sort_hand_by_suit();
|
sort_hand_by_suit();
|
||||||
}
|
}
|
||||||
@@ -283,29 +293,29 @@ void sort_cards(void)
|
|||||||
|
|
||||||
void hand_change_sort(bool to_sort_by_suit)
|
void hand_change_sort(bool to_sort_by_suit)
|
||||||
{
|
{
|
||||||
if (to_sort_by_suit != hand.sort_by_suit)
|
if (to_sort_by_suit != s_hand.sort_by_suit)
|
||||||
{
|
{
|
||||||
hand.sort_by_suit = to_sort_by_suit;
|
s_hand.sort_by_suit = to_sort_by_suit;
|
||||||
sort_cards();
|
sort_cards();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hand_select_card(int index)
|
void hand_select_card(int index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= hand_nb_held_cards() || hand.state != HAND_SELECT ||
|
if (index < 0 || index >= hand_nb_held_cards() || s_hand.state != HAND_SELECT ||
|
||||||
hand.cards[index] == NULL)
|
s_hand.cards[index] == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (card_object_is_selected(hand.cards[index]))
|
if (card_object_is_selected(s_hand.cards[index]))
|
||||||
{
|
{
|
||||||
card_object_set_selected(hand.cards[index], false);
|
card_object_set_selected(s_hand.cards[index], false);
|
||||||
hand.hand_selections--;
|
s_hand.hand_selections--;
|
||||||
play_sfx(SFX_CARD_DESELECT, MM_BASE_PITCH_RATE, SFX_DEFAULT_VOLUME);
|
play_sfx(SFX_CARD_DESELECT, MM_BASE_PITCH_RATE, SFX_DEFAULT_VOLUME);
|
||||||
}
|
}
|
||||||
else if (hand.hand_selections < MAX_SELECTION_SIZE)
|
else if (s_hand.hand_selections < MAX_SELECTION_SIZE)
|
||||||
{
|
{
|
||||||
card_object_set_selected(hand.cards[index], true);
|
card_object_set_selected(s_hand.cards[index], true);
|
||||||
hand.hand_selections++;
|
s_hand.hand_selections++;
|
||||||
play_sfx(SFX_CARD_SELECT, MM_BASE_PITCH_RATE, SFX_DEFAULT_VOLUME);
|
play_sfx(SFX_CARD_SELECT, MM_BASE_PITCH_RATE, SFX_DEFAULT_VOLUME);
|
||||||
}
|
}
|
||||||
compute_hand_value_info();
|
compute_hand_value_info();
|
||||||
@@ -314,12 +324,12 @@ void hand_select_card(int index)
|
|||||||
void hand_deselect_all_cards(void)
|
void hand_deselect_all_cards(void)
|
||||||
{
|
{
|
||||||
bool any_cards_deselected = false;
|
bool any_cards_deselected = false;
|
||||||
for (int i = 0; i <= hand.hand_top; i++)
|
for (int i = 0; i <= s_hand.hand_top; i++)
|
||||||
{
|
{
|
||||||
if (card_object_is_selected(hand.cards[i]))
|
if (card_object_is_selected(s_hand.cards[i]))
|
||||||
{
|
{
|
||||||
card_object_set_selected(hand.cards[i], false);
|
card_object_set_selected(s_hand.cards[i], false);
|
||||||
hand.hand_selections--;
|
s_hand.hand_selections--;
|
||||||
any_cards_deselected = true;
|
any_cards_deselected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,13 +356,13 @@ static void get_hand_distribution(u8 ranks_out[NUM_RANKS], u8 suits_out[NUM_SUIT
|
|||||||
for (int i = 0; i < NUM_SUITS; i++)
|
for (int i = 0; i < NUM_SUITS; i++)
|
||||||
suits_out[i] = 0;
|
suits_out[i] = 0;
|
||||||
|
|
||||||
int top = hand.hand_top;
|
int top = s_hand.hand_top;
|
||||||
for (int i = 0; i <= top; i++)
|
for (int i = 0; i <= top; i++)
|
||||||
{
|
{
|
||||||
if (hand.cards[i] && card_object_is_selected(hand.cards[i]))
|
if (s_hand.cards[i] && card_object_is_selected(s_hand.cards[i]))
|
||||||
{
|
{
|
||||||
ranks_out[hand.cards[i]->card->rank]++;
|
ranks_out[s_hand.cards[i]->card->rank]++;
|
||||||
suits_out[hand.cards[i]->card->suit]++;
|
suits_out[s_hand.cards[i]->card->suit]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,14 +583,8 @@ int find_flush_in_played_cards(CardObject** played, int top, int min_len, bool*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the number of cards in the best straight or 0 if no straight of min_len is found, marks
|
// Returns the number of cards in the best straight or 0 if no straight of min_len is found, marks
|
||||||
// as true them in out_selection[]. This is mostly from Google Gemini
|
// them as true in out_selection[]. This is mostly from Google Gemini
|
||||||
int find_straight_in_played_cards(
|
int find_straight_in_played_cards(CardObject** played, int top, int min_len, bool* out_selection)
|
||||||
CardObject** played,
|
|
||||||
int top,
|
|
||||||
bool shortcut_active,
|
|
||||||
int min_len,
|
|
||||||
bool* out_selection
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (top < 0)
|
if (top < 0)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -607,6 +611,7 @@ int find_straight_in_played_cards(
|
|||||||
// TODO: Consolidate functions to avoid code duplication?
|
// TODO: Consolidate functions to avoid code duplication?
|
||||||
// Might cost performance because this does a little more
|
// Might cost performance because this does a little more
|
||||||
int ace_low_len = ranks[ACE] ? 1 : 0;
|
int ace_low_len = ranks[ACE] ? 1 : 0;
|
||||||
|
bool is_shortcut_active = is_shortcut_joker_active();
|
||||||
for (int i = 0; i < NUM_RANKS; i++)
|
for (int i = 0; i < NUM_RANKS; i++)
|
||||||
{
|
{
|
||||||
if (ranks[i] > 0)
|
if (ranks[i] > 0)
|
||||||
@@ -614,7 +619,7 @@ int find_straight_in_played_cards(
|
|||||||
int prev1 = 0, prev2 = 0;
|
int prev1 = 0, prev2 = 0;
|
||||||
int parent1 = -1, parent2 = -1;
|
int parent1 = -1, parent2 = -1;
|
||||||
|
|
||||||
if (shortcut_active)
|
if (is_shortcut_active)
|
||||||
{
|
{
|
||||||
if (i == TWO)
|
if (i == TWO)
|
||||||
{
|
{
|
||||||
@@ -723,8 +728,6 @@ int find_straight_in_played_cards(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is used for the special case in "Four Fingers" where you can add a pair into a straight
|
|
||||||
// (e.g. AA234 should score all 5 cards)
|
|
||||||
void select_paired_cards_in_hand(CardObject** played, int played_top, bool* selection)
|
void select_paired_cards_in_hand(CardObject** played, int played_top, bool* selection)
|
||||||
{
|
{
|
||||||
// Build a set of ranks that are already selected
|
// Build a set of ranks that are already selected
|
||||||
@@ -762,7 +765,7 @@ static ContainedHandTypes compute_contained_hand_types(void)
|
|||||||
ContainedHandTypes hand_types = {0};
|
ContainedHandTypes hand_types = {0};
|
||||||
|
|
||||||
// Idk if this is how Balatro does it but this is how I'm doing it
|
// Idk if this is how Balatro does it but this is how I'm doing it
|
||||||
if (hand.hand_selections == 0 || hand.state == HAND_DISCARD)
|
if (s_hand.hand_selections == 0 || s_hand.state == HAND_DISCARD)
|
||||||
{
|
{
|
||||||
return hand_types;
|
return hand_types;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#include "item.h"
|
||||||
|
|
||||||
|
#include "item_funcs.h"
|
||||||
|
#include "mgba_logger.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <tonc.h>
|
||||||
|
|
||||||
|
Item* item_roll_new(enum ItemType item_type)
|
||||||
|
{
|
||||||
|
if ((int)item_type < 0 || item_type >= ITEM_NUM_TYPES)
|
||||||
|
{
|
||||||
|
MGBA_FUNC_ERROR("Undefined type %d", item_type);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemFuncs* item_funcs = get_item_type_funcs(item_type);
|
||||||
|
GBAL_RETURN_IF_NULL_RET(item_funcs, NULL);
|
||||||
|
GBAL_RETURN_IF_NULL_RET(item_funcs->roll_new, NULL);
|
||||||
|
|
||||||
|
return item_funcs->roll_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
int item_get_buy_price(Item* item)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_RET(item, UNDEFINED);
|
||||||
|
|
||||||
|
ItemFuncs* item_funcs = get_item_type_funcs(item->type);
|
||||||
|
GBAL_RETURN_IF_NULL_RET(item_funcs, UNDEFINED);
|
||||||
|
GBAL_RETURN_IF_NULL_RET(item_funcs->get_buy_price, UNDEFINED);
|
||||||
|
|
||||||
|
return item_funcs->get_buy_price(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void item_acquire(Item* item)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(item);
|
||||||
|
|
||||||
|
ItemFuncs* item_funcs = get_item_type_funcs(item->type);
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(item_funcs);
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(item_funcs->acquire);
|
||||||
|
|
||||||
|
item_funcs->acquire(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool item_can_acquire(Item* item)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_RET(item, false);
|
||||||
|
ItemFuncs* item_funcs = get_item_type_funcs(item->type);
|
||||||
|
GBAL_RETURN_IF_NULL_RET(item_funcs, false);
|
||||||
|
GBAL_RETURN_IF_NULL_RET(item_funcs->can_acquire, false);
|
||||||
|
|
||||||
|
return item_funcs->can_acquire(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void item_dispose(Item** item)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(item);
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(*item);
|
||||||
|
ItemFuncs* item_funcs = get_item_type_funcs((*item)->type);
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(item_funcs);
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(item_funcs->dispose);
|
||||||
|
|
||||||
|
item_funcs->dispose(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void item_print_buy_price_under(Item* item)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(item);
|
||||||
|
sprite_object_print_price_under((SpriteObject*)item, item_get_buy_price(item));
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#include "item_funcs.h"
|
||||||
|
|
||||||
|
#include "card.h"
|
||||||
|
#include "game.h"
|
||||||
|
#include "game/shop.h"
|
||||||
|
#include "joker.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static Item* item_roll_new_unimplemented();
|
||||||
|
static void item_acquire_unimplemented(Item* item);
|
||||||
|
static bool item_always_can_acquire(Item* item);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
ItemFuncs item_func_table[] = {
|
||||||
|
[ITEM_TYPE_JOKER] = {
|
||||||
|
.roll_new = joker_object_roll_new,
|
||||||
|
.get_buy_price = joker_object_get_buy_price,
|
||||||
|
.acquire = joker_object_add_to_owned,
|
||||||
|
.can_acquire = joker_object_can_acquire,
|
||||||
|
.dispose = joker_object_dispose
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Currently playing cards have partial implementations since Magic Trick is not implemented
|
||||||
|
* and playing cards can't appear in the shop.
|
||||||
|
* If the Magic Trick voucher is implemented, these need to be completed.
|
||||||
|
* Playing cards must stay the only exception, the rest of the item types must implement
|
||||||
|
* all the basic functions to appear in the shop.
|
||||||
|
*/
|
||||||
|
[ITEM_TYPE_PLAYING_CARD] = {
|
||||||
|
.roll_new = item_roll_new_unimplemented,
|
||||||
|
.get_buy_price = card_object_get_buy_price,
|
||||||
|
.acquire = item_acquire_unimplemented,
|
||||||
|
.can_acquire = item_always_can_acquire,
|
||||||
|
.dispose = card_object_dispose
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
ItemFuncs* get_item_type_funcs(enum ItemType type)
|
||||||
|
{
|
||||||
|
if ((int)type < 0 || type >= NUM_ELEM_IN_ARR(item_func_table))
|
||||||
|
{
|
||||||
|
MGBA_FUNC_ERROR("Invalid type %d", type);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &item_func_table[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
static Item* item_roll_new_unimplemented()
|
||||||
|
{
|
||||||
|
MGBA_FUNC_ERROR("Unimplemented roll_new function called");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void item_acquire_unimplemented(Item* item)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(item);
|
||||||
|
MGBA_FUNC_ERROR("Unimplemented acquire function called for item type type %d", (item)->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool item_always_can_acquire(Item* item)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
+195
-38
@@ -4,6 +4,8 @@
|
|||||||
#include "game/round.h"
|
#include "game/round.h"
|
||||||
#include "game_variables.h"
|
#include "game_variables.h"
|
||||||
#include "graphic_utils.h"
|
#include "graphic_utils.h"
|
||||||
|
#include "layout.h"
|
||||||
|
#include "mgba_logger.h"
|
||||||
#include "pool.h"
|
#include "pool.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
@@ -11,6 +13,7 @@
|
|||||||
|
|
||||||
// Tiles and palettes
|
// Tiles and palettes
|
||||||
#include "card_rarity_pal_gfx.h"
|
#include "card_rarity_pal_gfx.h"
|
||||||
|
#include "item.h"
|
||||||
#include "joker_gfx.h"
|
#include "joker_gfx.h"
|
||||||
|
|
||||||
#include <maxmod.h>
|
#include <maxmod.h>
|
||||||
@@ -35,7 +38,8 @@ static const unsigned short* joker_gfxPal[] = {
|
|||||||
#undef DEF_JOKER_GFX
|
#undef DEF_JOKER_GFX
|
||||||
};
|
};
|
||||||
|
|
||||||
const static u8 edition_price_lut[MAX_EDITIONS] = {
|
// TODO: Removed unplanned editions...
|
||||||
|
const static u8 EDITION_PRICE_LUT[MAX_EDITIONS] = {
|
||||||
0, // BASE_EDITION
|
0, // BASE_EDITION
|
||||||
2, // FOIL_EDITION
|
2, // FOIL_EDITION
|
||||||
3, // HOLO_EDITION
|
3, // HOLO_EDITION
|
||||||
@@ -50,20 +54,22 @@ const static u8 edition_price_lut[MAX_EDITIONS] = {
|
|||||||
logic. But I'm going to use a simpler approach for the joker objects since I'm lazy and sorting
|
logic. But I'm going to use a simpler approach for the joker objects since I'm lazy and sorting
|
||||||
them wouldn't look good enough to warrant the effort.
|
them wouldn't look good enough to warrant the effort.
|
||||||
*/
|
*/
|
||||||
static bool used_layers[MAX_JOKER_OBJECTS] = {false}; // Track used layers for joker sprites
|
static bool s_used_layers[MAX_JOKER_OBJECTS] = {false}; // Track used layers for joker sprites
|
||||||
// TODO: Refactor sorting into SpriteObject?
|
// TODO: Refactor sorting into SpriteObject?
|
||||||
|
|
||||||
// Maps the spritesheet index to the palette bank index allocated to it.
|
// Maps the spritesheet index to the palette bank index allocated to it.
|
||||||
// Spritesheets that were not allocated are
|
// Spritesheets that were not allocated are
|
||||||
static int joker_spritesheet_pb_map[MAX_NUM_JOKERS_SPRITESHEETS];
|
static int s_joker_spritesheet_pb_map[MAX_NUM_JOKERS_SPRITESHEETS];
|
||||||
static int joker_pb_num_sprite_users[JOKER_LAST_PB - JOKER_BASE_PB + 1] = {0};
|
static int s_joker_pb_num_sprite_users[JOKER_LAST_PB - JOKER_BASE_PB + 1] = {0};
|
||||||
|
|
||||||
|
BITSET_DEFINE(s_rollable_jokers_bitset, MAX_DEFINABLE_JOKERS)
|
||||||
|
|
||||||
// See linked issue for context of maps
|
// See linked issue for context of maps
|
||||||
// https://github.com/GBALATRO/balatro-gba/issues/274#issue-3685075538
|
// https://github.com/GBALATRO/balatro-gba/issues/274#issue-3685075538
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// Map of Joker ID -> Spritesheet idx
|
// Map of Joker ID -> Spritesheet idx
|
||||||
static int joker_id_to_sprite_map[] = {
|
static const int JOKER_ID_TO_SPRITE_MAP[] = {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
1, 1,
|
1, 1,
|
||||||
2, 2,
|
2, 2,
|
||||||
@@ -88,13 +94,13 @@ static int joker_id_to_sprite_map[] = {
|
|||||||
// This is used to determine the sprite index of a Joker's sprite
|
// This is used to determine the sprite index of a Joker's sprite
|
||||||
// within its spritesheet by substracting its ID to this starting ID.
|
// within its spritesheet by substracting its ID to this starting ID.
|
||||||
// Notice how spritesheets with only one Joker have sequential starting IDs.
|
// Notice how spritesheets with only one Joker have sequential starting IDs.
|
||||||
static int spritesheet_idx_to_starting_joker_id[] = {
|
static const int SPRITESHEET_IDX_TO_STARTING_JOKER_ID[] = {
|
||||||
0, 18, 20, 22, 27, 32, 36, 40, 42, 44,
|
0, 18, 20, 22, 27, 32, 36, 40, 42, 44,
|
||||||
45, 46, 47, 48, 49, 50, 51, 52
|
45, 46, 47, 48, 49, 50, 51, 52
|
||||||
};
|
};
|
||||||
|
|
||||||
// Lookup table of Joker Rarity strings. Used to display at the bottom of the description screen.
|
// Lookup table of Joker Rarity strings. Used to display at the bottom of the description screen.
|
||||||
static const char* joker_rarity_strings_lut[MAX_RARITIES] = {
|
static const char* JOKER_RARITY_STRINGS_LUT[MAX_RARITIES] = {
|
||||||
"Common", "Uncommon", "Rare", "Legendary"
|
"Common", "Uncommon", "Rare", "Legendary"
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
@@ -115,7 +121,7 @@ void joker_init()
|
|||||||
|
|
||||||
for (int i = 0; i < num_spritesheets; i++)
|
for (int i = 0; i < num_spritesheets; i++)
|
||||||
{
|
{
|
||||||
joker_spritesheet_pb_map[i] = UNDEFINED;
|
s_joker_spritesheet_pb_map[i] = UNDEFINED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +135,7 @@ Joker* joker_new(u8 id)
|
|||||||
|
|
||||||
joker->id = id;
|
joker->id = id;
|
||||||
joker->modifier = BASE_EDITION; // TODO: Make this a parameter
|
joker->modifier = BASE_EDITION; // TODO: Make this a parameter
|
||||||
joker->value = jinfo->base_value + edition_price_lut[joker->modifier];
|
joker->value = jinfo->base_value + EDITION_PRICE_LUT[joker->modifier];
|
||||||
joker->rarity = jinfo->rarity;
|
joker->rarity = jinfo->rarity;
|
||||||
joker->scoring_state = 0;
|
joker->scoring_state = 0;
|
||||||
joker->persistent_state = 0;
|
joker->persistent_state = 0;
|
||||||
@@ -166,7 +172,7 @@ const char* joker_get_rarity_string(u8 rarity)
|
|||||||
if (rarity >= MAX_RARITIES)
|
if (rarity >= MAX_RARITIES)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return joker_rarity_strings_lut[rarity];
|
return JOKER_RARITY_STRINGS_LUT[rarity];
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 joker_get_rarity_color(u8 rarity, bool main_color)
|
u16 joker_get_rarity_color(u8 rarity, bool main_color)
|
||||||
@@ -179,12 +185,16 @@ u16 joker_get_rarity_color(u8 rarity, bool main_color)
|
|||||||
return card_rarity_pal_gfxPal[1 + 2 * rarity + (main_color ? 0 : 1)];
|
return card_rarity_pal_gfxPal[1 + 2 * rarity + (main_color ? 0 : 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int joker_get_buy_price(const Joker* joker)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_RET(joker, UNDEFINED);
|
||||||
|
|
||||||
|
return joker->value;
|
||||||
|
}
|
||||||
|
|
||||||
int joker_get_sell_value(const Joker* joker)
|
int joker_get_sell_value(const Joker* joker)
|
||||||
{
|
{
|
||||||
if (joker == NULL)
|
GBAL_RETURN_IF_NULL_RET(joker, UNDEFINED);
|
||||||
{
|
|
||||||
return UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return joker->value / 2;
|
return joker->value / 2;
|
||||||
}
|
}
|
||||||
@@ -194,19 +204,22 @@ JokerObject* joker_object_new(Joker* joker)
|
|||||||
{
|
{
|
||||||
JokerObject* joker_object = POOL_GET(JokerObject);
|
JokerObject* joker_object = POOL_GET(JokerObject);
|
||||||
|
|
||||||
|
sprite_object_init((SpriteObject*)joker_object);
|
||||||
|
|
||||||
int layer = 0;
|
int layer = 0;
|
||||||
for (int i = 0; i < MAX_JOKER_OBJECTS; i++)
|
for (int i = 0; i < MAX_JOKER_OBJECTS; i++)
|
||||||
{
|
{
|
||||||
if (!used_layers[i])
|
if (!s_used_layers[i])
|
||||||
{
|
{
|
||||||
layer = i;
|
layer = i;
|
||||||
used_layers[i] = true; // Mark this layer as used
|
s_used_layers[i] = true; // Mark this layer as used
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
joker_object->joker = joker;
|
joker_object->joker = joker;
|
||||||
joker_object->sprite_object = sprite_object_new();
|
|
||||||
|
joker_object->type = ITEM_TYPE_JOKER;
|
||||||
|
|
||||||
int tile_index = JOKER_TID + layer * JOKER_SPRITE_OFFSET;
|
int tile_index = JOKER_TID + layer * JOKER_SPRITE_OFFSET;
|
||||||
|
|
||||||
@@ -222,7 +235,7 @@ JokerObject* joker_object_new(Joker* joker)
|
|||||||
);
|
);
|
||||||
|
|
||||||
sprite_object_set_sprite(
|
sprite_object_set_sprite(
|
||||||
joker_object->sprite_object,
|
(SpriteObject*)joker_object,
|
||||||
sprite_new(
|
sprite_new(
|
||||||
ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF,
|
ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF,
|
||||||
ATTR1_SIZE_32,
|
ATTR1_SIZE_32,
|
||||||
@@ -241,27 +254,171 @@ void joker_object_destroy(JokerObject** joker_object)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int layer = sprite_get_layer(joker_object_get_sprite(*joker_object)) - JOKER_STARTING_LAYER;
|
int layer = sprite_get_layer(joker_object_get_sprite(*joker_object)) - JOKER_STARTING_LAYER;
|
||||||
used_layers[layer] = false;
|
s_used_layers[layer] = false;
|
||||||
s_joker_pb_remove_sprite_user(sprite_get_pb(joker_object_get_sprite(*joker_object)));
|
s_joker_pb_remove_sprite_user(sprite_get_pb(joker_object_get_sprite(*joker_object)));
|
||||||
if (s_joker_pb_get_num_sprite_users((sprite_get_pb(joker_object_get_sprite(*joker_object)))) ==
|
if (s_joker_pb_get_num_sprite_users((sprite_get_pb(joker_object_get_sprite(*joker_object)))) ==
|
||||||
0)
|
0)
|
||||||
{
|
{
|
||||||
joker_spritesheet_pb_map[s_joker_get_spritesheet_idx((*joker_object)->joker->id)] =
|
s_joker_spritesheet_pb_map[s_joker_get_spritesheet_idx((*joker_object)->joker->id)] =
|
||||||
UNDEFINED;
|
UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprite_object_destroy(&(*joker_object)->sprite_object); // Destroy the sprite
|
sprite_object_destroy((SpriteObject*)(*joker_object));
|
||||||
joker_destroy(&(*joker_object)->joker); // Destroy the joker
|
joker_destroy(&(*joker_object)->joker);
|
||||||
POOL_FREE(JokerObject, *joker_object);
|
POOL_FREE(JokerObject, *joker_object);
|
||||||
*joker_object = NULL;
|
*joker_object = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void joker_object_shake(JokerObject* joker_object, mm_word sound_id)
|
void joker_object_dispose(Item** joker_object_item)
|
||||||
{
|
{
|
||||||
sprite_object_shake(joker_object->sprite_object, sound_id);
|
GBAL_RETURN_IF_NULL_VOID(joker_object_item);
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(*joker_object_item);
|
||||||
|
ITEM_RETURN_IF_UNEXPECTED_TYPE_VOID(*joker_object_item, ITEM_TYPE_JOKER);
|
||||||
|
|
||||||
|
JokerObject* joker_object = (JokerObject*)(*joker_object_item);
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(joker_object->joker);
|
||||||
|
|
||||||
|
joker_set_rollable(joker_object->joker->id, true);
|
||||||
|
|
||||||
|
joker_object_destroy(&joker_object);
|
||||||
|
*joker_object_item = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_and_shift_text(char* str, int* cursor_pos_x, int* cursor_pos_y, int color_pb)
|
void joker_object_shake(JokerObject* joker_object, mm_word sound_id)
|
||||||
|
{
|
||||||
|
sprite_object_shake((SpriteObject*)joker_object, sound_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int joker_object_get_buy_price(Item* joker_object)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_RET(joker_object, UNDEFINED);
|
||||||
|
ITEM_RETURN_IF_UNEXPECTED_TYPE_RET(joker_object, ITEM_TYPE_JOKER, UNDEFINED);
|
||||||
|
|
||||||
|
return ((JokerObject*)joker_object)->joker->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void joker_object_add_to_owned(Item* joker_object)
|
||||||
|
{
|
||||||
|
GBAL_RETURN_IF_NULL_VOID(joker_object);
|
||||||
|
ITEM_RETURN_IF_UNEXPECTED_TYPE_VOID(joker_object, ITEM_TYPE_JOKER);
|
||||||
|
|
||||||
|
joker_object->ty = int2fx(HELD_JOKERS_POS.y);
|
||||||
|
add_joker((JokerObject*)joker_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void joker_set_rollable(int joker_id, bool rollable)
|
||||||
|
{
|
||||||
|
bitset_set_idx(&s_rollable_jokers_bitset, joker_id, rollable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes the number of Jokers we can currently roll in the Shop.
|
||||||
|
* The Jokers we own is taken into account and can't be rolled again.
|
||||||
|
*/
|
||||||
|
static inline int get_num_rollable_jokers(void)
|
||||||
|
{
|
||||||
|
return bitset_num_set_bits(&s_rollable_jokers_bitset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if we can't roll any Joker
|
||||||
|
*/
|
||||||
|
static inline bool no_rollable_jokers(void)
|
||||||
|
{
|
||||||
|
return bitset_is_empty(&s_rollable_jokers_bitset);
|
||||||
|
}
|
||||||
|
|
||||||
|
GBAL_UNUSED
|
||||||
|
static inline bool joker_is_rollable(int joker_id)
|
||||||
|
{
|
||||||
|
return bitset_get_idx(&s_rollable_jokers_bitset, joker_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void joker_reset_rollable_jokers(void)
|
||||||
|
{
|
||||||
|
int num_jokers = get_joker_registry_size();
|
||||||
|
|
||||||
|
bitset_clear(&s_rollable_jokers_bitset);
|
||||||
|
for (int i = 0; i < num_jokers; i++)
|
||||||
|
{
|
||||||
|
bitset_set_idx(&s_rollable_jokers_bitset, i, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rolls a random Joker among the available ones
|
||||||
|
*/
|
||||||
|
static int joker_roll_id(void)
|
||||||
|
{
|
||||||
|
// Now determine how many jokers are available based on the rarity
|
||||||
|
int jokers_avail_size = get_num_rollable_jokers();
|
||||||
|
|
||||||
|
if (jokers_avail_size == 0)
|
||||||
|
return UNDEFINED;
|
||||||
|
|
||||||
|
// Roll for what rarity the joker will be
|
||||||
|
int joker_rarity = joker_get_random_rarity();
|
||||||
|
|
||||||
|
int matching_joker_ids[jokers_avail_size];
|
||||||
|
int fallback_random_idx = rng_get_u32() % jokers_avail_size;
|
||||||
|
int fallback_random_joker_id = UNDEFINED;
|
||||||
|
int match_count = 0;
|
||||||
|
|
||||||
|
BitsetItr itr = bitset_itr_create(&s_rollable_jokers_bitset);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int joker_id = UNDEFINED;
|
||||||
|
while ((joker_id = bitset_itr_next(&itr)) != UNDEFINED)
|
||||||
|
{
|
||||||
|
if (i++ == fallback_random_idx)
|
||||||
|
fallback_random_joker_id = joker_id;
|
||||||
|
const JokerInfo* info = get_joker_registry_entry(joker_id);
|
||||||
|
if (info->rarity == joker_rarity)
|
||||||
|
{
|
||||||
|
matching_joker_ids[match_count++] = joker_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int selected_joker_id = (match_count > 0) ? matching_joker_ids[rng_get_u32() % match_count]
|
||||||
|
: fallback_random_joker_id;
|
||||||
|
|
||||||
|
return selected_joker_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item* joker_object_roll_new(void)
|
||||||
|
{
|
||||||
|
if (no_rollable_jokers())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
int joker_id = 0;
|
||||||
|
#ifdef TEST_JOKER_ID0 // Allow defining an ID for a joker to always appear in shop and be tested
|
||||||
|
if (joker_is_rollable(TEST_JOKER_ID0))
|
||||||
|
{
|
||||||
|
joker_id = TEST_JOKER_ID0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
#ifdef TEST_JOKER_ID1
|
||||||
|
if (joker_is_rollable(TEST_JOKER_ID1))
|
||||||
|
{
|
||||||
|
joker_id = TEST_JOKER_ID1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
joker_id = joker_roll_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If for some reason only no joker is left, don't make another
|
||||||
|
if (joker_id == UNDEFINED)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
joker_set_rollable(joker_id, false);
|
||||||
|
|
||||||
|
return (Item*)joker_object_new(joker_new(joker_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_and_shift_text(char* str, int* cursor_pos_x, int* cursor_pos_y, int color_pb)
|
||||||
{
|
{
|
||||||
tte_set_pos(*cursor_pos_x, *cursor_pos_y);
|
tte_set_pos(*cursor_pos_x, *cursor_pos_y);
|
||||||
tte_set_special(color_pb * TTE_SPECIAL_PB_MULT_OFFSET);
|
tte_set_special(color_pb * TTE_SPECIAL_PB_MULT_OFFSET);
|
||||||
@@ -305,12 +462,12 @@ bool joker_object_score(
|
|||||||
{
|
{
|
||||||
// display the text on top of the card instead of below the Joker for Held Cards effects
|
// display the text on top of the card instead of below the Joker for Held Cards effects
|
||||||
// scored_card cannot be NULL here because of the joker event
|
// scored_card cannot be NULL here because of the joker event
|
||||||
cursorPosX += fx2int(card_object->sprite_object->x);
|
cursorPosX += fx2int(card_object->x);
|
||||||
cursorPosY = HELD_CARD_SCORE_TEXT_Y;
|
cursorPosY = HELD_CARD_SCORE_TEXT_Y;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cursorPosX += fx2int(joker_object->sprite_object->x);
|
cursorPosX += fx2int(joker_object->x);
|
||||||
cursorPosY = JOKER_SCORE_TEXT_Y;
|
cursorPosY = JOKER_SCORE_TEXT_Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +532,7 @@ Sprite* joker_object_get_sprite(JokerObject* joker_object)
|
|||||||
{
|
{
|
||||||
if (joker_object == NULL)
|
if (joker_object == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
return sprite_object_get_sprite(joker_object->sprite_object);
|
return sprite_object_get_sprite((SpriteObject*)joker_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
int joker_get_random_rarity()
|
int joker_get_random_rarity()
|
||||||
@@ -410,35 +567,35 @@ static int s_get_num_spritesheets()
|
|||||||
|
|
||||||
static int s_joker_get_spritesheet_idx(u8 joker_id)
|
static int s_joker_get_spritesheet_idx(u8 joker_id)
|
||||||
{
|
{
|
||||||
return joker_id_to_sprite_map[joker_id];
|
return JOKER_ID_TO_SPRITE_MAP[joker_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s_joker_get_sprite_idx_in_sheet(u8 joker_id, int spritesheet_idx)
|
static int s_joker_get_sprite_idx_in_sheet(u8 joker_id, int spritesheet_idx)
|
||||||
{
|
{
|
||||||
return joker_id - spritesheet_idx_to_starting_joker_id[joker_id_to_sprite_map[joker_id]];
|
return joker_id - SPRITESHEET_IDX_TO_STARTING_JOKER_ID[JOKER_ID_TO_SPRITE_MAP[joker_id]];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s_joker_pb_add_sprite_user(int pb)
|
static void s_joker_pb_add_sprite_user(int pb)
|
||||||
{
|
{
|
||||||
joker_pb_num_sprite_users[pb - JOKER_BASE_PB]++;
|
s_joker_pb_num_sprite_users[pb - JOKER_BASE_PB]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s_joker_pb_remove_sprite_user(int pb)
|
static void s_joker_pb_remove_sprite_user(int pb)
|
||||||
{
|
{
|
||||||
int num_sprite_users = joker_pb_num_sprite_users[pb - JOKER_BASE_PB];
|
int num_sprite_users = s_joker_pb_num_sprite_users[pb - JOKER_BASE_PB];
|
||||||
joker_pb_num_sprite_users[pb - JOKER_BASE_PB] = max(0, num_sprite_users - 1);
|
s_joker_pb_num_sprite_users[pb - JOKER_BASE_PB] = max(0, num_sprite_users - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s_joker_pb_get_num_sprite_users(int joker_pb)
|
static int s_joker_pb_get_num_sprite_users(int joker_pb)
|
||||||
{
|
{
|
||||||
return joker_pb_num_sprite_users[joker_pb - JOKER_BASE_PB];
|
return s_joker_pb_num_sprite_users[joker_pb - JOKER_BASE_PB];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s_get_unused_joker_pb()
|
static int s_get_unused_joker_pb()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < NUM_ELEM_IN_ARR(joker_pb_num_sprite_users); i++)
|
for (int i = 0; i < NUM_ELEM_IN_ARR(s_joker_pb_num_sprite_users); i++)
|
||||||
{
|
{
|
||||||
if (joker_pb_num_sprite_users[i] == 0)
|
if (s_joker_pb_num_sprite_users[i] == 0)
|
||||||
{
|
{
|
||||||
return (i + JOKER_BASE_PB);
|
return (i + JOKER_BASE_PB);
|
||||||
}
|
}
|
||||||
@@ -450,7 +607,7 @@ static int s_get_unused_joker_pb()
|
|||||||
static int s_allocate_pb_if_needed(u8 joker_id)
|
static int s_allocate_pb_if_needed(u8 joker_id)
|
||||||
{
|
{
|
||||||
int joker_spritesheet_idx = s_joker_get_spritesheet_idx(joker_id);
|
int joker_spritesheet_idx = s_joker_get_spritesheet_idx(joker_id);
|
||||||
int joker_pb = joker_spritesheet_pb_map[joker_spritesheet_idx];
|
int joker_pb = s_joker_spritesheet_pb_map[joker_spritesheet_idx];
|
||||||
if (joker_pb != UNDEFINED)
|
if (joker_pb != UNDEFINED)
|
||||||
{
|
{
|
||||||
// Already allocated
|
// Already allocated
|
||||||
@@ -467,7 +624,7 @@ static int s_allocate_pb_if_needed(u8 joker_id)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
joker_spritesheet_pb_map[joker_spritesheet_idx] = joker_pb;
|
s_joker_spritesheet_pb_map[joker_spritesheet_idx] = joker_pb;
|
||||||
memcpy16(
|
memcpy16(
|
||||||
&pal_obj_mem[PAL_ROW_LEN * joker_pb],
|
&pal_obj_mem[PAL_ROW_LEN * joker_pb],
|
||||||
joker_gfxPal[joker_spritesheet_idx],
|
joker_gfxPal[joker_spritesheet_idx],
|
||||||
|
|||||||
+315
-240
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,31 @@
|
|||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
#include "soundbank_bin.h"
|
#include "soundbank_bin.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TTE_ASCII_GLYPH_COUNT = 96,
|
||||||
|
FONT_TILES_PER_ROW = 16,
|
||||||
|
CHARBLOCK_4BPP_TILE_COUNT = 512,
|
||||||
|
SCREENBLOCK_4BPP_TILE_COUNT = 64,
|
||||||
|
CHINESE_GLYPH_COUNT = 0
|
||||||
|
#define CHINESE_GLYPH(codepoint) +1
|
||||||
|
#include "def_chinese_glyph_table.h"
|
||||||
|
#undef CHINESE_GLYPH
|
||||||
|
,
|
||||||
|
TTE_FONT_TILE_COUNT =
|
||||||
|
TTE_ASCII_GLYPH_COUNT +
|
||||||
|
((CHINESE_GLYPH_COUNT + FONT_TILES_PER_ROW - 1) / FONT_TILES_PER_ROW) * FONT_TILES_PER_ROW,
|
||||||
|
TTE_FONT_FIRST_TILE = TTE_CBB * CHARBLOCK_4BPP_TILE_COUNT,
|
||||||
|
TTE_FONT_END_TILE = TTE_FONT_FIRST_TILE + TTE_FONT_TILE_COUNT,
|
||||||
|
AFFINE_BG_MAP_FIRST_TILE = AFFINE_BG_SBB * SCREENBLOCK_4BPP_TILE_COUNT,
|
||||||
|
AFFINE_BG_MAP_END_TILE = AFFINE_BG_MAP_FIRST_TILE + SCREENBLOCK_4BPP_TILE_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
_Static_assert(
|
||||||
|
TTE_FONT_END_TILE <= AFFINE_BG_MAP_FIRST_TILE || AFFINE_BG_MAP_END_TILE <= TTE_FONT_FIRST_TILE,
|
||||||
|
"TTE font tiles overlap the affine background map"
|
||||||
|
);
|
||||||
|
|
||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
rng_init();
|
rng_init();
|
||||||
|
|||||||
+37
-9
@@ -16,28 +16,53 @@ static const u32 MGBA_ENABLE_MAGIC = 0xC0DE;
|
|||||||
static const u32 MGBA_ENABLE_OK = 0x1DEA;
|
static const u32 MGBA_ENABLE_OK = 0x1DEA;
|
||||||
static const u32 MGBA_LOG_SEND = 0x100;
|
static const u32 MGBA_LOG_SEND = 0x100;
|
||||||
static const u32 MGBA_LOG_BUFFER_SIZE = 0x100;
|
static const u32 MGBA_LOG_BUFFER_SIZE = 0x100;
|
||||||
|
static const u16 MGBA_LOG_LEVEL_MASK = 0x7;
|
||||||
|
|
||||||
static bool mgba_logger_available = false;
|
static bool s_mgba_logger_available = false;
|
||||||
|
|
||||||
bool mgba_logger_init(void)
|
bool mgba_logger_init(void)
|
||||||
{
|
{
|
||||||
*MGBA_REG_DEBUG_ENABLE = MGBA_ENABLE_MAGIC;
|
*MGBA_REG_DEBUG_ENABLE = MGBA_ENABLE_MAGIC;
|
||||||
mgba_logger_available = (*MGBA_REG_DEBUG_ENABLE == MGBA_ENABLE_OK);
|
s_mgba_logger_available = (*MGBA_REG_DEBUG_ENABLE == MGBA_ENABLE_OK);
|
||||||
return mgba_logger_available;
|
return s_mgba_logger_available;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mgba_vprintf(MgbaLogLevel level, const char* fmt, va_list args)
|
||||||
|
{
|
||||||
|
if (!s_mgba_logger_available || fmt == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
vsnprintf(MGBA_REG_DEBUG_STRING, MGBA_LOG_BUFFER_SIZE, fmt, args);
|
||||||
|
|
||||||
|
*MGBA_REG_DEBUG_FLAGS = ((uint16_t)level & MGBA_LOG_LEVEL_MASK) | MGBA_LOG_SEND;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mgba_printf(MgbaLogLevel level, const char* fmt, ...)
|
void mgba_printf(MgbaLogLevel level, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
if (!mgba_logger_available || fmt == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vsnprintf(MGBA_REG_DEBUG_STRING, MGBA_LOG_BUFFER_SIZE, fmt, args);
|
mgba_vprintf(level, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
*MGBA_REG_DEBUG_FLAGS = ((uint16_t)level & 0x7) | MGBA_LOG_SEND;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mgba_func_printf(MgbaLogLevel level, const char* func_name, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
if (!s_mgba_logger_available || func_name == NULL || fmt == NULL)
|
||||||
|
{
|
||||||
|
// The one place where we can't log the error.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char printed_str_buff[MGBA_LOG_BUFFER_SIZE];
|
||||||
|
|
||||||
|
// Expand the format first so the full string is truncated in case it's too long
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(printed_str_buff, sizeof(printed_str_buff), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
mgba_printf(level, "%s(): %s", func_name, printed_str_buff);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// Noop stubs
|
// Noop stubs
|
||||||
@@ -50,4 +75,7 @@ void mgba_printf(MgbaLogLevel level, const char* fmt, ...)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mgba_func_printf(MgbaLogLevel level, const char* func_name, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+3
-3
@@ -7,7 +7,7 @@
|
|||||||
#include <tonc.h>
|
#include <tonc.h>
|
||||||
|
|
||||||
// Accumulate timer 1 into a bigger variable so we can generate more diverse seeds
|
// Accumulate timer 1 into a bigger variable so we can generate more diverse seeds
|
||||||
static u32 timer_acc = 0;
|
static u32 s_timer_acc = 0;
|
||||||
|
|
||||||
// Timers usage docs: https://gbadev.net/tonc/timers.html
|
// Timers usage docs: https://gbadev.net/tonc/timers.html
|
||||||
void rng_init(void)
|
void rng_init(void)
|
||||||
@@ -18,7 +18,7 @@ void rng_init(void)
|
|||||||
|
|
||||||
void rng_update(void)
|
void rng_update(void)
|
||||||
{
|
{
|
||||||
timer_acc += (u32)REG_TM1D;
|
s_timer_acc += (u32)REG_TM1D;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rng_set_seed(u32 seed)
|
void rng_set_seed(u32 seed)
|
||||||
@@ -30,7 +30,7 @@ void rng_set_seed(u32 seed)
|
|||||||
|
|
||||||
void rng_shuffle_seed(void)
|
void rng_shuffle_seed(void)
|
||||||
{
|
{
|
||||||
rng_set_seed(timer_acc);
|
rng_set_seed(s_timer_acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 rng_get_u32(void)
|
u32 rng_get_u32(void)
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
#include <tonc.h>
|
#include <tonc.h>
|
||||||
|
|
||||||
static const Rect COUNTDOWN_TIMER_RECT = {208, 144, 240, 152};
|
static const Rect COUNTDOWN_TIMER_RECT = {208, 144, 240, 152};
|
||||||
static uint timer = 0;
|
static uint s_timer = 0;
|
||||||
|
|
||||||
void splash_screen_on_init(void)
|
void splash_screen_on_init(void)
|
||||||
{
|
{
|
||||||
timer = 0;
|
s_timer = 0;
|
||||||
|
|
||||||
tte_printf("#{P:72,8; cx:0x%X000}DISCLAIMER", TTE_WHITE_PB);
|
tte_printf("#{P:72,8; cx:0x%X000}DISCLAIMER", TTE_WHITE_PB);
|
||||||
tte_printf(
|
tte_printf(
|
||||||
@@ -29,9 +29,9 @@ void splash_screen_on_init(void)
|
|||||||
|
|
||||||
void splash_screen_on_update(void)
|
void splash_screen_on_update(void)
|
||||||
{
|
{
|
||||||
timer++;
|
s_timer++;
|
||||||
|
|
||||||
if (timer < SPLASH_DURATION_FRAMES)
|
if (s_timer < SPLASH_DURATION_FRAMES)
|
||||||
{
|
{
|
||||||
tte_erase_rect_wrapper(COUNTDOWN_TIMER_RECT);
|
tte_erase_rect_wrapper(COUNTDOWN_TIMER_RECT);
|
||||||
tte_printf(
|
tte_printf(
|
||||||
@@ -39,7 +39,7 @@ void splash_screen_on_update(void)
|
|||||||
COUNTDOWN_TIMER_RECT.left,
|
COUNTDOWN_TIMER_RECT.left,
|
||||||
COUNTDOWN_TIMER_RECT.top,
|
COUNTDOWN_TIMER_RECT.top,
|
||||||
TTE_WHITE_PB,
|
TTE_WHITE_PB,
|
||||||
1 + (SPLASH_DURATION_FRAMES - timer) / SPLASH_FPS
|
1 + (SPLASH_DURATION_FRAMES - s_timer) / SPLASH_FPS
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!key_hit(KEY_ANY))
|
if (!key_hit(KEY_ANY))
|
||||||
|
|||||||
+9
-10
@@ -4,6 +4,8 @@
|
|||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "game_variables.h"
|
#include "game_variables.h"
|
||||||
#include "graphic_utils.h"
|
#include "graphic_utils.h"
|
||||||
|
#include "item.h"
|
||||||
|
#include "mgba_logger.h"
|
||||||
#include "pool.h"
|
#include "pool.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
@@ -169,28 +171,25 @@ int sprite_get_pb(const Sprite* sprite)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SpriteObject methods
|
// SpriteObject methods
|
||||||
SpriteObject* sprite_object_new()
|
void sprite_object_init(SpriteObject* sprite_object)
|
||||||
{
|
{
|
||||||
SpriteObject* sprite_object = POOL_GET(SpriteObject);
|
GBAL_RETURN_IF_NULL_VOID(sprite_object);
|
||||||
|
|
||||||
sprite_object->sprite = NULL;
|
sprite_object->sprite = NULL;
|
||||||
sprite_object_reset_transform(sprite_object);
|
sprite_object_reset_transform(sprite_object);
|
||||||
sprite_object->focused = false;
|
sprite_object->focused = false;
|
||||||
|
|
||||||
list_push_back(&sprite_objects_list, sprite_object);
|
list_push_back(&sprite_objects_list, sprite_object);
|
||||||
|
|
||||||
return sprite_object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sprite_object_destroy(SpriteObject** sprite_object)
|
void sprite_object_destroy(SpriteObject* sprite_object)
|
||||||
{
|
{
|
||||||
if (*sprite_object == NULL)
|
if (sprite_object == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
list_remove_data(&sprite_objects_list, *sprite_object);
|
list_remove_data(&sprite_objects_list, sprite_object);
|
||||||
|
|
||||||
sprite_destroy(&(*sprite_object)->sprite);
|
sprite_destroy(&sprite_object->sprite);
|
||||||
POOL_FREE(SpriteObject, *sprite_object);
|
|
||||||
*sprite_object = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sprite_object_set_sprite(SpriteObject* sprite_object, Sprite* sprite)
|
void sprite_object_set_sprite(SpriteObject* sprite_object, Sprite* sprite)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
static List update_cbs = LIST_DEFAULT;
|
static List s_update_cbs = LIST_DEFAULT;
|
||||||
|
|
||||||
// Used as a No Operation for game states that have no init and/or exit function.
|
// Used as a No Operation for game states that have no init and/or exit function.
|
||||||
// ricfehr3 did the work of determining whether a noop or a NULL check was more
|
// ricfehr3 did the work of determining whether a noop or a NULL check was more
|
||||||
@@ -23,7 +23,7 @@ void state_machine_register(StateMachine* state_machine)
|
|||||||
state_machine->active_update = noop;
|
state_machine->active_update = noop;
|
||||||
state_machine->state = UNDEFINED;
|
state_machine->state = UNDEFINED;
|
||||||
|
|
||||||
list_push_back(&update_cbs, &state_machine->active_update);
|
list_push_back(&s_update_cbs, &state_machine->active_update);
|
||||||
}
|
}
|
||||||
|
|
||||||
void state_machine_remove(StateMachine* state_machine)
|
void state_machine_remove(StateMachine* state_machine)
|
||||||
@@ -32,12 +32,12 @@ void state_machine_remove(StateMachine* state_machine)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
state_machine->registered = false;
|
state_machine->registered = false;
|
||||||
list_remove_data(&update_cbs, &state_machine->active_update);
|
list_remove_data(&s_update_cbs, &state_machine->active_update);
|
||||||
}
|
}
|
||||||
|
|
||||||
void state_machine_update(void)
|
void state_machine_update(void)
|
||||||
{
|
{
|
||||||
ListItr itr = list_itr_create(&update_cbs);
|
ListItr itr = list_itr_create(&s_update_cbs);
|
||||||
StateCallback* cb;
|
StateCallback* cb;
|
||||||
while ((cb = list_itr_next(&itr)))
|
while ((cb = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
CC := gcc
|
||||||
|
CFLAGS := -I../../include -I. \
|
||||||
|
-g -O3 -std=gnu23 -Wall -Werror
|
||||||
|
SRC := chinese_text_test.c \
|
||||||
|
../../source/chinese_text.c
|
||||||
|
OUT := build/chinese_text_test
|
||||||
|
|
||||||
|
$(OUT): $(SRC) | build
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
build:
|
||||||
|
mkdir -p build
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OUT)
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#include "chinese_text.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const uint32_t CHINESE_CODEPOINTS[] = {
|
||||||
|
#define CHINESE_GLYPH(codepoint) codepoint,
|
||||||
|
#include "def_chinese_glyph_table.h"
|
||||||
|
#undef CHINESE_GLYPH
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t encode_utf8_codepoint(uint32_t codepoint, char output[4])
|
||||||
|
{
|
||||||
|
if (codepoint <= 0x7F)
|
||||||
|
{
|
||||||
|
output[0] = (char)codepoint;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (codepoint <= 0x7FF)
|
||||||
|
{
|
||||||
|
output[0] = (char)(0xC0 | (codepoint >> 6));
|
||||||
|
output[1] = (char)(0x80 | (codepoint & 0x3F));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
output[0] = (char)(0xE0 | (codepoint >> 12));
|
||||||
|
output[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
|
||||||
|
output[2] = (char)(0x80 | (codepoint & 0x3F));
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t decode_like_tonc(const unsigned char encoded[2])
|
||||||
|
{
|
||||||
|
return ((uint32_t)(encoded[0] & 0x1F) << 6) | (encoded[1] & 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ascii_is_unchanged(void)
|
||||||
|
{
|
||||||
|
char output[32];
|
||||||
|
|
||||||
|
size_t output_len = chinese_text_encode("A: +20", output, sizeof(output));
|
||||||
|
|
||||||
|
assert(output_len == strlen("A: +20"));
|
||||||
|
assert(strcmp(output, "A: +20") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_supported_chinese_uses_extended_glyphs(void)
|
||||||
|
{
|
||||||
|
char output[8];
|
||||||
|
|
||||||
|
size_t output_len = chinese_text_encode("小丑牌", output, sizeof(output));
|
||||||
|
|
||||||
|
assert(output_len == 6);
|
||||||
|
assert((unsigned char)output[0] == 0xC2);
|
||||||
|
assert((unsigned char)output[1] == 0x80);
|
||||||
|
assert((unsigned char)output[2] == 0xC2);
|
||||||
|
assert((unsigned char)output[3] == 0x81);
|
||||||
|
assert((unsigned char)output[4] == 0xC2);
|
||||||
|
assert((unsigned char)output[5] == 0x82);
|
||||||
|
assert(output[6] == '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_unsupported_unicode_becomes_question_mark(void)
|
||||||
|
{
|
||||||
|
char output[8];
|
||||||
|
|
||||||
|
size_t output_len = chinese_text_encode("龘", output, sizeof(output));
|
||||||
|
|
||||||
|
assert(output_len == 1);
|
||||||
|
assert(strcmp(output, "?") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_all_custom_glyphs_round_trip_through_tonc_utf8(void)
|
||||||
|
{
|
||||||
|
size_t glyph_count = sizeof(CHINESE_CODEPOINTS) / sizeof(CHINESE_CODEPOINTS[0]);
|
||||||
|
assert(glyph_count <= 128);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < glyph_count; i++)
|
||||||
|
{
|
||||||
|
char input[4] = {'\0'};
|
||||||
|
size_t input_len = encode_utf8_codepoint(CHINESE_CODEPOINTS[i], input);
|
||||||
|
input[input_len] = '\0';
|
||||||
|
|
||||||
|
char output[4] = {'\0'};
|
||||||
|
size_t output_len = chinese_text_encode(input, output, sizeof(output));
|
||||||
|
uint32_t expected_glyph_codepoint = 128 + i;
|
||||||
|
|
||||||
|
assert(output_len == 2);
|
||||||
|
assert(((unsigned char)output[0] & 0xE0) == 0xC0);
|
||||||
|
assert(((unsigned char)output[1] & 0xC0) == 0x80);
|
||||||
|
assert(decode_like_tonc((const unsigned char*)output) == expected_glyph_codepoint);
|
||||||
|
assert(output[2] == '\0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_small_buffer_does_not_split_utf8_sequence(void)
|
||||||
|
{
|
||||||
|
char output[4] = {'x', 'x', 'x', 'x'};
|
||||||
|
|
||||||
|
size_t output_len = chinese_text_encode("小丑牌", output, sizeof(output));
|
||||||
|
|
||||||
|
assert(output_len == 2);
|
||||||
|
assert((unsigned char)output[0] == 0xC2);
|
||||||
|
assert((unsigned char)output[1] == 0x80);
|
||||||
|
assert(output[2] == '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
test_ascii_is_unchanged();
|
||||||
|
test_supported_chinese_uses_extended_glyphs();
|
||||||
|
test_unsupported_unicode_becomes_question_mark();
|
||||||
|
test_all_custom_glyphs_round_trip_through_tonc_utf8();
|
||||||
|
test_small_buffer_does_not_split_utf8_sequence();
|
||||||
|
|
||||||
|
printf("Chinese text tests passed.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ run_test() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_test bitset
|
run_test bitset
|
||||||
|
run_test chinese_text
|
||||||
run_test list
|
run_test list
|
||||||
run_test pool
|
run_test pool
|
||||||
run_test util
|
run_test util
|
||||||
|
|||||||
Reference in New Issue
Block a user