/**
* @ file player . h
*
* Interface of player functionality , leveling , actions , creation , loading , etc .
*/
# pragma once
# include <array>
# include <cstdint>
# include "diablo.h"
# include "engine.h"
# include "gendung.h"
# include "items.h"
# include "multi.h"
# include "spelldat.h"
# include "path.h"
# include "interfac.h"
# include "utils/enum_traits.h"
# include "engine/animationinfo.h"
namespace devilution {
// number of inventory grid cells
# define NUM_INV_GRID_ELEM 40
# define MAXBELTITEMS 8
# define MAXRESIST 75
# define MAXCHARLEVEL 51
# define MAX_SPELL_LEVEL 15
# define PLR_NAME_LEN 32
/** Walking directions */
enum {
// clang-format off
WALK_NE = 1 ,
WALK_NW = 2 ,
WALK_SE = 3 ,
WALK_SW = 4 ,
WALK_N = 5 ,
WALK_E = 6 ,
WALK_S = 7 ,
WALK_W = 8 ,
WALK_NONE = - 1 ,
// clang-format on
} ;
enum class HeroClass : uint8_t {
Warrior ,
Rogue ,
Sorcerer ,
Monk ,
Bard ,
Barbarian ,
LAST = Barbarian
} ;
enum class CharacterAttribute : uint8_t {
Strength ,
Magic ,
Dexterity ,
Vitality ,
FIRST = Strength ,
LAST = Vitality
} ;
// Logical equipment locations
enum inv_body_loc : uint8_t {
INVLOC_HEAD ,
INVLOC_RING_LEFT ,
INVLOC_RING_RIGHT ,
INVLOC_AMULET ,
INVLOC_HAND_LEFT ,
INVLOC_HAND_RIGHT ,
INVLOC_CHEST ,
NUM_INVLOC ,
} ;
enum class player_graphic : uint16_t {
Stand ,
Walk ,
Attack ,
Hit ,
Lightning ,
Fire ,
Magic ,
Death ,
Block ,
LAST = Block
} ;
enum anim_weapon_id : uint8_t {
ANIM_ID_UNARMED ,
ANIM_ID_UNARMED_SHIELD ,
ANIM_ID_SWORD ,
ANIM_ID_SWORD_SHIELD ,
ANIM_ID_BOW ,
ANIM_ID_AXE ,
ANIM_ID_MACE ,
ANIM_ID_MACE_SHIELD ,
ANIM_ID_STAFF ,
} ;
enum PLR_MODE : uint8_t {
PM_STAND ,
PM_WALK , //Movement towards N, NW, or NE
PM_WALK2 , //Movement towards S, SW, or SE
PM_WALK3 , //Movement towards W or E
PM_ATTACK ,
PM_RATTACK ,
PM_BLOCK ,
PM_GOTHIT ,
PM_DEATH ,
PM_SPELL ,
PM_NEWLVL ,
PM_QUIT ,
} ;
enum action_id : int8_t {
// clang-format off
ACTION_WALK = - 2 , // Automatic walk when using gamepad
ACTION_NONE = - 1 ,
ACTION_ATTACK = 9 ,
ACTION_RATTACK = 10 ,
ACTION_SPELL = 12 ,
ACTION_OPERATE = 13 ,
ACTION_DISARM = 14 ,
ACTION_PICKUPITEM = 15 , // put item in hand (inventory screen open)
ACTION_PICKUPAITEM = 16 , // put item in inventory
ACTION_TALK = 17 ,
ACTION_OPERATETK = 18 , // operate via telekinesis
ACTION_ATTACKMON = 20 ,
ACTION_ATTACKPLR = 21 ,
ACTION_RATTACKMON = 22 ,
ACTION_RATTACKPLR = 23 ,
ACTION_SPELLMON = 24 ,
ACTION_SPELLPLR = 25 ,
ACTION_SPELLWALL = 26 ,
// clang-format on
} ;
enum player_weapon_type : uint8_t {
WT_MELEE ,
WT_RANGED ,
} ;
/*
* @ brief Contains Data ( CelSprites ) for a player graphic ( player_graphic )
*/
struct PlayerAnimationData {
/*
* @ brief CelSprites for the different directions
*/
std : : array < std : : optional < CelSprite > , 8 > CelSpritesForDirections ;
/*
* @ brief Raw Data ( binary ) of the CL2 file .
* Is referenced from CelSprite in CelSpritesForDirections
*/
std : : unique_ptr < byte [ ] > RawData ;
} ;
struct PlayerStruct {
PlayerStruct ( ) = default ;
PlayerStruct ( PlayerStruct & & ) noexcept = default ;
PlayerStruct & operator = ( PlayerStruct & & ) noexcept = default ;
PLR_MODE _pmode ;
int8_t walkpath [ MAX_PATH_LENGTH ] ;
bool plractive ;
action_id destAction ;
int destParam1 ;
int destParam2 ;
Direction destParam3 ;
int destParam4 ;
int plrlevel ;
ActorPosition position ;
Direction _pdir ; // Direction faced by player (direction enum)
int _pgfxnum ; // Bitmask indicating what variant of the sprite the player is using. Lower byte define weapon (anim_weapon_id) and higher values define armour (starting with anim_armor_id)
/*
* @ brief Contains Information for current Animation
*/
AnimationInfo AnimInfo ;
int _plid ;
int _pvid ;
spell_id _pSpell ;
spell_type _pSplType ;
int8_t _pSplFrom ; // TODO Create enum
spell_id _pTSpell ;
spell_type _pTSplType ;
spell_id _pRSpell ;
spell_type _pRSplType ;
spell_id _pSBkSpell ;
spell_type _pSBkSplType ;
int8_t _pSplLvl [ 64 ] ;
uint64_t _pMemSpells ; // Bitmask of learned spells
uint64_t _pAblSpells ; // Bitmask of abilities
uint64_t _pScrlSpells ; // Bitmask of spells available via scrolls
uint8_t _pSpellFlags ;
spell_id _pSplHotKey [ 4 ] ;
spell_type _pSplTHotKey [ 4 ] ;
player_weapon_type _pwtype ;
bool _pBlockFlag ;
bool _pInvincible ;
int8_t _pLightRad ;
bool _pLvlChanging ; // True when the player is transitioning between levels
char _pName [ PLR_NAME_LEN ] ;
HeroClass _pClass ;
int _pStrength ;
int _pBaseStr ;
int _pMagic ;
int _pBaseMag ;
int _pDexterity ;
int _pBaseDex ;
int _pVitality ;
int _pBaseVit ;
int _pStatPts ;
int _pDamageMod ;
int _pBaseToBlk ;
int _pHPBase ;
int _pMaxHPBase ;
int _pHitPoints ;
int _pMaxHP ;
int _pHPPer ;
int _pManaBase ;
int _pMaxManaBase ;
int _pMana ;
int _pMaxMana ;
int _pManaPer ;
int8_t _pLevel ;
int8_t _pMaxLvl ;
int _pExperience ;
int _pMaxExp ;
int _pNextExper ;
int8_t _pArmorClass ;
int8_t _pMagResist ;
int8_t _pFireResist ;
int8_t _pLghtResist ;
int _pGold ;
bool _pInfraFlag ;
/** Player's direction when ending movement. Also used for casting direction of SPL_FIREWALL. */
Direction tempDirection ;
/** Used for spell level, and X component of _pVar5 */
int _pVar4 ;
/** Used for storing position of a tile which should have its BFLAG_PLAYERLR flag removed after walking. When starting to walk the game places the player in the dPlayer array -1 in the Y coordinate, and uses BFLAG_PLAYERLR to check if it should be using -1 to the Y coordinate when rendering the player (also used for storing the level of a spell when the player casts it) */
int _pVar5 ;
/** Used for stalling the appearance of the options screen after dying in singleplayer */
int deathFrame ;
bool _pLvlVisited [ NUMLEVELS ] ;
bool _pSLvlVisited [ NUMLEVELS ] ; // only 10 used
/*
* @ brief Contains Data ( Sprites ) for the different Animations
*/
std : : array < PlayerAnimationData , enum_size < player_graphic > : : value > AnimationData ;
int _pNFrames ;
int _pWFrames ;
int _pAFrames ;
int _pAFNum ;
int _pSFrames ;
int _pSFNum ;
int _pHFrames ;
int _pDFrames ;
int _pBFrames ;
ItemStruct InvBody [ NUM_INVLOC ] ;
ItemStruct InvList [ NUM_INV_GRID_ELEM ] ;
int _pNumInv ;
int8_t InvGrid [ NUM_INV_GRID_ELEM ] ;
ItemStruct SpdList [ MAXBELTITEMS ] ;
ItemStruct HoldItem ;
int _pIMinDam ;
int _pIMaxDam ;
int _pIAC ;
int _pIBonusDam ;
int _pIBonusToHit ;
int _pIBonusAC ;
int _pIBonusDamMod ;
/** Bitmask of staff spell */
uint64_t _pISpells ;
/** Bitmask using item_special_effect */
int _pIFlags ;
int _pIGetHit ;
int8_t _pISplLvlAdd ;
int _pISplDur ;
int _pIEnAc ;
int _pIFMinDam ;
int _pIFMaxDam ;
int _pILMinDam ;
int _pILMaxDam ;
item_misc_id _pOilType ;
uint8_t pTownWarps ;
uint8_t pDungMsgs ;
uint8_t pLvlLoad ;
bool pBattleNet ;
bool pManaShield ;
uint8_t pDungMsgs2 ;
bool pOriginalCathedral ;
uint16_t wReflections ;
uint8_t pDiabloKillLevel ;
_difficulty pDifficulty ;
uint32_t pDamAcFlags ;
void CalcScrolls ( ) ;
bool HasItem ( int item , int * idx = nullptr ) const ;
/**
* @ brief Remove an item from player inventory
* @ param pnum Player index
* @ param iv invList index of item to be removed
* @ param calcScrolls If true , CalcScrolls ( ) gets called after removing item
*/
void RemoveInvItem ( int iv , bool calcScrolls = true ) ;
void RemoveSpdBarItem ( int iv ) ;
/**
* @ brief Gets the most valuable item out of all the player ' s items that match the given predicate .
* @ param itemPredicate The predicate used to match the items .
* @ return The most valuable item out of all the player ' s items that match the given predicate , or ' nullptr ' in case no
* matching items were found .
*/
template < typename TPredicate >
const ItemStruct * GetMostValuableItem ( const TPredicate & itemPredicate ) const
{
const auto getMostValuableItem = [ & itemPredicate ] ( const ItemStruct * begin , const ItemStruct * end , const ItemStruct * mostValuableItem = nullptr ) {
for ( const auto * item = begin ; item < end ; item + + ) {
if ( item - > isEmpty ( ) | | ! itemPredicate ( * item ) ) {
continue ;
}
if ( mostValuableItem = = nullptr | | item - > _iIvalue > mostValuableItem - > _iIvalue ) {
mostValuableItem = item ;
}
}
return mostValuableItem ;
} ;
const ItemStruct * mostValuableItem = getMostValuableItem ( SpdList , SpdList + MAXBELTITEMS ) ;
mostValuableItem = getMostValuableItem ( InvBody , InvBody + inv_body_loc : : NUM_INVLOC , mostValuableItem ) ;
mostValuableItem = getMostValuableItem ( InvList , InvList + _pNumInv , mostValuableItem ) ;
return mostValuableItem ;
}
/**
* @ brief Gets the base value of the player ' s specified attribute .
* @ param attribute The attribute to retrieve the base value for
* @ return The base value for the requested attribute .
*/
int GetBaseAttributeValue ( CharacterAttribute attribute ) const ;
/**
* @ brief Gets the maximum value of the player ' s specified attribute .
* @ param attribute The attribute to retrieve the maximum value for
* @ return The maximum value for the requested attribute .
*/
int GetMaximumAttributeValue ( CharacterAttribute attribute ) const ;
/**
* @ brief Get the tile coordinates a player is moving to ( if not moving , then it corresponds to current position ) .
*/
Point GetTargetPosition ( ) const ;
/**
* @ brief Says a speech line .
* @ todo BUGFIX Prevent more then one speech to be played at a time ( reject new requests ) .
*/
void Say ( HeroSpeech speechId ) const ;
/**
* @ brief Says a speech line after a given delay .
* @ param delay Multiple of 50 ms wait before starting the speech
*/
void Say ( HeroSpeech speechId , int delay ) const ;
/**
* @ brief Says a speech line , without random variants .
*/
void SaySpecific ( HeroSpeech speechId ) const ;
/**
* @ brief Attempts to stop the player from performing any queued up action . If the player is currently walking , his walking will
* stop as soon as he reaches the next tile . If any action was queued with the previous command ( like targeting a monster ,
* opening a chest , picking an item up , etc ) this action will also be cancelled .
*/
void Stop ( ) ;
/**
* @ brief Is the player currently walking ?
*/
bool IsWalking ( ) const ;
/**
* @ brief Resets all Data of the current PlayerStruct
*/
void Reset ( ) ;
} ;
extern int myplr ;
extern PlayerStruct plr [ MAX_PLRS ] ;
extern bool deathflag ;
extern int ToBlkTbl [ enum_size < HeroClass > : : value ] ;
void LoadPlrGFX ( PlayerStruct & player , player_graphic graphic ) ;
void InitPlayerGFX ( int pnum ) ;
void ResetPlayerGFX ( PlayerStruct & player ) ;
/**
* @ brief Sets the new Player Animation with all relevant information for rendering
* @ param pnum Player Id
* @ param graphic What player animation should be displayed
* @ param dir Direction of the animation
* @ param numberOfFrames Number of Frames in Animation
* @ param delayLen Delay after each Animation sequence
* @ param flags Specifies what special logics are applied to this Animation
* @ param numSkippedFrames Number of Frames that will be skipped ( for example with modifier " faster attack " )
* @ param distributeFramesBeforeFrame Distribute the numSkippedFrames only before this frame
*/
void NewPlrAnim ( PlayerStruct & player , player_graphic graphic , Direction dir , int numberOfFrames , int delayLen , AnimationDistributionFlags flags = AnimationDistributionFlags : : None , int numSkippedFrames = 0 , int distributeFramesBeforeFrame = 0 ) ;
void SetPlrAnims ( PlayerStruct & player ) ;
void CreatePlayer ( int playerId , HeroClass c ) ;
int CalcStatDiff ( PlayerStruct & player ) ;
# ifdef _DEBUG
void NextPlrLevel ( int pnum ) ;
# endif
void AddPlrExperience ( int pnum , int lvl , int exp ) ;
void AddPlrMonstExper ( int lvl , int exp , char pmask ) ;
void ApplyPlrDamage ( int pnum , int dam , int minHP = 0 , int frac = 0 , int earflag = 0 ) ;
void InitPlayer ( int pnum , bool FirstTime ) ;
void InitMultiView ( ) ;
bool SolidLoc ( Point position ) ;
void PlrClrTrans ( Point position ) ;
void PlrDoTrans ( Point position ) ;
void SetPlayerOld ( PlayerStruct & player ) ;
void FixPlayerLocation ( int pnum , Direction bDir ) ;
void StartStand ( int pnum , Direction dir ) ;
void StartAttack ( int pnum , Direction d ) ;
void StartPlrBlock ( int pnum , Direction dir ) ;
void FixPlrWalkTags ( int pnum ) ;
void RemovePlrFromMap ( int pnum ) ;
void StartPlrHit ( int pnum , int dam , bool forcehit ) ;
void StartPlayerKill ( int pnum , int earflag ) ;
void DropHalfPlayersGold ( int pnum ) ;
void StripTopGold ( int pnum ) ;
void SyncPlrKill ( int pnum , int earflag ) ;
void RemovePlrMissiles ( int pnum ) ;
void StartNewLvl ( int pnum , interface_mode fom , int lvl ) ;
void RestartTownLvl ( int pnum ) ;
void StartWarpLvl ( int pnum , int pidx ) ;
void ProcessPlayers ( ) ;
void ClrPlrPath ( PlayerStruct & player ) ;
bool PosOkPlayer ( int pnum , Point position ) ;
void MakePlrPath ( int pnum , Point targetPosition , bool endspace ) ;
void CheckPlrSpell ( ) ;
void SyncPlrAnim ( int pnum ) ;
void SyncInitPlrPos ( int pnum ) ;
void SyncInitPlr ( int pnum ) ;
void CheckStats ( PlayerStruct & player ) ;
void ModifyPlrStr ( int p , int l ) ;
void ModifyPlrMag ( int p , int l ) ;
void ModifyPlrDex ( int p , int l ) ;
void ModifyPlrVit ( int p , int l ) ;
void SetPlayerHitPoints ( int pnum , int val ) ;
void SetPlrStr ( int p , int v ) ;
void SetPlrMag ( int p , int v ) ;
void SetPlrDex ( int p , int v ) ;
void SetPlrVit ( int p , int v ) ;
void InitDungMsgs ( PlayerStruct & player ) ;
void PlayDungMsgs ( ) ;
/* data */
extern int plrxoff [ 9 ] ;
extern int plryoff [ 9 ] ;
extern int plrxoff2 [ 9 ] ;
extern int plryoff2 [ 9 ] ;
extern int StrengthTbl [ enum_size < HeroClass > : : value ] ;
extern int MagicTbl [ enum_size < HeroClass > : : value ] ;
extern int DexterityTbl [ enum_size < HeroClass > : : value ] ;
extern int VitalityTbl [ enum_size < HeroClass > : : value ] ;
extern int ExpLvlsTbl [ MAXCHARLEVEL ] ;
} // namespace devilution