Add a Cheats menu tab with engine cheat buttons and unlimited-resource switches
build / build (push) Successful in 3m42s
build / build (push) Successful in 3m42s
The new Cheats tab exposes the engine's own cheat handlers as buttons: give all items and weapons, toggle physics, next level and previous level. Physics shows its state through the button highlight. The buttons need a running game and show a message otherwise. It also has four independent session-only switches for unlimited health, energy, ammo and items (grenades and drugs). While a switch is on, its resource is topped up every frame by qs_cheat_refill() from the new engine patch 26. The switches are not saved to the config.
This commit is contained in:
@@ -358,7 +358,7 @@ keyboard (there is no hold behavior).
|
|||||||
- `keypad 0`-`keypad 9`: a controller button per digit is not useful; use the
|
- `keypad 0`-`keypad 9`: a controller button per digit is not useful; use the
|
||||||
on-screen keyboard.
|
on-screen keyboard.
|
||||||
- `toggle_freelook` (F): the free-view actions cover mouse look.
|
- `toggle_freelook` (F): the free-view actions cover mouse look.
|
||||||
- The 4 `cheat_*` entries: debug cheats.
|
- The 4 `cheat_*` entries: debug cheats. They are on the menu's Cheats tab instead.
|
||||||
- `mac_help` (disabled) and `audiolog_cancel` (its build flag is not
|
- `mac_help` (disabled) and `audiolog_cancel` (its build flag is not
|
||||||
defined): they are not real hotkeys in this build.
|
defined): they are not real hotkeys in this build.
|
||||||
- `toggle_opengl` (Ctrl+G): the Video Options tab's Renderer buttons already
|
- `toggle_opengl` (Ctrl+G): the Video Options tab's Renderer buttons already
|
||||||
|
|||||||
@@ -1250,6 +1250,7 @@ static Uint32 s_begin_control_capture_event_type;
|
|||||||
static Uint32 s_cancel_control_capture_event_type;
|
static Uint32 s_cancel_control_capture_event_type;
|
||||||
static Uint32 s_reset_player_controls_event_type;
|
static Uint32 s_reset_player_controls_event_type;
|
||||||
static Uint32 s_menu_visibility_event_type;
|
static Uint32 s_menu_visibility_event_type;
|
||||||
|
static Uint32 s_cheat_event_type;
|
||||||
|
|
||||||
void qs_config_load_startup(void) {
|
void qs_config_load_startup(void) {
|
||||||
// Registers the custom SDL event types qs_config_request_save()/_load()/
|
// Registers the custom SDL event types qs_config_request_save()/_load()/
|
||||||
@@ -1260,7 +1261,7 @@ void qs_config_load_startup(void) {
|
|||||||
// exist yet, rather than lazily from those functions themselves (which
|
// exist yet, rather than lazily from those functions themselves (which
|
||||||
// run on Android's UI thread and could otherwise race each other doing
|
// run on Android's UI thread and could otherwise race each other doing
|
||||||
// this lazily).
|
// this lazily).
|
||||||
Uint32 base = SDL_RegisterEvents(14);
|
Uint32 base = SDL_RegisterEvents(15);
|
||||||
s_save_event_type = base;
|
s_save_event_type = base;
|
||||||
s_load_event_type = base + 1;
|
s_load_event_type = base + 1;
|
||||||
s_set_renderer_event_type = base + 2;
|
s_set_renderer_event_type = base + 2;
|
||||||
@@ -1275,6 +1276,7 @@ void qs_config_load_startup(void) {
|
|||||||
s_cancel_control_capture_event_type = base + 11;
|
s_cancel_control_capture_event_type = base + 11;
|
||||||
s_reset_player_controls_event_type = base + 12;
|
s_reset_player_controls_event_type = base + 12;
|
||||||
s_menu_visibility_event_type = base + 13;
|
s_menu_visibility_event_type = base + 13;
|
||||||
|
s_cheat_event_type = base + 14;
|
||||||
|
|
||||||
QsParsedConfig parsed;
|
QsParsedConfig parsed;
|
||||||
bool haveConfig = qs_config_read_file(&parsed);
|
bool haveConfig = qs_config_read_file(&parsed);
|
||||||
@@ -1407,6 +1409,14 @@ void qs_config_request_cancel_control_capture(void) {
|
|||||||
SDL_PushEvent(&event);
|
SDL_PushEvent(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qs_config_request_cheat(int cheat_id) {
|
||||||
|
SDL_Event event;
|
||||||
|
SDL_zero(event);
|
||||||
|
event.type = s_cheat_event_type;
|
||||||
|
event.user.code = cheat_id;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
void qs_config_notify_menu_visibility(bool visible) {
|
void qs_config_notify_menu_visibility(bool visible) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
SDL_zero(event);
|
SDL_zero(event);
|
||||||
@@ -1449,6 +1459,77 @@ static void qs_apply_menu_pause(bool menu_visible) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hkeyfunc.c - the engine's own cheat hotkey handlers, and the refill added by
|
||||||
|
// android/engine-patches/26-android-cheat-refill.patch.
|
||||||
|
extern uchar toggle_giveall_func(ushort keycode, uint32_t context, intptr_t data);
|
||||||
|
extern uchar toggle_physics_func(ushort keycode, uint32_t context, intptr_t data);
|
||||||
|
extern uchar toggle_up_level_func(ushort keycode, uint32_t context, intptr_t data);
|
||||||
|
extern uchar toggle_down_level_func(ushort keycode, uint32_t context, intptr_t data);
|
||||||
|
extern void qs_cheat_refill(int what);
|
||||||
|
extern uchar physics_running;
|
||||||
|
|
||||||
|
#define QS_CHEAT_GIVE_ALL 0
|
||||||
|
#define QS_CHEAT_PHYSICS 1
|
||||||
|
#define QS_CHEAT_LEVEL_UP 2
|
||||||
|
#define QS_CHEAT_LEVEL_DOWN 3
|
||||||
|
#define QS_CHEAT_UNLIMITED_HEALTH 4
|
||||||
|
#define QS_CHEAT_UNLIMITED_ENERGY 5
|
||||||
|
#define QS_CHEAT_UNLIMITED_AMMO 6
|
||||||
|
#define QS_CHEAT_UNLIMITED_ITEMS 7
|
||||||
|
|
||||||
|
// Bits of s_cheat_unlimited; the values match qs_cheat_refill() in patch 26.
|
||||||
|
#define QS_CHEAT_MASK_HEALTH 1
|
||||||
|
#define QS_CHEAT_MASK_ENERGY 2
|
||||||
|
#define QS_CHEAT_MASK_AMMO 4
|
||||||
|
#define QS_CHEAT_MASK_ITEMS 8
|
||||||
|
|
||||||
|
static int s_cheat_unlimited = 0;
|
||||||
|
|
||||||
|
int qs_config_cheat_unlimited_mask(void) { return s_cheat_unlimited; }
|
||||||
|
bool qs_config_cheat_physics_running(void) { return physics_running != 0; }
|
||||||
|
|
||||||
|
void qs_config_frame_tick(void) {
|
||||||
|
if (s_cheat_unlimited && s_screen_profile_is_game)
|
||||||
|
qs_cheat_refill(s_cheat_unlimited);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qs_apply_cheat(int cheat_id) {
|
||||||
|
if (cheat_id >= QS_CHEAT_UNLIMITED_HEALTH && cheat_id <= QS_CHEAT_UNLIMITED_ITEMS) {
|
||||||
|
static const struct {
|
||||||
|
int mask;
|
||||||
|
const char *on, *off;
|
||||||
|
} kUnlimited[] = {
|
||||||
|
{QS_CHEAT_MASK_HEALTH, "Unlimited health on", "Unlimited health off"},
|
||||||
|
{QS_CHEAT_MASK_ENERGY, "Unlimited energy on", "Unlimited energy off"},
|
||||||
|
{QS_CHEAT_MASK_AMMO, "Unlimited ammo on", "Unlimited ammo off"},
|
||||||
|
{QS_CHEAT_MASK_ITEMS, "Unlimited items on", "Unlimited items off"},
|
||||||
|
};
|
||||||
|
const int i = cheat_id - QS_CHEAT_UNLIMITED_HEALTH;
|
||||||
|
s_cheat_unlimited ^= kUnlimited[i].mask;
|
||||||
|
message_info((s_cheat_unlimited & kUnlimited[i].mask) ? kUnlimited[i].on : kUnlimited[i].off);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The engine's own handlers assume a running game (player state, levels).
|
||||||
|
if (!s_screen_profile_is_game) {
|
||||||
|
message_info("Cheats need a running game");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (cheat_id) {
|
||||||
|
case QS_CHEAT_GIVE_ALL:
|
||||||
|
toggle_giveall_func(0, 0, 0);
|
||||||
|
break;
|
||||||
|
case QS_CHEAT_PHYSICS:
|
||||||
|
toggle_physics_func(0, 0, 0);
|
||||||
|
break;
|
||||||
|
case QS_CHEAT_LEVEL_UP:
|
||||||
|
toggle_up_level_func(0, 0, 0);
|
||||||
|
break;
|
||||||
|
case QS_CHEAT_LEVEL_DOWN:
|
||||||
|
toggle_down_level_func(0, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool qs_config_handle_event(const SDL_Event *event) {
|
bool qs_config_handle_event(const SDL_Event *event) {
|
||||||
if (event->type == s_save_event_type) {
|
if (event->type == s_save_event_type) {
|
||||||
if (qs_config_write_file())
|
if (qs_config_write_file())
|
||||||
@@ -1531,6 +1612,10 @@ bool qs_config_handle_event(const SDL_Event *event) {
|
|||||||
xr_input_capture_cancel();
|
xr_input_capture_cancel();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (event->type == s_cheat_event_type) {
|
||||||
|
qs_apply_cheat(event->user.code);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (event->type == s_menu_visibility_event_type) {
|
if (event->type == s_menu_visibility_event_type) {
|
||||||
qs_apply_menu_pause(event->user.code != 0);
|
qs_apply_menu_pause(event->user.code != 0);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -104,6 +104,20 @@ void qs_config_notify_control_capture_result(int action_index, const char *raw_n
|
|||||||
// engine's thread at pump_events() time.
|
// engine's thread at pump_events() time.
|
||||||
void qs_config_notify_menu_visibility(bool visible);
|
void qs_config_notify_menu_visibility(bool visible);
|
||||||
|
|
||||||
|
// Cheats tab (MenuOverlay.java). Same enqueue-only requests as above; the work
|
||||||
|
// runs on the engine's thread. cheat_id: 0 give everything, 1 toggle physics,
|
||||||
|
// 2 next level, 3 previous level, 4-7 toggle unlimited health, energy, ammo,
|
||||||
|
// items (grenades and drugs). Cheats need a running game (the unlimited
|
||||||
|
// switches can be set anywhere). The getters are plain scalar reads, safe from
|
||||||
|
// the UI thread; the unlimited mask has bit 0 health, 1 energy, 2 ammo, 3 items.
|
||||||
|
void qs_config_request_cheat(int cheat_id);
|
||||||
|
int qs_config_cheat_unlimited_mask(void);
|
||||||
|
bool qs_config_cheat_physics_running(void);
|
||||||
|
|
||||||
|
// Called once per frame from xr_input.c on the engine's thread; applies the
|
||||||
|
// unlimited-resource cheats that are switched on.
|
||||||
|
void qs_config_frame_tick(void);
|
||||||
|
|
||||||
// Called from xr_input.c's xr_input_suggest_bindings() when the OpenXR runtime
|
// Called from xr_input.c's xr_input_suggest_bindings() when the OpenXR runtime
|
||||||
// rejects a supported interaction profile's suggested bindings at startup.
|
// rejects a supported interaction profile's suggested bindings at startup.
|
||||||
// That is an app bug, not a config problem, so it shows
|
// That is an app bug, not a config problem, so it shows
|
||||||
|
|||||||
@@ -270,3 +270,19 @@ Java_de_ladkau_questshock_MenuOverlay_nativeSetControllerOptionsTabOpen(JNIEnv *
|
|||||||
jboolean open) {
|
jboolean open) {
|
||||||
xr_input_set_controller_options_tab_open(open == JNI_TRUE);
|
xr_input_set_controller_options_tab_open(open == JNI_TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cheats tab: same enqueue-only pattern as the other requests.
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_de_ladkau_questshock_MenuOverlay_nativeRequestCheat(JNIEnv *env, jclass clazz, jint cheatId) {
|
||||||
|
qs_config_request_cheat(cheatId);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_de_ladkau_questshock_MenuOverlay_nativeGetCheatUnlimitedMask(JNIEnv *env, jclass clazz) {
|
||||||
|
return qs_config_cheat_unlimited_mask();
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_de_ladkau_questshock_MenuOverlay_nativeGetCheatPhysicsRunning(JNIEnv *env, jclass clazz) {
|
||||||
|
return qs_config_cheat_physics_running();
|
||||||
|
}
|
||||||
|
|||||||
@@ -2000,6 +2000,7 @@ void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
|||||||
(menuVisible || freeView[xr_input_raw_input_hand(raw)])) &&
|
(menuVisible || freeView[xr_input_raw_input_hand(raw)])) &&
|
||||||
g_raw_down[raw];
|
g_raw_down[raw];
|
||||||
}
|
}
|
||||||
|
qs_config_frame_tick();
|
||||||
xr_input_update_cyber_scales(wantDown);
|
xr_input_update_cyber_scales(wantDown);
|
||||||
xr_input_apply_free_view(freeView);
|
xr_input_apply_free_view(freeView);
|
||||||
for (size_t i = 0; i < QS_NUM_KEY_ACTIONS; i++) {
|
for (size_t i = 0; i < QS_NUM_KEY_ACTIONS; i++) {
|
||||||
|
|||||||
@@ -143,14 +143,27 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
private TextView statsView;
|
private TextView statsView;
|
||||||
|
|
||||||
// Content area (tabs) - only one of mainMenuTab/videoOptionsTab/
|
// Content area (tabs) - only one of mainMenuTab/videoOptionsTab/
|
||||||
// controllerOptionsTab is VISIBLE at a time; currentTab tracks which.
|
// controllerOptionsTab/cheatsTab is VISIBLE at a time; currentTab tracks which.
|
||||||
private View mainMenuTab;
|
private View mainMenuTab;
|
||||||
private View videoOptionsTab;
|
private View videoOptionsTab;
|
||||||
private View controllerOptionsTab;
|
private View controllerOptionsTab;
|
||||||
|
private View cheatsTab;
|
||||||
private View currentTab;
|
private View currentTab;
|
||||||
private Button mainMenuTabButton;
|
private Button mainMenuTabButton;
|
||||||
private Button videoOptionsTabButton;
|
private Button videoOptionsTabButton;
|
||||||
private Button controllerOptionsTabButton;
|
private Button controllerOptionsTabButton;
|
||||||
|
private Button cheatsTabButton;
|
||||||
|
|
||||||
|
// Cheats tab
|
||||||
|
private TextView cheatsNoteView;
|
||||||
|
private Button giveAllButton;
|
||||||
|
private Button physicsButton;
|
||||||
|
private Button levelUpButton;
|
||||||
|
private Button levelDownButton;
|
||||||
|
private Button unlimitedHealthButton;
|
||||||
|
private Button unlimitedEnergyButton;
|
||||||
|
private Button unlimitedAmmoButton;
|
||||||
|
private Button unlimitedItemsButton;
|
||||||
|
|
||||||
// Main Menu tab's own two states (only one VISIBLE at a time, within
|
// Main Menu tab's own two states (only one VISIBLE at a time, within
|
||||||
// mainMenuTab) - mainMenuContent is the normal state (button row +
|
// mainMenuTab) - mainMenuContent is the normal state (button row +
|
||||||
@@ -251,14 +264,18 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
mainMenuTab = buildMainMenuTab();
|
mainMenuTab = buildMainMenuTab();
|
||||||
videoOptionsTab = buildVideoOptionsTab();
|
videoOptionsTab = buildVideoOptionsTab();
|
||||||
controllerOptionsTab = buildControllerOptionsTab();
|
controllerOptionsTab = buildControllerOptionsTab();
|
||||||
|
cheatsTab = buildCheatsTab();
|
||||||
content.addView(mainMenuTab, new FrameLayout.LayoutParams(
|
content.addView(mainMenuTab, new FrameLayout.LayoutParams(
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
||||||
content.addView(videoOptionsTab, new FrameLayout.LayoutParams(
|
content.addView(videoOptionsTab, new FrameLayout.LayoutParams(
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
||||||
content.addView(controllerOptionsTab, new FrameLayout.LayoutParams(
|
content.addView(controllerOptionsTab, new FrameLayout.LayoutParams(
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
||||||
|
content.addView(cheatsTab, new FrameLayout.LayoutParams(
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
||||||
videoOptionsTab.setVisibility(View.GONE);
|
videoOptionsTab.setVisibility(View.GONE);
|
||||||
controllerOptionsTab.setVisibility(View.GONE);
|
controllerOptionsTab.setVisibility(View.GONE);
|
||||||
|
cheatsTab.setVisibility(View.GONE);
|
||||||
currentTab = mainMenuTab;
|
currentTab = mainMenuTab;
|
||||||
panel.addView(content, new LinearLayout.LayoutParams(
|
panel.addView(content, new LinearLayout.LayoutParams(
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f));
|
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||||
@@ -923,6 +940,86 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
return profile.image;
|
return profile.image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cheats tab: the engine's own cheat functions as buttons, plus four switches
|
||||||
|
// for unlimited health, energy, ammo and items. The physics and unlimited
|
||||||
|
// buttons show their state through the selected highlight.
|
||||||
|
private static final int CHEAT_GIVE_ALL = 0;
|
||||||
|
private static final int CHEAT_PHYSICS = 1;
|
||||||
|
private static final int CHEAT_LEVEL_UP = 2;
|
||||||
|
private static final int CHEAT_LEVEL_DOWN = 3;
|
||||||
|
private static final int CHEAT_UNLIMITED_HEALTH = 4;
|
||||||
|
private static final int CHEAT_UNLIMITED_ENERGY = 5;
|
||||||
|
private static final int CHEAT_UNLIMITED_AMMO = 6;
|
||||||
|
private static final int CHEAT_UNLIMITED_ITEMS = 7;
|
||||||
|
|
||||||
|
private View buildCheatsTab() {
|
||||||
|
LinearLayout tab = new LinearLayout(activity);
|
||||||
|
tab.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
tab.setGravity(Gravity.CENTER);
|
||||||
|
tab.setPadding(24, 16, 24, 16);
|
||||||
|
|
||||||
|
cheatsNoteView = new TextView(activity);
|
||||||
|
cheatsNoteView.setTextColor(0xFFFFD060);
|
||||||
|
cheatsNoteView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
|
||||||
|
cheatsNoteView.setGravity(Gravity.CENTER);
|
||||||
|
cheatsNoteView.setPadding(0, 0, 0, 16);
|
||||||
|
cheatsNoteView.setText(Strings.get("cheats_note", currentLanguage));
|
||||||
|
tab.addView(cheatsNoteView);
|
||||||
|
|
||||||
|
LinearLayout firstRow = new LinearLayout(activity);
|
||||||
|
firstRow.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
giveAllButton = addSelectableButton(firstRow, Strings.get("cheat_give_all", currentLanguage),
|
||||||
|
v -> nativeRequestCheat(CHEAT_GIVE_ALL));
|
||||||
|
physicsButton = addSelectableButton(firstRow, Strings.get("cheat_physics", currentLanguage),
|
||||||
|
v -> {
|
||||||
|
nativeRequestCheat(CHEAT_PHYSICS);
|
||||||
|
refreshCheatsSelection();
|
||||||
|
});
|
||||||
|
tab.addView(firstRow);
|
||||||
|
|
||||||
|
LinearLayout secondRow = new LinearLayout(activity);
|
||||||
|
secondRow.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
levelUpButton = addSelectableButton(secondRow, Strings.get("cheat_level_up", currentLanguage),
|
||||||
|
v -> nativeRequestCheat(CHEAT_LEVEL_UP));
|
||||||
|
levelDownButton = addSelectableButton(secondRow, Strings.get("cheat_level_down", currentLanguage),
|
||||||
|
v -> nativeRequestCheat(CHEAT_LEVEL_DOWN));
|
||||||
|
tab.addView(secondRow);
|
||||||
|
|
||||||
|
LinearLayout thirdRow = new LinearLayout(activity);
|
||||||
|
thirdRow.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
unlimitedHealthButton = addSelectableButton(thirdRow,
|
||||||
|
Strings.get("cheat_unlimited_health", currentLanguage), v -> toggleUnlimited(CHEAT_UNLIMITED_HEALTH));
|
||||||
|
unlimitedEnergyButton = addSelectableButton(thirdRow,
|
||||||
|
Strings.get("cheat_unlimited_energy", currentLanguage), v -> toggleUnlimited(CHEAT_UNLIMITED_ENERGY));
|
||||||
|
tab.addView(thirdRow);
|
||||||
|
|
||||||
|
LinearLayout fourthRow = new LinearLayout(activity);
|
||||||
|
fourthRow.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
unlimitedAmmoButton = addSelectableButton(fourthRow,
|
||||||
|
Strings.get("cheat_unlimited_ammo", currentLanguage), v -> toggleUnlimited(CHEAT_UNLIMITED_AMMO));
|
||||||
|
unlimitedItemsButton = addSelectableButton(fourthRow,
|
||||||
|
Strings.get("cheat_unlimited_items", currentLanguage), v -> toggleUnlimited(CHEAT_UNLIMITED_ITEMS));
|
||||||
|
tab.addView(fourthRow);
|
||||||
|
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The requests run on the engine's thread a frame later, so the physics
|
||||||
|
// and unlimited highlights catch up on the next periodic refresh.
|
||||||
|
private void refreshCheatsSelection() {
|
||||||
|
physicsButton.setSelected(nativeGetCheatPhysicsRunning());
|
||||||
|
int mask = nativeGetCheatUnlimitedMask();
|
||||||
|
unlimitedHealthButton.setSelected((mask & 1) != 0);
|
||||||
|
unlimitedEnergyButton.setSelected((mask & 2) != 0);
|
||||||
|
unlimitedAmmoButton.setSelected((mask & 4) != 0);
|
||||||
|
unlimitedItemsButton.setSelected((mask & 8) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleUnlimited(int cheatId) {
|
||||||
|
nativeRequestCheat(cheatId);
|
||||||
|
refreshCheatsSelection();
|
||||||
|
}
|
||||||
|
|
||||||
private View buildTabBar() {
|
private View buildTabBar() {
|
||||||
LinearLayout tabBar = new LinearLayout(activity);
|
LinearLayout tabBar = new LinearLayout(activity);
|
||||||
tabBar.setOrientation(LinearLayout.HORIZONTAL);
|
tabBar.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
@@ -969,7 +1066,21 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
controllerOptionsTabButton.setOnTouchListener(CLICK_ON_TOUCH_UP);
|
controllerOptionsTabButton.setOnTouchListener(CLICK_ON_TOUCH_UP);
|
||||||
registerHoverable(controllerOptionsTabButton);
|
registerHoverable(controllerOptionsTabButton);
|
||||||
tabBar.addView(controllerOptionsTabButton,
|
tabBar.addView(controllerOptionsTabButton,
|
||||||
new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
|
new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1.3f));
|
||||||
|
|
||||||
|
View separator3 = new View(activity);
|
||||||
|
separator3.setBackgroundColor(0xFFFFFFFF);
|
||||||
|
tabBar.addView(separator3, new LinearLayout.LayoutParams(2, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
|
||||||
|
cheatsTabButton = new Button(activity);
|
||||||
|
cheatsTabButton.setText(Strings.get("cheats", currentLanguage));
|
||||||
|
cheatsTabButton.setBackground(selectableButtonBackground());
|
||||||
|
cheatsTabButton.setTextColor(buttonTextColors());
|
||||||
|
cheatsTabButton.setOnClickListener(v -> switchTab(cheatsTab));
|
||||||
|
cheatsTabButton.setOnTouchListener(CLICK_ON_TOUCH_UP);
|
||||||
|
registerHoverable(cheatsTabButton);
|
||||||
|
tabBar.addView(cheatsTabButton,
|
||||||
|
new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 0.7f));
|
||||||
|
|
||||||
return tabBar;
|
return tabBar;
|
||||||
}
|
}
|
||||||
@@ -992,6 +1103,7 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
mainMenuTabButton.setSelected(tab == mainMenuTab);
|
mainMenuTabButton.setSelected(tab == mainMenuTab);
|
||||||
videoOptionsTabButton.setSelected(tab == videoOptionsTab);
|
videoOptionsTabButton.setSelected(tab == videoOptionsTab);
|
||||||
controllerOptionsTabButton.setSelected(tab == controllerOptionsTab);
|
controllerOptionsTabButton.setSelected(tab == controllerOptionsTab);
|
||||||
|
cheatsTabButton.setSelected(tab == cheatsTab);
|
||||||
nativeSetControllerOptionsTabOpen(tab == controllerOptionsTab);
|
nativeSetControllerOptionsTabOpen(tab == controllerOptionsTab);
|
||||||
resetHover();
|
resetHover();
|
||||||
if (tab == videoOptionsTab) {
|
if (tab == videoOptionsTab) {
|
||||||
@@ -1007,6 +1119,8 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
controllerScrollView.scrollTo(0, 0);
|
controllerScrollView.scrollTo(0, 0);
|
||||||
controllerScrollBar.setFraction(0f);
|
controllerScrollBar.setFraction(0f);
|
||||||
refreshControllerOptionsSelection();
|
refreshControllerOptionsSelection();
|
||||||
|
} else if (tab == cheatsTab) {
|
||||||
|
refreshCheatsSelection();
|
||||||
}
|
}
|
||||||
relayoutAndRedraw();
|
relayoutAndRedraw();
|
||||||
}
|
}
|
||||||
@@ -1172,6 +1286,8 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
refreshLanguageSelection();
|
refreshLanguageSelection();
|
||||||
} else if (currentTab == controllerOptionsTab) {
|
} else if (currentTab == controllerOptionsTab) {
|
||||||
refreshControllerOptionsSelection();
|
refreshControllerOptionsSelection();
|
||||||
|
} else if (currentTab == cheatsTab) {
|
||||||
|
refreshCheatsSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The stats text's width changes every tick (digit count, "ms"
|
// The stats text's width changes every tick (digit count, "ms"
|
||||||
@@ -1227,6 +1343,16 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
gameScreenDistanceLabel.setText(Strings.get("distance", currentLanguage));
|
gameScreenDistanceLabel.setText(Strings.get("distance", currentLanguage));
|
||||||
|
|
||||||
controllerOptionsTabButton.setText(Strings.get("controller_options", currentLanguage));
|
controllerOptionsTabButton.setText(Strings.get("controller_options", currentLanguage));
|
||||||
|
cheatsTabButton.setText(Strings.get("cheats", currentLanguage));
|
||||||
|
cheatsNoteView.setText(Strings.get("cheats_note", currentLanguage));
|
||||||
|
giveAllButton.setText(Strings.get("cheat_give_all", currentLanguage));
|
||||||
|
physicsButton.setText(Strings.get("cheat_physics", currentLanguage));
|
||||||
|
levelUpButton.setText(Strings.get("cheat_level_up", currentLanguage));
|
||||||
|
levelDownButton.setText(Strings.get("cheat_level_down", currentLanguage));
|
||||||
|
unlimitedHealthButton.setText(Strings.get("cheat_unlimited_health", currentLanguage));
|
||||||
|
unlimitedEnergyButton.setText(Strings.get("cheat_unlimited_energy", currentLanguage));
|
||||||
|
unlimitedAmmoButton.setText(Strings.get("cheat_unlimited_ammo", currentLanguage));
|
||||||
|
unlimitedItemsButton.setText(Strings.get("cheat_unlimited_items", currentLanguage));
|
||||||
controllerGenericNoteView.setText(Strings.get("generic_profile_note", currentLanguage));
|
controllerGenericNoteView.setText(Strings.get("generic_profile_note", currentLanguage));
|
||||||
controlResetButton.setText(Strings.get("reset_to_defaults", currentLanguage));
|
controlResetButton.setText(Strings.get("reset_to_defaults", currentLanguage));
|
||||||
for (int i = 0; i < ACTION_KEYS.length; i++) {
|
for (int i = 0; i < ACTION_KEYS.length; i++) {
|
||||||
@@ -1351,6 +1477,12 @@ public class MenuOverlay extends OverlayPanel {
|
|||||||
// or reassigned - see switchTab().
|
// or reassigned - see switchTab().
|
||||||
private static native void nativeSetControllerOptionsTabOpen(boolean open);
|
private static native void nativeSetControllerOptionsTabOpen(boolean open);
|
||||||
|
|
||||||
|
// Cheats tab - see qs_config.h. cheatId: 0 give all, 1 toggle physics, 2 next level,
|
||||||
|
// 3 previous level, 4-7 toggle unlimited health/energy/ammo/items.
|
||||||
|
private static native void nativeRequestCheat(int cheatId);
|
||||||
|
private static native int nativeGetCheatUnlimitedMask();
|
||||||
|
private static native boolean nativeGetCheatPhysicsRunning();
|
||||||
|
|
||||||
// Called from xr_overlay.c's xr_overlay_init_jni(), on the render
|
// Called from xr_overlay.c's xr_overlay_init_jni(), on the render
|
||||||
// thread, right after it resolves this class via the activity's own
|
// thread, right after it resolves this class via the activity's own
|
||||||
// ClassLoader (plain FindClass() can't see app classes from a thread
|
// ClassLoader (plain FindClass() can't see app classes from a thread
|
||||||
|
|||||||
@@ -119,6 +119,19 @@ final class Strings {
|
|||||||
put("action_mfd_right_4", "Right MFD: Target", "MFD droit : cible", "Rechtes MFD: Ziel");
|
put("action_mfd_right_4", "Right MFD: Target", "MFD droit : cible", "Rechtes MFD: Ziel");
|
||||||
put("action_mfd_right_5", "Right MFD: Info", "MFD droit : info", "Rechtes MFD: Info");
|
put("action_mfd_right_5", "Right MFD: Info", "MFD droit : info", "Rechtes MFD: Info");
|
||||||
put("action_fire_weapon", "Fire Weapon", "Tirer", "Waffe abfeuern");
|
put("action_fire_weapon", "Fire Weapon", "Tirer", "Waffe abfeuern");
|
||||||
|
// Cheats tab.
|
||||||
|
put("cheats", "Cheats", "Triches", "Cheats");
|
||||||
|
put("cheats_note", "Cheats need a running game.", "Les triches nécessitent une partie en cours.",
|
||||||
|
"Cheats benötigen ein laufendes Spiel.");
|
||||||
|
put("cheat_give_all", "Give All Items & Weapons", "Tout obtenir", "Alles erhalten");
|
||||||
|
put("cheat_physics", "Physics", "Physique", "Physik");
|
||||||
|
put("cheat_level_up", "Next Level", "Niveau suivant", "Nächstes Level");
|
||||||
|
put("cheat_level_down", "Previous Level", "Niveau précédent", "Vorheriges Level");
|
||||||
|
put("cheat_unlimited_health", "Unlimited Health", "Santé illimitée", "Unbegrenzt Leben");
|
||||||
|
put("cheat_unlimited_energy", "Unlimited Energy", "Énergie illimitée", "Unbegrenzt Energie");
|
||||||
|
put("cheat_unlimited_ammo", "Unlimited Ammo", "Munitions illimitées", "Unbegrenzt Munition");
|
||||||
|
put("cheat_unlimited_items", "Unlimited Items", "Objets illimités", "Unbegrenzt Items");
|
||||||
|
|
||||||
put("action_free_left_thumbstick_view", "Free View (Left Stick)",
|
put("action_free_left_thumbstick_view", "Free View (Left Stick)",
|
||||||
"Vue libre (stick gauche)", "Freie Ansicht (linker Stick)");
|
"Vue libre (stick gauche)", "Freie Ansicht (linker Stick)");
|
||||||
put("action_free_right_thumbstick_view", "Free View (Right Stick)",
|
put("action_free_right_thumbstick_view", "Free View (Right Stick)",
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
--- a/src/GameSrc/hkeyfunc.c
|
||||||
|
+++ b/src/GameSrc/hkeyfunc.c
|
||||||
|
@@ -1465,3 +1465,71 @@
|
||||||
|
#pragma enable_message(202)
|
||||||
|
|
||||||
|
#endif // NOT_YET
|
||||||
|
+
|
||||||
|
+#ifdef __ANDROID__
|
||||||
|
+// Tops up parts of the player's state; android/app/src/main/cpp/qs_config.c
|
||||||
|
+// calls it once per frame with the "unlimited" cheats that are switched on.
|
||||||
|
+// `what` is a bitmask: 1 health, 2 energy, 4 ammo, 8 items (the same values as
|
||||||
|
+// QS_CHEAT_MASK_* in qs_config.c). Only conventional guns use ammo: beam and
|
||||||
|
+// melee weapons keep heat in the same field, so they are left alone (beams draw
|
||||||
|
+// on energy). Spare ammo clips, grenades and drugs are only refilled for kinds
|
||||||
|
+// the player already holds, so the cheat never creates items.
|
||||||
|
+void qs_cheat_refill(int what) {
|
||||||
|
+ const int kAmmoFloor = 99;
|
||||||
|
+ const int kItemFloor = 99;
|
||||||
|
+ uchar vitalsChanged = FALSE;
|
||||||
|
+ uchar inventoryChanged = FALSE;
|
||||||
|
+
|
||||||
|
+ if (player_struct.dead)
|
||||||
|
+ return;
|
||||||
|
+ if ((what & 1) && player_struct.hit_points < PLAYER_MAX_HP) {
|
||||||
|
+ player_struct.hit_points = PLAYER_MAX_HP;
|
||||||
|
+ vitalsChanged = TRUE;
|
||||||
|
+ }
|
||||||
|
+ if ((what & 1) && player_struct.cspace_hp < PLAYER_MAX_HP) {
|
||||||
|
+ player_struct.cspace_hp = PLAYER_MAX_HP;
|
||||||
|
+ vitalsChanged = TRUE;
|
||||||
|
+ }
|
||||||
|
+ if ((what & 2) && player_struct.energy < 255) {
|
||||||
|
+ player_struct.energy = 255;
|
||||||
|
+ vitalsChanged = TRUE;
|
||||||
|
+ }
|
||||||
|
+ if (vitalsChanged)
|
||||||
|
+ chg_set_flg(VITALS_UPDATE);
|
||||||
|
+
|
||||||
|
+ if (what & 4) {
|
||||||
|
+ for (int i = 0; i < NUM_WEAPON_SLOTS; i++) {
|
||||||
|
+ weapon_slot *slot = &player_struct.weapons[i];
|
||||||
|
+ if (slot->type != EMPTY_WEAPON_SLOT && slot->type <= GUN_SUBCLASS_SPECIAL &&
|
||||||
|
+ slot->ammo < kAmmoFloor) {
|
||||||
|
+ slot->ammo = kAmmoFloor;
|
||||||
|
+ inventoryChanged = TRUE;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ for (int i = 0; i < NUM_AMMO_TYPES; i++) {
|
||||||
|
+ if (player_struct.cartridges[i] > 0 && player_struct.cartridges[i] < kItemFloor) {
|
||||||
|
+ player_struct.cartridges[i] = kItemFloor;
|
||||||
|
+ inventoryChanged = TRUE;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (what & 8) {
|
||||||
|
+ for (int i = 0; i < NUM_GRENADEZ; i++) {
|
||||||
|
+ if (player_struct.grenades[i] > 0 && player_struct.grenades[i] < kItemFloor) {
|
||||||
|
+ player_struct.grenades[i] = kItemFloor;
|
||||||
|
+ inventoryChanged = TRUE;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ for (int i = 0; i < NUM_DRUGZ; i++) {
|
||||||
|
+ if (player_struct.drugs[i] > 0 && player_struct.drugs[i] < kItemFloor) {
|
||||||
|
+ player_struct.drugs[i] = kItemFloor;
|
||||||
|
+ inventoryChanged = TRUE;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (inventoryChanged) {
|
||||||
|
+ chg_set_flg(INVENTORY_UPDATE);
|
||||||
|
+ mfd_force_update();
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+#endif
|
||||||
Reference in New Issue
Block a user