Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ ESP32-S3-BOX-3 which provides:
- Genesis emulator (gwenesis) - full speed / buttery smooth when muted; unmuted it runs a little slower but has nice sound
- Regular Controls (D-Pad/A/B/C/Start/Select) (note: A is mapped to B, B is mapped to A, and C is mapped to Y)
- Doom engine (prboom) - full speed with audio and control inputs. A is fire/enter, B is strafe/backspace, X is use, Y is weapontoggle, START is escape, and SELECT is map.
- Added haptic feedback to doom for when the player
- Fires a weapon (depending on the weapon that is fired)
- Receives damage (depending on amount of health / armor damage received)
- Interacts, e.g. with doors
- Picks up a weapon
- Picks up ammo
- Picks up health
- Picks up armor
- Picks up a power up
- Picks up a card / key
- LVGL main menu with rom select (including boxart display) and settings page
(all generated from Squareline Studio)
- LVGL emulation paused menu with save slot select, save slot image display,
Expand Down Expand Up @@ -122,6 +132,7 @@ This project has the following features (still WIP):
- [x] MSX emulator
- [x] Sega Mega Drive / Genesis emulator
- [x] Doom
- [x] Haptics :rocket:
- [ ] Dark Forces (WIP)
- [ ] SNES emulator (WIP)
- [x] uSD card (FAT) filesystem over SPI
Expand Down
44 changes: 43 additions & 1 deletion components/doom/prboom/p_inter.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ static boolean P_GiveAmmo(player_t *player, ammotype_t ammo, int num)
else
num = clipammo[ammo]/2;

// [WILLIAM] - trigger haptic effect for the player picking up ammo
// printf("Player %d picked up ammo %d\n",
// player - players, num);
R_PlayerPickupAmmo(player, ammo, num);

// give double ammo in trainer mode, you'll need in nightmare
if (gameskill == sk_baby || gameskill == sk_nightmare)
num <<= 1;
Expand Down Expand Up @@ -210,6 +215,11 @@ static boolean P_GiveWeapon(player_t *player, weapontype_t weapon, boolean dropp
gaveweapon = true;
player->weaponowned[weapon] = true;
player->pendingweapon = weapon;
// [WILLIAM] - trigger haptic effect for the player picking up a weapon
//
// printf("Player %d picked up weapon %d\n",
// player - players, weapon);
R_PlayerPickupWeapon(player, weapon);
}
return gaveweapon || gaveammo;
}
Expand All @@ -227,6 +237,10 @@ static boolean P_GiveBody(player_t *player, int num)
if (player->health > maxhealth)
player->health = maxhealth;
player->mo->health = player->health;
// [WILLIAM] - trigger haptic effect for the player picking up health
// printf("Player %d picked up health %d\n",
// player - players, num);
R_PlayerPickupHealth(player, num);
return true;
}

Expand All @@ -243,6 +257,10 @@ static boolean P_GiveArmor(player_t *player, int armortype)
return false; // don't pick up
player->armortype = armortype;
player->armorpoints = hits;
// [WILLIAM] - trigger haptic effect for the player picking up armor
// printf("Player %d picked up armor %d\n",
// player - players, armortype);
R_PlayerPickupArmor(player, armortype);
return true;
}

Expand All @@ -256,6 +274,11 @@ static void P_GiveCard(player_t *player, card_t card)
return;
player->bonuscount = BONUSADD;
player->cards[card] = 1;

// [WILLIAM] - trigger haptic effect for the player picking up a card
// printf("Player %d picked up card %d\n",
// player - players, card);
R_PlayerPickupCard(player, card);
}

//
Expand Down Expand Up @@ -289,6 +312,12 @@ boolean P_GivePower(player_t *player, int power)

if (player->powers[power] >= 0)
player->powers[power] = tics[power];

// [WILLIAM] - trigger haptic effect for the player picking up a powerup
// printf("Player %d picked up powerup %d\n",
// player - players, power);
R_PlayerPickupPowerUp(player, power);

return true;
}

Expand Down Expand Up @@ -332,6 +361,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher)

// bonus items
case SPR_BON1:
R_PlayerPickupHealth(player, 1);
player->health++; // can go over 100%
if (player->health > (maxhealth * 2))
player->health = (maxhealth * 2);
Expand All @@ -340,6 +370,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher)
break;

case SPR_BON2:
R_PlayerPickupArmor(player, 1);
player->armorpoints++; // can go over 100%
if (player->armorpoints > max_armor)
player->armorpoints = max_armor;
Expand All @@ -349,6 +380,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher)
break;

case SPR_SOUL:
R_PlayerPickupHealth(player, soul_health);
player->health += soul_health;
if (player->health > max_soul)
player->health = max_soul;
Expand All @@ -360,6 +392,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher)
case SPR_MEGA:
if (gamemode != commercial)
return;
R_PlayerPickupHealth(player, mega_health);
player->health = mega_health;
player->mo->health = player->health;
P_GiveArmor (player,blue_armor_class);
Expand Down Expand Up @@ -808,9 +841,10 @@ void P_DamageMobj(mobj_t *target,mobj_t *inflictor, mobj_t *source, int damage)
(player->cheats&CF_GODMODE || player->powers[pw_invulnerability]))
return;

int saved = 0;
if (player->armortype)
{
int saved = player->armortype == 1 ? damage/3 : damage/2;
saved = player->armortype == 1 ? damage/3 : damage/2;
if (player->armorpoints <= saved)
{
// armor is used up
Expand All @@ -825,6 +859,14 @@ void P_DamageMobj(mobj_t *target,mobj_t *inflictor, mobj_t *source, int damage)
if (player->health < 0)
player->health = 0;

// [WILLIAM]: trigger haptic effect for the player getting injured based
// on the amount of damage received (armor/base). Use damage
// (hits to health) and saved (hits to armor) to determine.
//
// printf("Player %d took %d damage (%d saved)\n",
// player - players, damage, saved);
R_PlayerHurt(player, damage, saved);

player->attacker = source;
player->damagecount += damage; // add damage after armor / invuln

Expand Down
8 changes: 5 additions & 3 deletions components/doom/prboom/p_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -1727,7 +1727,7 @@ boolean PTR_NoWayTraverse(intercept_t* in)
// Looks for special lines in front of the player to activate.
//
void P_UseLines (player_t* player)
{
{
int angle;
fixed_t x1;
fixed_t y1;
Expand All @@ -1749,10 +1749,12 @@ void P_UseLines (player_t* player)
//
// This added test makes the "oof" sound work on 2s lines -- killough:

if (P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ))
if (!comp[comp_sound] && !P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_NoWayTraverse ))
if (P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse )) {
if (!comp[comp_sound] && !P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_NoWayTraverse )) {
S_StartSound (usething, sfx_noway);
}
}
}


//
Expand Down
6 changes: 6 additions & 0 deletions components/doom/prboom/p_pspr.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,12 @@ static void P_FireWeapon(player_t *player)
newstate = weaponinfo[player->readyweapon].atkstate;
P_SetPsprite(player, ps_weapon, newstate);
P_NoiseAlert(player->mo, player->mo);
// [WILLIAM]: trigger haptic effect for firing the weapon based on the
// weaponinfo[player->readyweapon]. player->readyweapon is the
// weapon enum (e.g. 0 is fist, 1 is gun, 2 is shotgun, etc.).
//
// printf("P_FireWeapon: %d\n", player->readyweapon);
R_PlayerFire(player);
}

//
Expand Down
14 changes: 11 additions & 3 deletions components/doom/prboom/p_switch.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,11 @@ P_UseSpecialLine
linefunc = EV_DoGenCrusher;
}

if (linefunc)
if (linefunc) {
// [WILLIAM] - Callback to trigger haptics when using a switch
R_PlayerInteract(thing->player, line->special);
switch((line->special & TriggerType) >> TriggerTypeShift)
{
{
case PushOnce:
if (!side)
if (linefunc(line))
Expand All @@ -340,7 +342,8 @@ P_UseSpecialLine
return true;
default: // if not a switch/push type, do nothing here
return false;
}
}
}
}

// Switches that other things can activate.
Expand Down Expand Up @@ -372,6 +375,11 @@ P_UseSpecialLine
if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types
return false;

if (thing->player) {
// [WILLIAM] - Callback to trigger haptics when using a switch
R_PlayerInteract(thing->player, line->special);
}

// Dispatch to handler according to linedef type
switch (line->special)
{
Expand Down
25 changes: 25 additions & 0 deletions components/doom/prboom/r_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,29 @@ void R_Init(void); // Called by startup code.
void R_SetViewSize(int blocks); // Called by M_Responder.
void R_ExecuteSetViewSize(void); // cph - called by D_Display to complete a view resize

//
// HAPTICS - functions to call for various events that should trigger haptics
//

// called when the player fires a weapon, can get player->readyweapon to know
// which weapon to use for the haptic feedback
void R_PlayerFire(player_t *player);

// called when the player picks up a weapon, can get player->readyweapon to know
// which weapon
void R_PlayerPickupWeapon(player_t *player, int weapon);

void R_PlayerPickupAmmo(player_t *player, ammotype_t ammo, int num);
void R_PlayerPickupHealth(player_t *player, int health);
void R_PlayerPickupArmor(player_t *player, int armor);
void R_PlayerPickupCard(player_t *player, card_t card);
void R_PlayerPickupPowerUp(player_t *player, int powerup);

void R_PlayerInteract(player_t *player, int special);

// called when the player is hurt. damage is the amount of health lost, saved is
// the amount of health saved by armor (which is the same as the amount of armor
// lost)
void R_PlayerHurt(player_t *player, int damage, int saved);

#endif
98 changes: 97 additions & 1 deletion components/doom/src/doom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,32 @@ static std::unique_ptr<espp::Task> audio_task;

static const char *doom_argv[10];

enum class WeaponHaptics : int {
FIST = 3,
PISTOL = 2,
SHOTGUN = 10,
CHAINGUN = 12,
ROCKET_LAUNCHER = 27,
PLASMA_RIFLE = 14,
BFG9000 = 47,
CHAINSAW = 15,
SUPER_SHOTGUN = 52
};

// NOTE: The order of the enum values must match the order of the weapons in the
// game, which is the wp_* enum values defined in doomdef.h
static constexpr int WeaponHapticLookup[] = {
(int)WeaponHaptics::FIST,
(int)WeaponHaptics::PISTOL,
(int)WeaponHaptics::SHOTGUN,
(int)WeaponHaptics::CHAINGUN,
(int)WeaponHaptics::ROCKET_LAUNCHER,
(int)WeaponHaptics::PLASMA_RIFLE,
(int)WeaponHaptics::BFG9000,
(int)WeaponHaptics::CHAINSAW,
(int)WeaponHaptics::SUPER_SHOTGUN
};

// prboom includes
extern "C" {
/////////////////////////////////////////////
Expand Down Expand Up @@ -96,6 +122,76 @@ extern "C" {
{(int)GamepadState::Button::Y, &key_weapontoggle},
};

void R_PlayerFire(player_t *player) {
static auto& box = BoxEmu::get();
int weapon_fired = player->readyweapon;
if (weapon_fired >= 0 && weapon_fired < sizeof(WeaponHapticLookup) / sizeof(WeaponHapticLookup[0])) {
int haptic_effect_index = WeaponHapticLookup[weapon_fired];
box.play_haptic_effect(haptic_effect_index);
} else {
// Handle invalid weapon index (e.g., log an error or use a default effect)
// For now, we skip playing the haptic effect.
// Example: box.play_haptic_effect(DEFAULT_HAPTIC_EFFECT);
}
}

void R_PlayerHurt(player_t *player, int damage, int saved) {
static auto& box = BoxEmu::get();
int haptic_effect_index = 0;
if (damage > 5) {
// 70 - transition ramp down long smooth 1 - 100 to 0%
// 75 - transition ramp down short smooth 2 - 100 to 0%
haptic_effect_index = saved > 0 ? 70 : 75;
} else if (damage > 0) {
// 78 - transition ramp down medium sharp 1 - 100 to 0%
// 64 - transition hum 100%
haptic_effect_index = saved > 0 ? 78 : 64;
}
box.play_haptic_effect(haptic_effect_index);
}

void R_PlayerInteract(player_t *player, int special) {
static auto& box = BoxEmu::get();
// play 4 (sharp click - 100%)
box.play_haptic_effect(4);
}

void R_PlayerPickupWeapon(player_t *player, int weapon) {
static auto& box = BoxEmu::get();
// play 29 (short double click strong 3 - 60%)
box.play_haptic_effect(29);
}

void R_PlayerPickupAmmo(player_t *player, ammotype_t ammo, int num) {
static auto& box = BoxEmu::get();
// play 34 (short double sharp tick 1 - 100%)
box.play_haptic_effect(34);
}

void R_PlayerPickupHealth(player_t *player, int health) {
static auto& box = BoxEmu::get();
// play 18 (strong click 2 - 80%)
box.play_haptic_effect(18);
}

void R_PlayerPickupArmor(player_t *player, int armor) {
static auto& box = BoxEmu::get();
// play 19 (strong click 3 - 60%)
box.play_haptic_effect(19);
}

void R_PlayerPickupCard(player_t *player, card_t card) {
static auto& box = BoxEmu::get();
// play 5 (sharp click - 60%)
box.play_haptic_effect(5);
}

void R_PlayerPickupPowerUp(player_t *player, int powerup) {
static auto& box = BoxEmu::get();
// play 12 (triple click - 100%)
box.play_haptic_effect(12);
}

void I_StartFrame(void) {
}

Expand Down Expand Up @@ -244,7 +340,7 @@ extern "C" {

if (haveSFX) {
int16_t *audioBuffer = (int16_t *)mixbuffer;
int16_t *audioBufferEnd = audioBuffer + AUDIO_BUFFER_LENGTH;
const int16_t *audioBufferEnd = audioBuffer + AUDIO_BUFFER_LENGTH;
while (audioBuffer < audioBufferEnd) {
int totalSample = 0;
int totalSources = 0;
Expand Down
4 changes: 4 additions & 0 deletions components/gui/include/gui.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ class Gui {
paused_ = false;
}

int get_haptic_waveform() const {
return haptic_waveform_;
}

void set_haptic_waveform(int new_waveform) {
if (new_waveform > 123) {
new_waveform = 1;
Expand Down
Loading